diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 95bb416d2a..059c39aa56 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -12,87 +12,88 @@ pr: - '*' jobs: - - job: Build - displayName: 'Build' +- job: Build + displayName: 'Build' - strategy: - matrix: - Development: - BuildConfiguration: development - Production: - BuildConfiguration: production - Standalone: - BuildConfiguration: standalone + strategy: + matrix: + Development: + BuildConfiguration: development + Production: + BuildConfiguration: production - pool: - vmImage: 'ubuntu-latest' + pool: + vmImage: 'ubuntu-latest' - steps: - - task: NodeTool@0 - displayName: 'Install Node' - inputs: - versionSpec: '12.x' + steps: + - task: NodeTool@0 + displayName: 'Install Node' + inputs: + versionSpec: '12.x' - - task: Cache@2 - displayName: 'Check Cache' - inputs: - key: 'yarn | yarn.lock' - path: 'node_modules' - cacheHitVar: CACHE_RESTORED + - task: Cache@2 + displayName: 'Check Cache' + inputs: + key: 'yarn | yarn.lock' + path: 'node_modules' + cacheHitVar: CACHE_RESTORED - - script: 'yarn install --frozen-lockfile' - displayName: 'Install Dependencies' - condition: ne(variables.CACHE_RESTORED, 'true') + - script: 'yarn install --frozen-lockfile' + displayName: 'Install Dependencies' + condition: ne(variables.CACHE_RESTORED, 'true') - - script: 'yarn build:development' - displayName: 'Build Development' - condition: eq(variables['BuildConfiguration'], 'development') + - script: 'yarn build:development' + displayName: 'Build Development' + condition: eq(variables['BuildConfiguration'], 'development') - - script: 'yarn build:production' - displayName: 'Build Bundle' - condition: eq(variables['BuildConfiguration'], 'production') + - script: 'yarn build:production' + displayName: 'Build Production' + condition: eq(variables['BuildConfiguration'], 'production') - - script: 'yarn build:standalone' - displayName: 'Build Standalone' - condition: eq(variables['BuildConfiguration'], 'standalone') + - script: 'test -d dist' + displayName: 'Check Build' - - script: 'test -d dist' - displayName: 'Check Build' + - script: 'mv dist jellyfin-web' + displayName: 'Rename Directory' - - script: 'mv dist jellyfin-web' - displayName: 'Rename Directory' + - task: ArchiveFiles@2 + displayName: 'Archive Directory' + inputs: + rootFolderOrFile: 'jellyfin-web' + includeRootFolder: true + archiveFile: 'jellyfin-web-$(BuildConfiguration)' - - task: PublishPipelineArtifact@1 - displayName: 'Publish Release' - inputs: - targetPath: '$(Build.SourcesDirectory)/jellyfin-web' - artifactName: 'jellyfin-web-$(BuildConfiguration)' + - task: PublishPipelineArtifact@1 + displayName: 'Publish Release' + inputs: + targetPath: '$(Build.SourcesDirectory)/jellyfin-web-$(BuildConfiguration).zip' + artifactName: 'jellyfin-web-$(BuildConfiguration)' - - job: Lint - displayName: 'Lint' +- job: Lint + displayName: 'Lint' - pool: - vmImage: 'ubuntu-latest' + pool: + vmImage: 'ubuntu-latest' - steps: - - task: NodeTool@0 - displayName: 'Install Node' - inputs: - versionSpec: '12.x' + steps: + - task: NodeTool@0 + displayName: 'Install Node' + inputs: + versionSpec: '12.x' - - task: Cache@2 - displayName: 'Check Cache' - inputs: - key: 'yarn | yarn.lock' - path: 'node_modules' - cacheHitVar: CACHE_RESTORED + - task: Cache@2 + displayName: 'Check Cache' + inputs: + key: 'yarn | yarn.lock' + path: 'node_modules' + cacheHitVar: CACHE_RESTORED - - script: 'yarn install --frozen-lockfile' - displayName: 'Install Dependencies' - condition: ne(variables.CACHE_RESTORED, 'true') + - script: 'yarn install --frozen-lockfile' + displayName: 'Install Dependencies' + condition: ne(variables.CACHE_RESTORED, 'true') - - script: 'yarn run lint --quiet' - displayName: 'Run ESLint' + - script: 'yarn run lint --quiet' + displayName: 'Run ESLint' - - script: 'yarn run stylelint' - displayName: 'Run Stylelint' + - script: 'yarn run stylelint' + displayName: 'Run Stylelint' diff --git a/.copr/Makefile b/.copr/Makefile new file mode 120000 index 0000000000..ec3c90dfd9 --- /dev/null +++ b/.copr/Makefile @@ -0,0 +1 @@ +../fedora/Makefile \ No newline at end of file diff --git a/.dependabot/config.yml b/.dependabot/config.yml new file mode 100644 index 0000000000..02dfd18aac --- /dev/null +++ b/.dependabot/config.yml @@ -0,0 +1,5 @@ +version: 1 +update_configs: + - package_manager: "javascript" + directory: "/" + update_schedule: "weekly" diff --git a/.editorconfig b/.editorconfig index 81eba8ccc9..92cf9dc590 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,3 +7,6 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true end_of_line = lf + +[json] +indent_size = 2 diff --git a/.eslintignore b/.eslintignore index 52369be1e3..8e3aee83fb 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,5 @@ -libraries/ +node_modules +dist +.idea +.vscode +src/libraries diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..ab53f0f03d --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,196 @@ +module.exports = { + root: true, + plugins: [ + 'promise', + 'import', + 'eslint-comments' + ], + env: { + node: true, + es6: true, + es2017: true, + es2020: true + }, + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + ecmaFeatures: { + impliedStrict: true + } + }, + extends: [ + 'eslint:recommended', + // 'plugin:promise/recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:eslint-comments/recommended', + 'plugin:compat/recommended' + ], + rules: { + 'block-spacing': ['error'], + 'brace-style': ['error'], + 'comma-dangle': ['error', 'never'], + 'comma-spacing': ['error'], + 'eol-last': ['error'], + 'indent': ['error', 4, { 'SwitchCase': 1 }], + 'keyword-spacing': ['error'], + 'max-statements-per-line': ['error'], + 'no-floating-decimal': ['error'], + 'no-multi-spaces': ['error'], + 'no-multiple-empty-lines': ['error', { 'max': 1 }], + 'no-trailing-spaces': ['error'], + 'one-var': ['error', 'never'], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }], + 'semi': ['error'], + 'space-before-blocks': ['error'], + 'space-infix-ops': 'error' + }, + overrides: [ + { + files: [ + './src/**/*.js' + ], + parser: 'babel-eslint', + env: { + node: false, + amd: true, + browser: true, + es6: true, + es2017: true, + es2020: true + }, + globals: { + // Browser globals + 'MediaMetadata': 'readonly', + // Tizen globals + 'tizen': 'readonly', + 'webapis': 'readonly', + // WebOS globals + 'webOS': 'readonly', + // Dependency globals + '$': 'readonly', + 'jQuery': 'readonly', + 'requirejs': 'readonly', + // Jellyfin globals + 'ApiClient': 'writable', + 'AppInfo': 'writable', + 'chrome': 'writable', + 'ConnectionManager': 'writable', + 'DlnaProfilePage': 'writable', + 'Dashboard': 'writable', + 'DashboardPage': 'writable', + 'Emby': 'readonly', + 'Events': 'writable', + 'getParameterByName': 'writable', + 'getWindowLocationSearch': 'writable', + 'Globalize': 'writable', + 'Hls': 'writable', + 'dfnshelper': 'writable', + 'LibraryMenu': 'writable', + 'LinkParser': 'writable', + 'LiveTvHelpers': 'writable', + 'MetadataEditor': 'writable', + 'pageClassOn': 'writable', + 'pageIdOn': 'writable', + 'PlaylistViewer': 'writable', + 'UserParentalControlPage': 'writable', + 'Windows': 'readonly' + }, + rules: { + // TODO: Fix warnings and remove these rules + 'no-redeclare': ['warn'], + 'no-unused-vars': ['warn'], + 'no-useless-escape': ['warn'], + // TODO: Remove after ES6 migration is complete + 'import/no-unresolved': ['off'] + }, + settings: { + polyfills: [ + // Native Promises Only + 'Promise', + // whatwg-fetch + 'fetch', + // document-register-element + 'document.registerElement', + // resize-observer-polyfill + 'ResizeObserver', + // fast-text-encoding + 'TextEncoder', + // intersection-observer + 'IntersectionObserver', + // Core-js + 'Object.assign', + 'Object.is', + 'Object.setPrototypeOf', + 'Object.toString', + 'Object.freeze', + 'Object.seal', + 'Object.preventExtensions', + 'Object.isFrozen', + 'Object.isSealed', + 'Object.isExtensible', + 'Object.getOwnPropertyDescriptor', + 'Object.getPrototypeOf', + 'Object.keys', + 'Object.entries', + 'Object.getOwnPropertyNames', + 'Function.name', + 'Function.hasInstance', + 'Array.from', + 'Array.arrayOf', + 'Array.copyWithin', + 'Array.fill', + 'Array.find', + 'Array.findIndex', + 'Array.iterator', + 'String.fromCodePoint', + 'String.raw', + 'String.iterator', + 'String.codePointAt', + 'String.endsWith', + 'String.includes', + 'String.repeat', + 'String.startsWith', + 'String.trim', + 'String.anchor', + 'String.big', + 'String.blink', + 'String.bold', + 'String.fixed', + 'String.fontcolor', + 'String.fontsize', + 'String.italics', + 'String.link', + 'String.small', + 'String.strike', + 'String.sub', + 'String.sup', + 'RegExp', + 'Number', + 'Math', + 'Date', + 'async', + 'Symbol', + 'Map', + 'Set', + 'WeakMap', + 'WeakSet', + 'ArrayBuffer', + 'DataView', + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'Reflect', + // Temporary while eslint-compat-plugin is buggy + 'document.querySelector' + ] + } + } + ] +} diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index a3348b70d7..0000000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,87 +0,0 @@ -env: - amd: true - browser: true - es6: true - es2017: true - es2020: true - -parserOptions: - ecmaVersion: 2020 - sourceType: module - ecmaFeatures: - impliedStrict: true - -plugins: - - promise - - import - - eslint-comments - -extends: - - eslint:recommended - - plugin:promise/recommended - - plugin:import/errors - - plugin:import/warnings - - plugin:eslint-comments/recommended - -globals: - # Browser globals - MediaMetadata: readonly - # Tizen globals - tizen: readonly - webapis: readonly - # WebOS globals - webOS: readonly - # Dependency globals - $: readonly - jQuery: readonly - requirejs: readonly - # Jellyfin globals - ApiClient: writable - AppInfo: writable - chrome: writable - ConnectionManager: writable - DlnaProfilePage: writable - Dashboard: writable - DashboardPage: writable - Emby: readonly - Events: writable - getParameterByName: writable - getWindowLocationSearch: writable - Globalize: writable - Hls: writable - dfnshelper: writable - LibraryMenu: writable - LinkParser: writable - LiveTvHelpers: writable - MetadataEditor: writable - pageClassOn: writable - pageIdOn: writable - PlaylistViewer: writable - UserParentalControlPage: writable - Windows: readonly - -rules: - block-spacing: ["error"] - brace-style: ["error"] - comma-dangle: ["error", "never"] - comma-spacing: ["error"] - eol-last: ["error"] - indent: ["error", 4, { "SwitchCase": 1 }] - keyword-spacing: ["error"] - max-statements-per-line: ["error"] - no-floating-decimal: ["error"] - no-multi-spaces: ["error"] - no-multiple-empty-lines: ["error", { "max": 1 }] - no-trailing-spaces: ["error"] - one-var: ["error", "never"] - semi: ["error"] - space-before-blocks: ["error"] - # TODO: Fix warnings and remove these rules - no-redeclare: ["warn"] - no-unused-vars: ["warn"] - no-useless-escape: ["warn"] - promise/catch-or-return: ["warn"] - promise/always-return: ["warn"] - promise/no-return-wrap: ["warn"] - # TODO: Remove after ES6 migration is complete - import/no-unresolved: ["warn"] diff --git a/.gitattributes b/.gitattributes index 80f9bc36ed..9e495a4df0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,35 @@ -/CONTRIBUTORS.md merge=union +* text=auto + +CONTRIBUTORS.md merge=union +README.md text +LICENSE text + +*.css text +*.eot binary +*.gif binary +*.html text diff=html +*.ico binary +*.*ignore text +*.jpg binary +*.js text +*.json text +*.lock text -diff +*.map text -diff +*.md text +*.otf binary +*.png binary +*.py text diff=python +*.svg binary +*.ts text +*.ttf binary +*.sass text +*.vue text +*.webp binary +*.woff binary +*.woff2 binary + +.editorconfig text +.gitattributes export-ignore +.gitignore export-ignore + +*.gitattributes linguist-language=gitattributes diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..a35eb9981f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +.ci @dkanada @EraYaN +.github @jellyfin/core +build.sh @joshuaboniface +deployment @joshuaboniface diff --git a/.gitignore b/.gitignore index 2bb5bc64d3..9bccd32fb8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,12 @@ config.json # npm dist +web node_modules # ide .idea .vscode + +#log +yarn-error.log diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index aa3ec707e3..73f40aaca1 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -34,6 +34,8 @@ - [Ryan Hartzell](https://github.com/ryan-hartzell) - [Thibault Nocchi](https://github.com/ThibaultNocchi) - [MrTimscampi](https://github.com/MrTimscampi) + - [Sarab Singh](https://github.com/sarab97) + - [Andrei Oanca](https://github.com/OancaAndrei) # Emby Contributors diff --git a/README.md b/README.md index e2aac6b155..f06e461320 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Jellyfin Web is the frontend used for most of the clients available for end user ### Dependencies -- Yarn +- [Yarn 1.22.4](https://classic.yarnpkg.com/en/docs/install) - Gulp-cli ### Getting Started @@ -78,4 +78,4 @@ Jellyfin Web is the frontend used for most of the clients available for end user ```sh yarn build:standalone - ``` \ No newline at end of file + ``` diff --git a/babel.config.json b/babel.config.json deleted file mode 100644 index 1320b9a327..0000000000 --- a/babel.config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["@babel/preset-env"] -} diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000..9f5fa60229 --- /dev/null +++ b/build.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +# build.sh - Build Jellyfin binary packages +# Part of the Jellyfin Project + +set -o errexit +set -o pipefail + +usage() { + echo -e "build.sh - Build Jellyfin binary packages" + echo -e "Usage:" + echo -e " $0 -t/--type -p/--platform [-k/--keep-artifacts] [-l/--list-platforms]" + echo -e "Notes:" + echo -e " * BUILD_TYPE can be one of: [native, docker] and must be specified" + echo -e " * native: Build using the build script in the host OS" + echo -e " * docker: Build using the build script in a standardized Docker container" + echo -e " * PLATFORM can be any platform shown by -l/--list-platforms and must be specified" + echo -e " * If -k/--keep-artifacts is specified, transient artifacts (e.g. Docker containers) will be" + echo -e " retained after the build is finished; the source directory will still be cleaned" + echo -e " * If -l/--list-platforms is specified, all other arguments are ignored; the script will print" + echo -e " the list of supported platforms and exit" +} + +list_platforms() { + declare -a platforms + platforms=( + $( find deployment -maxdepth 1 -mindepth 1 -name "build.*" | awk -F'.' '{ $1=""; printf $2; if ($3 != ""){ printf "." $3; }; if ($4 != ""){ printf "." $4; }; print ""; }' | sort ) + ) + echo -e "Valid platforms:" + echo + for platform in ${platforms[@]}; do + echo -e "* ${platform} : $( grep '^#=' deployment/build.${platform} | sed 's/^#= //' )" + done +} + +do_build_native() { + export IS_DOCKER=NO + deployment/build.${PLATFORM} +} + +do_build_docker() { + if ! dpkg --print-architecture | grep -q 'amd64'; then + echo "Docker-based builds only support amd64-based cross-building; use a 'native' build instead." + exit 1 + fi + if [[ ! -f deployment/Dockerfile.${PLATFORM} ]]; then + echo "Missing Dockerfile for platform ${PLATFORM}" + exit 1 + fi + if [[ ${KEEP_ARTIFACTS} == YES ]]; then + docker_args="" + else + docker_args="--rm" + fi + + docker build . -t "jellyfin-builder.${PLATFORM}" -f deployment/Dockerfile.${PLATFORM} + mkdir -p ${ARTIFACT_DIR} + docker run $docker_args -v "${SOURCE_DIR}:/jellyfin" -v "${ARTIFACT_DIR}:/dist" "jellyfin-builder.${PLATFORM}" +} + +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + -t|--type) + BUILD_TYPE="$2" + shift + shift + ;; + -p|--platform) + PLATFORM="$2" + shift + shift + ;; + -k|--keep-artifacts) + KEEP_ARTIFACTS=YES + shift + ;; + -l|--list-platforms) + list_platforms + exit 0 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option $1" + usage + exit 1 + ;; + esac +done + +if [[ -z ${BUILD_TYPE} || -z ${PLATFORM} ]]; then + usage + exit 1 +fi + +export SOURCE_DIR="$( pwd )" +export ARTIFACT_DIR="${SOURCE_DIR}/../bin/${PLATFORM}" + +# Determine build type +case ${BUILD_TYPE} in + native) + do_build_native + ;; + docker) + do_build_docker + ;; +esac diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000000..fe1633faec --- /dev/null +++ b/build.yaml @@ -0,0 +1,9 @@ +--- +# We just wrap `build` so this is really it +name: "jellyfin-web" +version: "10.6.0" +packages: + - debian.all + - fedora.all + - centos.all + - portable diff --git a/bump_version b/bump_version new file mode 100755 index 0000000000..bc8288b829 --- /dev/null +++ b/bump_version @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +# bump_version - increase the shared version and generate changelogs + +set -o errexit +set -o pipefail + +usage() { + echo -e "bump_version - increase the shared version and generate changelogs" + echo -e "" + echo -e "Usage:" + echo -e " $ bump_version " +} + +if [[ -z $1 ]]; then + usage + exit 1 +fi + +shared_version_file="src/components/apphost.js" +build_file="./build.yaml" + +new_version="$1" + +# Parse the version from shared version file +old_version="$( + grep "appVersion" ${shared_version_file} | head -1 \ + | sed -E 's/var appVersion = "([0-9\.]+)";/\1/' +)" +echo "Old version in appHost is: $old_version" + +# Set the shared version to the specified new_version +old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars +new_version_sed="$( cut -f1 -d'-' <<<"${new_version}" )" +sed -i "s/${old_version_sed}/${new_version_sed}/g" ${shared_version_file} + +old_version="$( + grep "version:" ${build_file} \ + | sed -E 's/version: "([0-9\.]+[-a-z0-9]*)"/\1/' +)" +echo "Old version in ${build_file}: $old_version`" + +# Set the build.yaml version to the specified new_version +old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars +sed -i "s/${old_version_sed}/${new_version}/g" ${build_file} + +if [[ ${new_version} == *"-"* ]]; then + new_version_deb="$( sed 's/-/~/g' <<<"${new_version}" )" +else + new_version_deb="${new_version}-1" +fi + +# Write out a temporary Debian changelog with our new stuff appended and some templated formatting +debian_changelog_file="debian/changelog" +debian_changelog_temp="$( mktemp )" +# Create new temp file with our changelog +echo -e "jellyfin (${new_version_deb}) unstable; urgency=medium + + * New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v${new_version} + + -- Jellyfin Packaging Team $( date --rfc-2822 ) +" >> ${debian_changelog_temp} +cat ${debian_changelog_file} >> ${debian_changelog_temp} +# Move into place +mv ${debian_changelog_temp} ${debian_changelog_file} + +# Write out a temporary Yum changelog with our new stuff prepended and some templated formatting +fedora_spec_file="fedora/jellyfin.spec" +fedora_changelog_temp="$( mktemp )" +fedora_spec_temp_dir="$( mktemp -d )" +fedora_spec_temp="${fedora_spec_temp_dir}/jellyfin.spec.tmp" +# Make a copy of our spec file for hacking +cp ${fedora_spec_file} ${fedora_spec_temp_dir}/ +pushd ${fedora_spec_temp_dir} +# Split out the stuff before and after changelog +csplit jellyfin.spec "/^%changelog/" # produces xx00 xx01 +# Update the version in xx00 +sed -i "s/${old_version_sed}/${new_version_sed}/g" xx00 +# Remove the header from xx01 +sed -i '/^%changelog/d' xx01 +# Create new temp file with our changelog +echo -e "%changelog +* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team +- New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v${new_version}" >> ${fedora_changelog_temp} +cat xx01 >> ${fedora_changelog_temp} +# Reassembble +cat xx00 ${fedora_changelog_temp} > ${fedora_spec_temp} +popd +# Move into place +mv ${fedora_spec_temp} ${fedora_spec_file} +# Clean up +rm -rf ${fedora_changelog_temp} ${fedora_spec_temp_dir} + +# Stage the changed files for commit +git add ${shared_version_file} ${build_file} ${debian_changelog_file} ${fedora_spec_file} Dockerfile* +git status diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000000..50966c3a01 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +jellyfin-web (10.6.0-1) unstable; urgency=medium + + * New upstream version 10.6.0; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.0 + + -- Jellyfin Packaging Team Mon, 16 Mar 2020 11:15:00 -0400 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000000..45a4fb75db --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000000..ce7b130efc --- /dev/null +++ b/debian/control @@ -0,0 +1,16 @@ +Source: jellyfin-web +Section: misc +Priority: optional +Maintainer: Jellyfin Team +Build-Depends: debhelper (>= 9), + npm | nodejs +Standards-Version: 3.9.4 +Homepage: https://jellyfin.org/ +Vcs-Git: https://github.org/jellyfin/jellyfin-web.git +Vcs-Browser: https://github.org/jellyfin/jellyfin-web + +Package: jellyfin-web +Recommends: jellyfin-server +Architecture: all +Description: Jellyfin is the Free Software Media System. + This package provides the Jellyfin web client. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000000..85548075eb --- /dev/null +++ b/debian/copyright @@ -0,0 +1,28 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: jellyfin-web +Source: https://github.com/jellyfin/jellyfin-web + +Files: * +Copyright: 2018-2020 Jellyfin Team +License: GPL-3.0 + +Files: debian/* +Copyright: 2020 Joshua Boniface +License: GPL-3.0 + +License: GPL-3.0 + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000000..60b3d28723 --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,6 @@ +[DEFAULT] +pristine-tar = False +cleaner = fakeroot debian/rules clean + +[import-orig] +filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ] diff --git a/debian/install b/debian/install new file mode 100644 index 0000000000..584fe06a11 --- /dev/null +++ b/debian/install @@ -0,0 +1 @@ +web usr/share/jellyfin/ diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in new file mode 100644 index 0000000000..cef83a3407 --- /dev/null +++ b/debian/po/POTFILES.in @@ -0,0 +1 @@ +[type: gettext/rfc822deb] templates diff --git a/debian/po/templates.pot b/debian/po/templates.pot new file mode 100644 index 0000000000..2cdcae4173 --- /dev/null +++ b/debian/po/templates.pot @@ -0,0 +1,57 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: jellyfin-server\n" +"Report-Msgid-Bugs-To: jellyfin-server@packages.debian.org\n" +"POT-Creation-Date: 2015-06-12 20:51-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: note +#. Description +#: ../templates:1001 +msgid "Jellyfin permission info:" +msgstr "" + +#. Type: note +#. Description +#: ../templates:1001 +msgid "" +"Jellyfin by default runs under a user named \"jellyfin\". Please ensure that the " +"user jellyfin has read and write access to any folders you wish to add to your " +"library. Otherwise please run jellyfin under a different user." +msgstr "" + +#. Type: string +#. Description +#: ../templates:2001 +msgid "Username to run Jellyfin as:" +msgstr "" + +#. Type: string +#. Description +#: ../templates:2001 +msgid "The user that jellyfin will run as." +msgstr "" + +#. Type: note +#. Description +#: ../templates:3001 +msgid "Jellyfin still running" +msgstr "" + +#. Type: note +#. Description +#: ../templates:3001 +msgid "Jellyfin is currently running. Please close it and try again." +msgstr "" diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000000..f3e568225c --- /dev/null +++ b/debian/rules @@ -0,0 +1,20 @@ +#! /usr/bin/make -f +export DH_VERBOSE=1 + +%: + dh $@ + +# disable "make check" +override_dh_auto_test: + +# disable stripping debugging symbols +override_dh_clistrip: + +override_dh_auto_build: + npx yarn install + mv $(CURDIR)/dist $(CURDIR)/web + +override_dh_auto_clean: + test -d $(CURDIR)/dist && rm -rf '$(CURDIR)/dist' || true + test -d $(CURDIR)/web && rm -rf '$(CURDIR)/web' || true + test -d $(CURDIR)/node_modules && rm -rf '$(CURDIR)/node_modules' || true diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000000..d3827e75a5 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +1.0 diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 0000000000..b7adf56c67 --- /dev/null +++ b/debian/source/options @@ -0,0 +1,7 @@ +tar-ignore='.git*' +tar-ignore='**/.git' +tar-ignore='**/.hg' +tar-ignore='**/.vs' +tar-ignore='**/.vscode' +tar-ignore='deployment' +tar-ignore='*.deb' diff --git a/deployment/Dockerfile.centos.all b/deployment/Dockerfile.centos.all new file mode 100644 index 0000000000..93bf8d6988 --- /dev/null +++ b/deployment/Dockerfile.centos.all @@ -0,0 +1,27 @@ +FROM centos:7 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV IS_DOCKER=YES + +# Prepare CentOS environment +RUN yum update -y \ + && yum install -y epel-release \ + && yum install -y @buildsys-build rpmdevtools git yum-plugins-core nodejs-yarn autoconf automake glibc-devel + +# Install recent NodeJS and Yarn +RUN curl -fSsLo /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \ + && rpm -i https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm \ + && yum install -y yarn + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.centos.all /build.sh + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.debian.all b/deployment/Dockerfile.debian.all new file mode 100644 index 0000000000..54281a5eb4 --- /dev/null +++ b/deployment/Dockerfile.debian.all @@ -0,0 +1,25 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y debhelper mmv npm git + +# Prepare Yarn +RUN npm install -g yarn + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.all /build.sh + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.fedora.all b/deployment/Dockerfile.fedora.all new file mode 100644 index 0000000000..d47f4ff4da --- /dev/null +++ b/deployment/Dockerfile.fedora.all @@ -0,0 +1,21 @@ +FROM fedora:31 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV IS_DOCKER=YES + +# Prepare Fedora environment +RUN dnf update -y \ + && dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core nodejs-yarn autoconf automake glibc-devel + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora.all /build.sh + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable new file mode 100644 index 0000000000..e0d1f45265 --- /dev/null +++ b/deployment/Dockerfile.portable @@ -0,0 +1,25 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y mmv npm git + +# Prepare Yarn +RUN npm install -g yarn + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.portable /build.sh + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/build.centos.all b/deployment/build.centos.all new file mode 100755 index 0000000000..8c2cec6d33 --- /dev/null +++ b/deployment/build.centos.all @@ -0,0 +1,27 @@ +#!/bin/bash + +#= CentOS 7 all .rpm + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +cp -a yarn.lock /tmp/yarn.lock + +# Build RPM +make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS +rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm + +# Move the artifacts out +mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +rm -f fedora/jellyfin*.tar.gz +cp -a /tmp/yarn.lock yarn.lock + +popd diff --git a/deployment/build.debian.all b/deployment/build.debian.all new file mode 100755 index 0000000000..8d617a288e --- /dev/null +++ b/deployment/build.debian.all @@ -0,0 +1,25 @@ +#!/bin/bash + +#= Debian/Ubuntu all .deb + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +cp -a yarn.lock /tmp/yarn.lock + +# Build DEB +dpkg-buildpackage -us -uc --pre-clean --post-clean + +mkdir -p ${ARTIFACT_DIR}/ +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ + +cp -a /tmp/yarn.lock yarn.lock + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd diff --git a/deployment/build.fedora.all b/deployment/build.fedora.all new file mode 100755 index 0000000000..4ba12f35ea --- /dev/null +++ b/deployment/build.fedora.all @@ -0,0 +1,27 @@ +#!/bin/bash + +#= Fedora 29+ all .rpm + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +cp -a yarn.lock /tmp/yarn.lock + +# Build RPM +make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS +rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm + +# Move the artifacts out +mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +rm -f fedora/jellyfin*.tar.gz +cp -a /tmp/yarn.lock yarn.lock + +popd diff --git a/deployment/build.portable b/deployment/build.portable new file mode 100755 index 0000000000..c4cbe927e4 --- /dev/null +++ b/deployment/build.portable @@ -0,0 +1,28 @@ +#!/bin/bash + +#= Portable .NET DLL .tar.gz + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +# Build archives +npx yarn install +mv dist/ jellyfin-web_${version} +tar -czf jellyfin-web_${version}_portable.tar.gz jellyfin-web_${version} +rm -rf dist/ + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd diff --git a/fedora/Makefile b/fedora/Makefile new file mode 100644 index 0000000000..1d3e39ac8b --- /dev/null +++ b/fedora/Makefile @@ -0,0 +1,21 @@ +VERSION := $(shell sed -ne '/^Version:/s/.* *//p' fedora/jellyfin-web.spec) + +srpm: + cd fedora/; \ + SOURCE_DIR=.. \ + WORKDIR="$${PWD}"; \ + tar \ + --transform "s,^\.,jellyfin-web-$(VERSION)," \ + --exclude='.git*' \ + --exclude='**/.git' \ + --exclude='**/.hg' \ + --exclude='deployment' \ + --exclude='*.deb' \ + --exclude='*.rpm' \ + --exclude='jellyfin-web-$(VERSION).tar.gz' \ + -czf "jellyfin-web-$(VERSION).tar.gz" \ + -C $${SOURCE_DIR} ./ + cd fedora/; \ + rpmbuild -bs jellyfin-web.spec \ + --define "_sourcedir $$PWD/" \ + --define "_srcrpmdir $(outdir)" diff --git a/fedora/jellyfin-web.spec b/fedora/jellyfin-web.spec new file mode 100644 index 0000000000..dbc0bd0efa --- /dev/null +++ b/fedora/jellyfin-web.spec @@ -0,0 +1,43 @@ +%global debug_package %{nil} + +Name: jellyfin-web +Version: 10.6.0 +Release: 1%{?dist} +Summary: The Free Software Media System web client +License: GPLv3 +URL: https://jellyfin.org +# Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` +Source0: jellyfin-web-%{version}.tar.gz + +%if 0%{?centos} +BuildRequires: yarn +%else +BuildRequires nodejs-yarn +%endif +BuildArch: noarch + +# Disable Automatic Dependency Processing +AutoReqProv: no + +%description +Jellyfin is a free software media system that puts you in control of managing and streaming your media. + + +%prep +%autosetup -n jellyfin-web-%{version} -b 0 + +%build + +%install +yarn install +%{__mkdir} -p %{buildroot}%{_datadir} +mv dist %{buildroot}%{_datadir}/jellyfin-web +%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/jellyfin/LICENSE + +%files +%attr(755,root,root) %{_datadir}/jellyfin-web +%{_datadir}/licenses/jellyfin/LICENSE + +%changelog +* Mon Mar 23 2020 Jellyfin Packaging Team +- Forthcoming stable release diff --git a/gulpfile.js b/gulpfile.js index 0eb5593541..03826e8b6e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,5 +1,3 @@ -'use strict'; - const { src, dest, series, parallel, watch } = require('gulp'); const browserSync = require('browser-sync').create(); const del = require('del'); @@ -10,15 +8,14 @@ const htmlmin = require('gulp-htmlmin'); const imagemin = require('gulp-imagemin'); const sourcemaps = require('gulp-sourcemaps'); const mode = require('gulp-mode')({ - modes: ["development", "production"], - default: "development", + modes: ['development', 'production'], + default: 'development', verbose: false }); const stream = require('webpack-stream'); const inject = require('gulp-inject'); const postcss = require('gulp-postcss'); const sass = require('gulp-sass'); -const gulpif = require('gulp-if'); const lazypipe = require('lazypipe'); sass.compiler = require('node-sass'); @@ -47,7 +44,7 @@ const options = { query: ['src/**/*.png', 'src/**/*.jpg', 'src/**/*.gif', 'src/**/*.svg'] }, copy: { - query: ['src/**/*.json', 'src/**/*.ico'] + query: ['src/**/*.json', 'src/**/*.ico', 'src/**/*.mp3'] }, injectBundle: { query: 'src/index.html' @@ -57,12 +54,12 @@ const options = { function serve() { browserSync.init({ server: { - baseDir: "./dist" + baseDir: './dist' }, port: 8080 }); - let events = ['add', 'change']; + const events = ['add', 'change']; watch(options.javascript.query).on('all', function (event, path) { if (events.includes(event)) { @@ -70,7 +67,7 @@ function serve() { } }); - watch(options.apploader.query, apploader(true)); + watch(options.apploader.query, apploader()); watch('src/bundle.js', webpack); @@ -105,7 +102,7 @@ function clean() { return del(['dist/']); } -let pipelineJavascript = lazypipe() +const pipelineJavascript = lazypipe() .pipe(function () { return mode.development(sourcemaps.init({ loadMaps: true })); }) @@ -133,18 +130,12 @@ function javascript(query) { .pipe(browserSync.stream()); } -function apploader(standalone) { - function task() { - return src(options.apploader.query, { base: './src/' }) - .pipe(gulpif(standalone, concat('scripts/apploader.js'))) - .pipe(pipelineJavascript()) - .pipe(dest('dist/')) - .pipe(browserSync.stream()); - }; - - task.displayName = 'apploader'; - - return task; +function apploader() { + return src(options.apploader.query, { base: './src/' }) + .pipe(concat('scripts/apploader.js')) + .pipe(pipelineJavascript()) + .pipe(dest('dist/')) + .pipe(browserSync.stream()); } function webpack() { @@ -192,10 +183,5 @@ function injectBundle() { .pipe(browserSync.stream()); } -function build(standalone) { - return series(clean, parallel(javascript, apploader(standalone), webpack, css, html, images, copy), injectBundle); -} - -exports.default = build(false); -exports.standalone = build(true); -exports.serve = series(exports.standalone, serve); +exports.default = series(clean, parallel(javascript, apploader, webpack, css, html, images, copy), injectBundle); +exports.serve = series(exports.default, serve); diff --git a/package.json b/package.json index fe84f7693d..de9fa24e8d 100644 --- a/package.json +++ b/package.json @@ -5,26 +5,29 @@ "repository": "https://github.com/jellyfin/jellyfin-web", "license": "GPL-2.0-or-later", "devDependencies": { - "@babel/core": "^7.8.6", - "@babel/plugin-transform-modules-amd": "^7.8.3", + "@babel/core": "^7.10.2", + "@babel/plugin-proposal-class-properties": "^7.10.1", + "@babel/plugin-proposal-private-methods": "^7.10.1", + "@babel/plugin-transform-modules-amd": "^7.9.6", "@babel/polyfill": "^7.8.7", - "@babel/preset-env": "^7.8.6", - "autoprefixer": "^9.7.4", + "@babel/preset-env": "^7.10.2", + "autoprefixer": "^9.8.0", + "babel-eslint": "^11.0.0-beta.2", "babel-loader": "^8.0.6", "browser-sync": "^2.26.7", - "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^5.1.1", "css-loader": "^3.4.2", "cssnano": "^4.1.10", "del": "^5.1.0", "eslint": "^6.8.0", - "eslint-plugin-eslint-comments": "^3.1.2", + "eslint-plugin-compat": "^3.5.1", + "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.20.2", "eslint-plugin-promise": "^4.2.1", "file-loader": "^6.0.0", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", - "gulp-cli": "^2.2.0", + "gulp-cli": "^2.2.1", "gulp-concat": "^2.6.1", "gulp-htmlmin": "^5.0.1", "gulp-if": "^3.0.0", @@ -35,44 +38,48 @@ "gulp-sass": "^4.0.2", "gulp-sourcemaps": "^2.6.5", "gulp-terser": "^1.2.0", - "html-webpack-plugin": "^4.0.2", + "html-webpack-plugin": "^4.3.0", "lazypipe": "^1.0.2", "node-sass": "^4.13.1", "postcss-loader": "^3.0.0", "postcss-preset-env": "^6.7.0", "style-loader": "^1.1.3", - "stylelint": "^13.1.0", + "stylelint": "^13.5.0", "stylelint-config-rational-order": "^0.1.2", "stylelint-no-browser-hacks": "^1.2.1", "stylelint-order": "^4.0.0", "webpack": "^4.41.5", - "webpack-cli": "^3.3.10", - "webpack-concat-plugin": "^3.0.0", - "webpack-dev-server": "^3.10.3", "webpack-merge": "^4.2.2", "webpack-stream": "^5.2.1" }, "dependencies": { "alameda": "^1.4.0", + "blurhash": "^1.1.3", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", - "core-js": "^3.6.4", - "date-fns": "^2.11.1", + "core-js": "^3.6.5", + "date-fns": "^2.14.0", "document-register-element": "^1.14.3", + "epubjs": "^0.3.85", + "fast-text-encoding": "^1.0.1", "flv.js": "^1.5.0", + "headroom.js": "^0.11.0", "hls.js": "^0.13.1", - "howler": "^2.1.3", + "howler": "^2.2.0", + "intersection-observer": "^0.10.0", + "jellyfin-apiclient": "^1.2.0", "jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto", - "jquery": "^3.4.1", + "jquery": "^3.5.1", "jstree": "^3.3.7", - "libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf", + "libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv", "material-design-icons-iconfont": "^5.0.1", "native-promise-only": "^0.8.0-a", - "page": "^1.11.5", + "page": "^1.11.6", "query-string": "^6.11.1", "resize-observer-polyfill": "^1.5.1", - "shaka-player": "^2.5.10", + "screenfull": "^5.0.2", + "shaka-player": "^2.5.12", "sortablejs": "^1.10.2", - "swiper": "^5.3.1", + "swiper": "^5.4.1", "webcomponents.js": "^0.7.24", "whatwg-fetch": "^3.0.0" }, @@ -83,22 +90,53 @@ "overrides": [ { "test": [ + "src/components/accessSchedule/accessSchedule.js", + "src/components/actionSheet/actionSheet.js", "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", - "src/components/scrollManager.js", + "src/components/cardbuilder/chaptercardbuilder.js", + "src/components/cardbuilder/peoplecardbuilder.js", + "src/components/images/imageLoader.js", + "src/components/indicators/indicators.js", + "src/components/lazyLoader/lazyLoaderIntersectionObserver.js", + "src/components/playback/brightnessosd.js", + "src/components/playback/mediasession.js", + "src/components/playback/nowplayinghelper.js", + "src/components/playback/playbackorientation.js", + "src/components/playback/playerSelectionMenu.js", + "src/components/playback/playersettingsmenu.js", + "src/components/playback/playmethodhelper.js", + "src/components/playback/remotecontrolautoplay.js", + "src/components/playback/volumeosd.js", + "src/components/playmenu.js", "src/components/sanatizefilename.js", - "src/scripts/settings/webSettings.js", + "src/components/scrollManager.js", + "src/components/syncplay/groupSelectionMenu.js", + "src/components/syncplay/playbackPermissionManager.js", + "src/components/syncplay/syncPlayManager.js", + "src/components/syncplay/timeSyncManager.js", + "src/plugins/bookPlayer/plugin.js", + "src/plugins/bookPlayer/tableOfContent.js", + "src/plugins/photoPlayer/plugin.js", + "src/scripts/deleteHelper.js", + "src/scripts/dfnshelper.js", + "src/scripts/dom.js", + "src/scripts/fileDownloader.js", + "src/scripts/filesystem.js", + "src/scripts/imagehelper.js", + "src/scripts/inputManager.js", + "src/plugins/backdropScreensaver/plugin.js", + "src/components/filterdialog/filterdialog.js", + "src/components/fetchhelper.js", + "src/scripts/keyboardNavigation.js", "src/scripts/settings/appSettings.js", "src/scripts/settings/userSettings.js", - "src/scripts/imagehelper.js", - "src/scripts/dfnshelper.js" + "src/scripts/settings/webSettings.js" ], "plugins": [ - "@babel/plugin-transform-modules-amd" + "@babel/plugin-transform-modules-amd", + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-private-methods" ] } ] @@ -119,12 +157,11 @@ "Firefox ESR" ], "scripts": { - "serve": "gulp serve", + "serve": "gulp serve --development", "prepare": "gulp --production", "build:development": "gulp --development", "build:production": "gulp --production", - "build:standalone": "gulp standalone --development", - "lint": "eslint \"src\"", + "lint": "eslint \".\"", "stylelint": "stylelint \"src/**/*.css\"" } } diff --git a/postcss.config.js b/postcss.config.js index 23159fd295..bd1651fa19 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,11 +1,16 @@ +const packageConfig = require('./package.json'); const postcssPresetEnv = require('postcss-preset-env'); +const autoprefixer = require('autoprefixer'); const cssnano = require('cssnano'); const config = () => ({ - plugins: [ - postcssPresetEnv(), - cssnano() - ] + plugins: [ + // Explicitly specify browserslist to override ones from node_modules + // For example, Swiper has it in its package.json + postcssPresetEnv({browsers: packageConfig.browserslist}), + autoprefixer({overrideBrowserslist: packageConfig.browserslist}), + cssnano() + ] }); -module.exports = config +module.exports = config; diff --git a/scripts/scdup.py b/scripts/scdup.py index 468e31f14a..9b9ddf6466 100644 --- a/scripts/scdup.py +++ b/scripts/scdup.py @@ -15,6 +15,8 @@ print(langlst) input('press enter to continue') keysus = [] +missing = [] + with open(langdir + '/' + 'en-us.json') as en: langus = json.load(en) for key in langus: @@ -32,10 +34,19 @@ for lang in langlst: for key in langjson: if key in keysus: langjnew[key] = langjson[key] + elif key not in missing: + missing.append(key) f.seek(0) f.write(json.dumps(langjnew, indent=inde, sort_keys=False, ensure_ascii=False)) f.write('\n') f.truncate() f.close() +print(missing) +print('LENGTH: ' + str(len(missing))) +with open('missing.txt', 'w') as out: + for item in missing: + out.write(item + '\n') + out.close() + print('DONE') diff --git a/scripts/scgen.py b/scripts/scgen.py index 0d831426e6..12af27320a 100644 --- a/scripts/scgen.py +++ b/scripts/scgen.py @@ -34,7 +34,7 @@ for lang in langlst: print(dep) print('LENGTH: ' + str(len(dep))) -with open('scout.txt', 'w') as out: +with open('unused.txt', 'w') as out: for item in dep: out.write(item + '\n') out.close() diff --git a/src/addplugin.html b/src/addplugin.html index 83640033bb..81c671d5bb 100644 --- a/src/addplugin.html +++ b/src/addplugin.html @@ -5,12 +5,11 @@
-

-

-

+

+

@@ -28,7 +27,6 @@
${ServerRestartNeededAfterPluginInstall}
-

@@ -37,9 +35,6 @@ diff --git a/src/addserver.html b/src/addserver.html index 763f56851b..02850fffb8 100644 --- a/src/addserver.html +++ b/src/addserver.html @@ -3,7 +3,7 @@

${HeaderConnectToServer}

- +
${LabelServerHostHelp}

diff --git a/src/apikeys.html b/src/apikeys.html index 6f766ae6c9..3cb7cd6de1 100644 --- a/src/apikeys.html +++ b/src/apikeys.html @@ -4,18 +4,19 @@

${HeaderApiKeys}

${HeaderApiKeysHelp}


+ - - - - + + + + diff --git a/src/assets/audio/silence.mp3 b/src/assets/audio/silence.mp3 new file mode 100644 index 0000000000..29dbef2185 Binary files /dev/null and b/src/assets/audio/silence.mp3 differ diff --git a/src/assets/css/librarybrowser.css b/src/assets/css/librarybrowser.css index f0865815f2..494a6c523d 100644 --- a/src/assets/css/librarybrowser.css +++ b/src/assets/css/librarybrowser.css @@ -21,7 +21,11 @@ } .libraryPage { - padding-top: 7em; + padding-top: 7em !important; +} + +.layout-mobile .libraryPage { + padding-top: 4em !important; } .itemDetailPage { @@ -128,10 +132,6 @@ margin-top: 0; } -.layout-mobile .pageTitleWithDefaultLogo { - background-image: url(../img/icon-transparent.png); -} - .headerLeft, .skinHeader { display: -webkit-box; @@ -242,10 +242,11 @@ } .mainDrawer-scrollContainer { - margin-bottom: 10vh; + padding-bottom: 10vh; } @media all and (min-width: 40em) { + .dashboardDocument .adminDrawerLogo, .dashboardDocument .mainDrawerButton { display: none !important; } @@ -313,7 +314,7 @@ } .dashboardDocument .mainDrawer-scrollContainer { - margin-top: 4.6em !important; + margin-top: 4.65em !important; } } @@ -516,6 +517,13 @@ .itemName { margin: 0.5em 0; + font-weight: 600; +} + +.nameContainer { + display: flex; + flex-direction: column; + flex-wrap: wrap; } .itemMiscInfo { @@ -533,7 +541,6 @@ .layout-mobile .itemName, .layout-mobile .itemMiscInfo, .layout-mobile .mainDetailButtons { - display: flex; align-items: center; justify-content: center; text-align: center; @@ -575,7 +582,6 @@ .infoText { white-space: nowrap; - overflow: hidden; text-overflow: ellipsis; text-align: left; } @@ -606,12 +612,11 @@ } .detailLogo { - width: 67.25vw; - height: 14.5vh; + width: 30vw; + height: 25vh; position: absolute; - top: 15vh; - right: 0; - -webkit-background-size: contain; + top: 10vh; + right: 20vw; background-size: contain; } @@ -619,26 +624,8 @@ display: none; } -@media all and (max-width: 87.5em) { - .detailLogo { - right: 5%; - } -} - -@media all and (max-width: 75em) { - .detailLogo { - right: 2%; - } -} - @media all and (max-width: 68.75em) { .detailLogo { - width: 14.91em; - height: 3.5em; - right: 5%; - bottom: 5%; - top: auto; - background-position: center right; display: none; } } @@ -657,12 +644,7 @@ div.itemDetailGalleryLink.defaultCardBackground { height: 23vw; /* Dirty hack to get it to look somewhat square. Less than ideal. */ } -.btnSyncComplete i { - -webkit-border-radius: 100em; - border-radius: 100em; -} - -.itemDetailGalleryLink.defaultCardBackground > i { +.itemDetailGalleryLink.defaultCardBackground > .material-icons { font-size: 15vw; margin-top: 50%; transform: translateY(-50%); @@ -694,10 +676,6 @@ div.itemDetailGalleryLink.defaultCardBackground { } } -.btnSyncComplete { - background: #673ab7 !important; -} - .emby-button.detailFloatingButton { position: absolute; background-color: rgba(0, 0, 0, 0.5) !important; @@ -709,7 +687,7 @@ div.itemDetailGalleryLink.defaultCardBackground { color: rgba(255, 255, 255, 0.76); } -.emby-button.detailFloatingButton i { +.emby-button.detailFloatingButton .material-icons { font-size: 3.5em; } @@ -1166,3 +1144,21 @@ div:not(.sectionTitleContainer-cards) > .sectionTitle-cards { margin-top: 0; font-size: 1.4em; } + +.overview-controls { + display: flex; + justify-content: flex-end; +} + +.detail-clamp-text { + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 12; + -webkit-box-orient: vertical; +} + +@media all and (min-width: 40em) { + .detail-clamp-text { + -webkit-line-clamp: 6; + } +} diff --git a/src/assets/css/site.css b/src/assets/css/site.css index 67416663e7..d489f77f01 100644 --- a/src/assets/css/site.css +++ b/src/assets/css/site.css @@ -5,6 +5,17 @@ html { height: 100%; } +.clipForScreenReader { + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(50%); + height: 1px; + width: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; +} + .material-icons { /* Fix font ligatures on older WebOS versions */ -webkit-font-feature-settings: "liga"; @@ -96,3 +107,24 @@ div[data-role=page] { margin-right: auto; width: 85%; } + +.headroom { + will-change: transform; + transition: transform 200ms linear; +} + +.headroom--pinned { + transform: translateY(0%); +} + +.headroom--unpinned { + transform: translateY(-100%); +} + +.force-scroll { + overflow-y: scroll; +} + +.hide-scroll { + overflow-y: hidden; +} diff --git a/src/assets/css/videoosd.css b/src/assets/css/videoosd.css index f4f198325b..50cb41021b 100644 --- a/src/assets/css/videoosd.css +++ b/src/assets/css/videoosd.css @@ -30,7 +30,7 @@ opacity: 0; } -.osdHeader .headerButton:not(.headerBackButton):not(.headerCastButton) { +.osdHeader .headerButton:not(.headerBackButton):not(.headerCastButton):not(.headerSyncButton) { display: none; } diff --git a/src/bundle.js b/src/bundle.js index 11379c9d87..41648f7c4f 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -5,127 +5,185 @@ var _define = window.define; // document-register-element -var docRegister = require("document-register-element"); -_define("document-register-element", function() { +var docRegister = require('document-register-element'); +_define('document-register-element', function() { return docRegister; }); // fetch -var fetch = require("whatwg-fetch"); -_define("fetch", function() { +var fetch = require('whatwg-fetch'); +_define('fetch', function() { return fetch; }); +// Blurhash +var blurhash = require('blurhash'); +_define('blurhash', function() { + return blurhash; +}); + // query-string -var query = require("query-string"); -_define("queryString", function() { +var query = require('query-string'); +_define('queryString', function() { return query; }); // flvjs -var flvjs = require("flv.js/dist/flv").default; -_define("flvjs", function() { +var flvjs = require('flv.js/dist/flv').default; +_define('flvjs', function() { return flvjs; }); // jstree -var jstree = require("jstree"); -require("jstree/dist/themes/default/style.css"); -_define("jstree", function() { +var jstree = require('jstree'); +require('jstree/dist/themes/default/style.css'); +_define('jstree', function() { return jstree; }); // jquery -var jquery = require("jquery"); -_define("jQuery", function() { +var jquery = require('jquery'); +_define('jQuery', function() { return jquery; }); // hlsjs -var hlsjs = require("hls.js"); -_define("hlsjs", function() { +var hlsjs = require('hls.js'); +_define('hlsjs', function() { return hlsjs; }); // howler -var howler = require("howler"); -_define("howler", function() { +var howler = require('howler'); +_define('howler', function() { return howler; }); // resize-observer-polyfill -var resize = require("resize-observer-polyfill").default; -_define("resize-observer-polyfill", function() { +var resize = require('resize-observer-polyfill').default; +_define('resize-observer-polyfill', function() { return resize; }); // shaka -var shaka = require("shaka-player"); -_define("shaka", function() { +var shaka = require('shaka-player'); +_define('shaka', function() { return shaka; }); // swiper -var swiper = require("swiper/js/swiper"); -require("swiper/css/swiper.min.css"); -_define("swiper", function() { +var swiper = require('swiper/js/swiper'); +require('swiper/css/swiper.min.css'); +_define('swiper', function() { return swiper; }); // sortable -var sortable = require("sortablejs").default; -_define("sortable", function() { +var sortable = require('sortablejs').default; +_define('sortable', function() { return sortable; }); // webcomponents -var webcomponents = require("webcomponents.js/webcomponents-lite"); -_define("webcomponents", function() { +var webcomponents = require('webcomponents.js/webcomponents-lite'); +_define('webcomponents', function() { return webcomponents; }); // libass-wasm -var libass_wasm = require("libass-wasm"); -_define("JavascriptSubtitlesOctopus", function() { - return libass_wasm; +var libassWasm = require('libass-wasm'); +_define('JavascriptSubtitlesOctopus', function() { + return libassWasm; }); // material-icons -var material_icons = require("material-design-icons-iconfont/dist/material-design-icons.css"); -_define("material-icons", function() { - return material_icons; +var materialIcons = require('material-design-icons-iconfont/dist/material-design-icons.css'); +_define('material-icons', function() { + return materialIcons; }); // noto font -var noto = require("jellyfin-noto"); -_define("jellyfin-noto", function () { +var noto = require('jellyfin-noto'); +_define('jellyfin-noto', function () { return noto; }); +var epubjs = require('epubjs'); +_define('epubjs', function () { + return epubjs; +}); + // page.js -var page = require("page"); -_define("page", function() { +var page = require('page'); +_define('page', function() { return page; }); -var polyfill = require("@babel/polyfill/dist/polyfill"); -_define("polyfill", function () { +// core-js +var polyfill = require('@babel/polyfill/dist/polyfill'); +_define('polyfill', function () { return polyfill; }); // domtokenlist-shim -var classlist = require("classlist.js"); -_define("classlist-polyfill", function () { +var classlist = require('classlist.js'); +_define('classlist-polyfill', function () { return classlist; }); // Date-FNS -var date_fns = require("date-fns"); -_define("date-fns", function () { - return date_fns; +var dateFns = require('date-fns'); +_define('date-fns', function () { + return dateFns; }); -var date_fns_locale = require("date-fns/locale"); -_define("date-fns/locale", function () { - return date_fns_locale; +var dateFnsLocale = require('date-fns/locale'); +_define('date-fns/locale', function () { + return dateFnsLocale; +}); + +var fast_text_encoding = require('fast-text-encoding'); +_define('fast-text-encoding', function () { + return fast_text_encoding; +}); + +// intersection-observer +var intersection_observer = require('intersection-observer'); +_define('intersection-observer', function () { + return intersection_observer; +}); + +// screenfull +var screenfull = require('screenfull'); +_define('screenfull', function () { + return screenfull; +}); + +// headroom.js +var headroom = require('headroom.js/dist/headroom'); +_define('headroom', function () { + return headroom; +}); + +// apiclient +var apiclient = require('jellyfin-apiclient'); + +_define('apiclient', function () { + return apiclient.ApiClient; +}); + +_define('events', function () { + return apiclient.Events; +}); + +_define('credentialprovider', function () { + return apiclient.Credentials; +}); + +_define('connectionManagerFactory', function () { + return apiclient.ConnectionManager; +}); + +_define('appStorage', function () { + return apiclient.AppStorage; }); diff --git a/src/components/accessSchedule/accessSchedule.js b/src/components/accessSchedule/accessSchedule.js new file mode 100644 index 0000000000..166460a025 --- /dev/null +++ b/src/components/accessSchedule/accessSchedule.js @@ -0,0 +1,98 @@ +/* eslint-disable indent */ + +/** + * Module for controlling user parental control from. + * @module components/accessSchedule/accessSchedule + */ + +import dialogHelper from 'dialogHelper'; +import datetime from 'datetime'; +import globalize from 'globalize'; +import 'emby-select'; +import 'paper-icon-button-light'; +import 'formDialogStyle'; + + function getDisplayTime(hours) { + let minutes = 0; + const pct = hours % 1; + + if (pct) { + minutes = parseInt(60 * pct); + } + + return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0)); + } + + function populateHours(context) { + let html = ''; + + for (let i = 0; i < 24; i++) { + html += ``; + } + + html += ``; + context.querySelector('#selectStart').innerHTML = html; + context.querySelector('#selectEnd').innerHTML = html; + } + + function loadSchedule(context, {DayOfWeek, StartHour, EndHour}) { + context.querySelector('#selectDay').value = DayOfWeek || 'Sunday'; + context.querySelector('#selectStart').value = StartHour || 0; + context.querySelector('#selectEnd').value = EndHour || 0; + } + + function submitSchedule(context, options) { + const updatedSchedule = { + DayOfWeek: context.querySelector('#selectDay').value, + StartHour: context.querySelector('#selectStart').value, + EndHour: context.querySelector('#selectEnd').value + }; + + if (parseFloat(updatedSchedule.StartHour) >= parseFloat(updatedSchedule.EndHour)) { + return void alert(globalize.translate('ErrorMessageStartHourGreaterThanEnd')); + } + + context.submitted = true; + options.schedule = Object.assign(options.schedule, updatedSchedule); + dialogHelper.close(context); + } + + export function show(options) { + return new Promise((resolve, reject) => { + // TODO: remove require + require(['text!./components/accessSchedule/accessSchedule.template.html'], template => { + const dlg = dialogHelper.createDialog({ + removeOnClose: true, + size: 'small' + }); + dlg.classList.add('formDialog'); + let html = ''; + html += globalize.translateDocument(template); + dlg.innerHTML = html; + populateHours(dlg); + loadSchedule(dlg, options.schedule); + dialogHelper.open(dlg); + dlg.addEventListener('close', () => { + if (dlg.submitted) { + resolve(options.schedule); + } else { + reject(); + } + }); + dlg.querySelector('.btnCancel').addEventListener('click', () => { + dialogHelper.close(dlg); + }); + dlg.querySelector('form').addEventListener('submit', event => { + submitSchedule(dlg, options); + event.preventDefault(); + return false; + }); + }); + }); + } + +/* eslint-enable indent */ + +export default { + show: show +}; diff --git a/src/components/accessschedule/accessschedule.template.html b/src/components/accessSchedule/accessSchedule.template.html similarity index 94% rename from src/components/accessschedule/accessschedule.template.html rename to src/components/accessSchedule/accessSchedule.template.html index 02f11942ed..493150ae5e 100644 --- a/src/components/accessschedule/accessschedule.template.html +++ b/src/components/accessSchedule/accessSchedule.template.html @@ -1,6 +1,6 @@
-

${HeaderAccessSchedule} diff --git a/src/components/accessschedule/accessschedule.js b/src/components/accessschedule/accessschedule.js deleted file mode 100644 index 2f4be8b2a7..0000000000 --- a/src/components/accessschedule/accessschedule.js +++ /dev/null @@ -1,89 +0,0 @@ -define(["dialogHelper", "datetime", "emby-select", "paper-icon-button-light", "formDialogStyle"], function (dialogHelper, datetime) { - "use strict"; - - function getDisplayTime(hours) { - var minutes = 0; - var pct = hours % 1; - - if (pct) { - minutes = parseInt(60 * pct); - } - - return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0)); - } - - function populateHours(context) { - var html = ""; - - for (var i = 0; i < 24; i++) { - html += '"; - } - - html += '"; - context.querySelector("#selectStart").innerHTML = html; - context.querySelector("#selectEnd").innerHTML = html; - } - - function loadSchedule(context, schedule) { - context.querySelector("#selectDay").value = schedule.DayOfWeek || "Sunday"; - context.querySelector("#selectStart").value = schedule.StartHour || 0; - context.querySelector("#selectEnd").value = schedule.EndHour || 0; - } - - function submitSchedule(context, options) { - var updatedSchedule = { - DayOfWeek: context.querySelector("#selectDay").value, - StartHour: context.querySelector("#selectStart").value, - EndHour: context.querySelector("#selectEnd").value - }; - - if (parseFloat(updatedSchedule.StartHour) >= parseFloat(updatedSchedule.EndHour)) { - return void alert(Globalize.translate("ErrorMessageStartHourGreaterThanEnd")); - } - - context.submitted = true; - options.schedule = Object.assign(options.schedule, updatedSchedule); - dialogHelper.close(context); - } - - return { - show: function (options) { - return new Promise(function (resolve, reject) { - var xhr = new XMLHttpRequest(); - xhr.open("GET", "components/accessschedule/accessschedule.template.html", true); - - xhr.onload = function (e) { - var template = this.response; - var dlg = dialogHelper.createDialog({ - removeOnClose: true, - size: "small" - }); - dlg.classList.add("formDialog"); - var html = ""; - html += Globalize.translateDocument(template); - dlg.innerHTML = html; - populateHours(dlg); - loadSchedule(dlg, options.schedule); - dialogHelper.open(dlg); - dlg.addEventListener("close", function () { - if (dlg.submitted) { - resolve(options.schedule); - } else { - reject(); - } - }); - dlg.querySelector(".btnCancel").addEventListener("click", function (e) { - dialogHelper.close(dlg); - }); - dlg.querySelector("form").addEventListener("submit", function (e) { - submitSchedule(dlg, options); - e.preventDefault(); - return false; - }); - }; - - xhr.send(); - }); - } - }; -}); diff --git a/src/components/actionsheet/actionsheet.css b/src/components/actionSheet/actionSheet.css similarity index 100% rename from src/components/actionsheet/actionsheet.css rename to src/components/actionSheet/actionSheet.css diff --git a/src/components/actionSheet/actionSheet.js b/src/components/actionSheet/actionSheet.js new file mode 100644 index 0000000000..c56f42a9d9 --- /dev/null +++ b/src/components/actionSheet/actionSheet.js @@ -0,0 +1,340 @@ +import dialogHelper from 'dialogHelper'; +import layoutManager from 'layoutManager'; +import globalize from 'globalize'; +import dom from 'dom'; +import 'emby-button'; +import 'css!./actionSheet'; +import 'material-icons'; +import 'scrollStyles'; +import 'listViewStyle'; + +function getOffsets(elems) { + + let results = []; + + if (!document) { + return results; + } + + for (let elem of elems) { + let box = elem.getBoundingClientRect(); + + results.push({ + top: box.top, + left: box.left, + width: box.width, + height: box.height + }); + } + + return results; +} + +function getPosition(options, dlg) { + + const windowSize = dom.getWindowSize(); + const windowHeight = windowSize.innerHeight; + const windowWidth = windowSize.innerWidth; + + let pos = getOffsets([options.positionTo])[0]; + + if (options.positionY !== 'top') { + pos.top += (pos.height || 0) / 2; + } + + pos.left += (pos.width || 0) / 2; + + const height = dlg.offsetHeight || 300; + const width = dlg.offsetWidth || 160; + + // Account for popup size + pos.top -= height / 2; + pos.left -= width / 2; + + // Avoid showing too close to the bottom + const overflowX = pos.left + width - windowWidth; + const overflowY = pos.top + height - windowHeight; + + if (overflowX > 0) { + pos.left -= (overflowX + 20); + } + if (overflowY > 0) { + pos.top -= (overflowY + 20); + } + + pos.top += (options.offsetTop || 0); + pos.left += (options.offsetLeft || 0); + + // Do some boundary checking + pos.top = Math.max(pos.top, 10); + pos.left = Math.max(pos.left, 10); + + return pos; +} + +function centerFocus(elem, horiz, on) { + require(['scrollHelper'], function (scrollHelper) { + const fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); +} + +export function show(options) { + + // items + // positionTo + // showCancel + // title + let dialogOptions = { + removeOnClose: true, + enableHistory: options.enableHistory, + scrollY: false + }; + + let isFullscreen; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + isFullscreen = true; + dialogOptions.autoFocus = true; + } else { + + dialogOptions.modal = false; + dialogOptions.entryAnimation = options.entryAnimation; + dialogOptions.exitAnimation = options.exitAnimation; + dialogOptions.entryAnimationDuration = options.entryAnimationDuration || 140; + dialogOptions.exitAnimationDuration = options.exitAnimationDuration || 100; + dialogOptions.autoFocus = false; + } + + let dlg = dialogHelper.createDialog(dialogOptions); + + if (isFullscreen) { + dlg.classList.add('actionsheet-fullscreen'); + } else { + dlg.classList.add('actionsheet-not-fullscreen'); + } + + dlg.classList.add('actionSheet'); + + if (options.dialogClass) { + dlg.classList.add(options.dialogClass); + } + + let html = ''; + + const scrollClassName = layoutManager.tv ? 'scrollY smoothScrollY hiddenScrollY' : 'scrollY'; + let style = ''; + + // Admittedly a hack but right now the scrollbar is being factored into the width which is causing truncation + if (options.items.length > 20) { + const minWidth = dom.getWindowSize().innerWidth >= 300 ? 240 : 200; + style += 'min-width:' + minWidth + 'px;'; + } + + let renderIcon = false; + let icons = []; + let itemIcon; + for (let item of options.items) { + + itemIcon = item.icon || (item.selected ? 'check' : null); + + if (itemIcon) { + renderIcon = true; + } + icons.push(itemIcon || ''); + } + + if (layoutManager.tv) { + html += ``; + } + + // If any items have an icon, give them all an icon just to make sure they're all lined up evenly + const center = options.title && (!renderIcon /*|| itemsWithIcons.length != options.items.length*/); + + if (center || layoutManager.tv) { + html += '
'; + } else { + html += '
'; + } + + if (options.title) { + + html += '

' + options.title + '

'; + } + if (options.text) { + html += '

' + options.text + '

'; + } + + let scrollerClassName = 'actionSheetScroller'; + if (layoutManager.tv) { + scrollerClassName += ' actionSheetScroller-tv focuscontainer-x focuscontainer-y'; + } + html += '
'; + + let menuItemClass = 'listItem listItem-button actionSheetMenuItem'; + + if (options.border || options.shaded) { + menuItemClass += ' listItem-border'; + } + + if (options.menuItemClass) { + menuItemClass += ' ' + options.menuItemClass; + } + + if (layoutManager.tv) { + menuItemClass += ' listItem-focusscale'; + } + + if (layoutManager.mobile) { + menuItemClass += ' actionsheet-xlargeFont'; + } + + // 'options.items' is HTMLOptionsCollection, so no fancy loops + for (let i = 0; i < options.items.length; i++) { + const item = options.items[i]; + + if (item.divider) { + + html += '
'; + continue; + } + + const autoFocus = item.selected && layoutManager.tv ? ' autoFocus' : ''; + + // Check for null in case int 0 was passed in + const optionId = item.id == null || item.id === '' ? item.value : item.id; + html += ''; + + itemIcon = icons[i]; + + if (itemIcon) { + html += ``; + } else if (renderIcon && !center) { + html += ''; + } + + html += '
'; + + html += '
'; + html += (item.name || item.textContent || item.innerText); + html += '
'; + + if (item.secondaryText) { + html += `
${item.secondaryText}
`; + } + + html += '
'; + + if (item.asideText) { + html += `
${item.asideText}
`; + } + + html += ''; + } + + if (options.showCancel) { + html += '
'; + html += ``; + html += '
'; + } + html += '
'; + + dlg.innerHTML = html; + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.actionSheetScroller'), false, true); + } + + let btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet'); + if (btnCloseActionSheet) { + btnCloseActionSheet.addEventListener('click', function () { + dialogHelper.close(dlg); + }); + } + + // Seeing an issue in some non-chrome browsers where this is requiring a double click + //var eventName = browser.firefox ? 'mousedown' : 'click'; + let selectedId; + + let timeout; + if (options.timeout) { + timeout = setTimeout(function () { + dialogHelper.close(dlg); + }, options.timeout); + } + + return new Promise(function (resolve, reject) { + + let isResolved; + + dlg.addEventListener('click', function (e) { + + const actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem'); + + if (actionSheetMenuItem) { + selectedId = actionSheetMenuItem.getAttribute('data-id'); + + if (options.resolveOnClick) { + + if (options.resolveOnClick.indexOf) { + + if (options.resolveOnClick.indexOf(selectedId) !== -1) { + + resolve(selectedId); + isResolved = true; + } + + } else { + resolve(selectedId); + isResolved = true; + } + } + + dialogHelper.close(dlg); + } + + }); + + dlg.addEventListener('close', function () { + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.actionSheetScroller'), false, false); + } + + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + + if (!isResolved) { + if (selectedId != null) { + if (options.callback) { + options.callback(selectedId); + } + + resolve(selectedId); + } else { + reject(); + } + } + }); + + dialogHelper.open(dlg); + + const pos = options.positionTo && dialogOptions.size !== 'fullscreen' ? getPosition(options, dlg) : null; + + if (pos) { + dlg.style.position = 'fixed'; + dlg.style.margin = 0; + dlg.style.left = pos.left + 'px'; + dlg.style.top = pos.top + 'px'; + } + }); +} + +export default { + show: show +}; diff --git a/src/components/actionsheet/actionsheet.js b/src/components/actionsheet/actionsheet.js deleted file mode 100644 index 926d63b637..0000000000 --- a/src/components/actionsheet/actionsheet.js +++ /dev/null @@ -1,360 +0,0 @@ -define(['dialogHelper', 'layoutManager', 'globalize', 'browser', 'dom', 'emby-button', 'css!./actionsheet', 'material-icons', 'scrollStyles', 'listViewStyle'], function (dialogHelper, layoutManager, globalize, browser, dom) { - 'use strict'; - - function getOffsets(elems) { - - var doc = document; - var results = []; - - if (!doc) { - return results; - } - - var box; - var elem; - - for (var i = 0, length = elems.length; i < length; i++) { - - elem = elems[i]; - // Support: BlackBerry 5, iOS 3 (original iPhone) - // If we don't have gBCR, just use 0,0 rather than error - if (elem.getBoundingClientRect) { - box = elem.getBoundingClientRect(); - } else { - box = { top: 0, left: 0 }; - } - - results[i] = { - top: box.top, - left: box.left, - width: box.width, - height: box.height - }; - } - - return results; - } - - function getPosition(options, dlg) { - - var windowSize = dom.getWindowSize(); - var windowHeight = windowSize.innerHeight; - var windowWidth = windowSize.innerWidth; - - var pos = getOffsets([options.positionTo])[0]; - - if (options.positionY !== 'top') { - pos.top += (pos.height || 0) / 2; - } - - pos.left += (pos.width || 0) / 2; - - var height = dlg.offsetHeight || 300; - var width = dlg.offsetWidth || 160; - - // Account for popup size - pos.top -= height / 2; - pos.left -= width / 2; - - // Avoid showing too close to the bottom - var overflowX = pos.left + width - windowWidth; - var overflowY = pos.top + height - windowHeight; - - if (overflowX > 0) { - pos.left -= (overflowX + 20); - } - if (overflowY > 0) { - pos.top -= (overflowY + 20); - } - - pos.top += (options.offsetTop || 0); - pos.left += (options.offsetLeft || 0); - - // Do some boundary checking - pos.top = Math.max(pos.top, 10); - pos.left = Math.max(pos.left, 10); - - return pos; - } - - function centerFocus(elem, horiz, on) { - require(['scrollHelper'], function (scrollHelper) { - var fn = on ? 'on' : 'off'; - scrollHelper.centerFocus[fn](elem, horiz); - }); - } - - function show(options) { - - // items - // positionTo - // showCancel - // title - var dialogOptions = { - removeOnClose: true, - enableHistory: options.enableHistory, - scrollY: false - }; - - var backButton = false; - var isFullscreen; - - if (layoutManager.tv) { - dialogOptions.size = 'fullscreen'; - isFullscreen = true; - backButton = true; - dialogOptions.autoFocus = true; - } else { - - dialogOptions.modal = false; - dialogOptions.entryAnimation = options.entryAnimation; - dialogOptions.exitAnimation = options.exitAnimation; - dialogOptions.entryAnimationDuration = options.entryAnimationDuration || 140; - dialogOptions.exitAnimationDuration = options.exitAnimationDuration || 100; - dialogOptions.autoFocus = false; - } - - var dlg = dialogHelper.createDialog(dialogOptions); - - if (isFullscreen) { - dlg.classList.add('actionsheet-fullscreen'); - } else { - dlg.classList.add('actionsheet-not-fullscreen'); - } - - dlg.classList.add('actionSheet'); - - if (options.dialogClass) { - dlg.classList.add(options.dialogClass); - } - - var html = ''; - - var scrollClassName = layoutManager.tv ? 'scrollY smoothScrollY hiddenScrollY' : 'scrollY'; - var style = ''; - - // Admittedly a hack but right now the scrollbar is being factored into the width which is causing truncation - if (options.items.length > 20) { - var minWidth = dom.getWindowSize().innerWidth >= 300 ? 240 : 200; - style += "min-width:" + minWidth + "px;"; - } - - var i; - var length; - var option; - var renderIcon = false; - var icons = []; - var itemIcon; - for (i = 0, length = options.items.length; i < length; i++) { - - option = options.items[i]; - - itemIcon = option.icon || (option.selected ? 'check' : null); - - if (itemIcon) { - renderIcon = true; - } - icons.push(itemIcon || ''); - } - - if (layoutManager.tv) { - html += ''; - } - - // If any items have an icon, give them all an icon just to make sure they're all lined up evenly - var center = options.title && (!renderIcon /*|| itemsWithIcons.length != options.items.length*/); - - if (center || layoutManager.tv) { - html += '
'; - } else { - html += '
'; - } - - if (options.title) { - - html += '

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

'; - } - if (options.text) { - html += '

'; - html += options.text; - html += '

'; - } - - var scrollerClassName = 'actionSheetScroller'; - if (layoutManager.tv) { - scrollerClassName += ' actionSheetScroller-tv focuscontainer-x focuscontainer-y'; - } - html += '
'; - - var menuItemClass = 'listItem listItem-button actionSheetMenuItem'; - - if (options.border || options.shaded) { - menuItemClass += ' listItem-border'; - } - - if (options.menuItemClass) { - menuItemClass += ' ' + options.menuItemClass; - } - - if (layoutManager.tv) { - menuItemClass += ' listItem-focusscale'; - } - - if (layoutManager.mobile) { - menuItemClass += ' actionsheet-xlargeFont'; - } - - for (i = 0, length = options.items.length; i < length; i++) { - - option = options.items[i]; - - if (option.divider) { - - html += '
'; - continue; - } - - var autoFocus = option.selected && layoutManager.tv ? ' autoFocus' : ''; - - // Check for null in case int 0 was passed in - var optionId = option.id == null || option.id === '' ? option.value : option.id; - html += ''; - - itemIcon = icons[i]; - - if (itemIcon) { - - html += '' + itemIcon + ''; - } else if (renderIcon && !center) { - html += ''; - } - - html += '
'; - - html += '
'; - html += (option.name || option.textContent || option.innerText); - html += '
'; - - if (option.secondaryText) { - html += '
'; - html += option.secondaryText; - html += '
'; - } - - html += '
'; - - if (option.asideText) { - html += '
'; - html += option.asideText; - html += '
'; - } - - html += ''; - } - - if (options.showCancel) { - html += '
'; - html += ''; - html += '
'; - } - html += '
'; - - dlg.innerHTML = html; - - if (layoutManager.tv) { - centerFocus(dlg.querySelector('.actionSheetScroller'), false, true); - } - - var btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet'); - if (btnCloseActionSheet) { - dlg.querySelector('.btnCloseActionSheet').addEventListener('click', function () { - dialogHelper.close(dlg); - }); - } - - // Seeing an issue in some non-chrome browsers where this is requiring a double click - //var eventName = browser.firefox ? 'mousedown' : 'click'; - var selectedId; - - var timeout; - if (options.timeout) { - timeout = setTimeout(function () { - dialogHelper.close(dlg); - }, options.timeout); - } - - return new Promise(function (resolve, reject) { - - var isResolved; - - dlg.addEventListener('click', function (e) { - - var actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem'); - - if (actionSheetMenuItem) { - selectedId = actionSheetMenuItem.getAttribute('data-id'); - - if (options.resolveOnClick) { - - if (options.resolveOnClick.indexOf) { - - if (options.resolveOnClick.indexOf(selectedId) !== -1) { - - resolve(selectedId); - isResolved = true; - } - - } else { - resolve(selectedId); - isResolved = true; - } - } - - dialogHelper.close(dlg); - } - - }); - - dlg.addEventListener('close', function () { - - if (layoutManager.tv) { - centerFocus(dlg.querySelector('.actionSheetScroller'), false, false); - } - - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - - if (!isResolved) { - if (selectedId != null) { - if (options.callback) { - options.callback(selectedId); - } - - resolve(selectedId); - } else { - reject(); - } - } - }); - - dialogHelper.open(dlg); - - var pos = options.positionTo && dialogOptions.size !== 'fullscreen' ? getPosition(options, dlg) : null; - - if (pos) { - dlg.style.position = 'fixed'; - dlg.style.margin = 0; - dlg.style.left = pos.left + 'px'; - dlg.style.top = pos.top + 'px'; - } - }); - } - - return { - show: show - }; -}); diff --git a/src/components/activitylog.js b/src/components/activitylog.js index 62eda74d5f..bbb0995063 100644 --- a/src/components/activitylog.js +++ b/src/components/activitylog.js @@ -1,59 +1,63 @@ -define(["events", "globalize", "dom", "date-fns", "dfnshelper", "userSettings", "serverNotifications", "connectionManager", "emby-button", "listViewStyle"], function (events, globalize, dom, datefns, dfnshelper, userSettings, serverNotifications, connectionManager) { - "use strict"; +define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings', 'serverNotifications', 'connectionManager', 'emby-button', 'listViewStyle'], function (events, globalize, dom, datefns, dfnshelper, userSettings, serverNotifications, connectionManager) { + 'use strict'; function getEntryHtml(entry, apiClient) { - var html = ""; + var html = ''; html += '
'; - var color = "#00a4dc"; - var icon = "notifications"; + var color = '#00a4dc'; + var icon = 'notifications'; - if ("Error" == entry.Severity || "Fatal" == entry.Severity || "Warn" == entry.Severity) { - color = "#cc0000"; - icon = "notification_important"; + if ('Error' == entry.Severity || 'Fatal' == entry.Severity || 'Warn' == entry.Severity) { + color = '#cc0000'; + icon = 'notification_important'; } if (entry.UserId && entry.UserPrimaryImageTag) { - html += 'dvr"; + }) + "');background-repeat:no-repeat;background-position:center center;background-size: cover;\">"; } else { - html += '' + icon + ''; + html += ''; } html += '
'; html += '
'; html += entry.Name; - html += "
"; + html += '
'; html += '
'; html += datefns.formatRelative(Date.parse(entry.Date), Date.parse(new Date()), { locale: dfnshelper.getLocale() }); - html += "
"; + html += '
'; html += '
'; - html += entry.ShortOverview || ""; - html += "
"; - html += "
"; + html += entry.ShortOverview || ''; + html += '
'; + html += '
'; if (entry.Overview) { - html += ''; + html += ``; } - return html += "
"; + html += '

'; + + return html; } function renderList(elem, apiClient, result, startIndex, limit) { elem.innerHTML = result.Items.map(function (i) { return getEntryHtml(i, apiClient); - }).join(""); + }).join(''); } function reloadData(instance, elem, apiClient, startIndex, limit) { if (null == startIndex) { - startIndex = parseInt(elem.getAttribute("data-activitystartindex") || "0"); + startIndex = parseInt(elem.getAttribute('data-activitystartindex') || '0'); } - limit = limit || parseInt(elem.getAttribute("data-activitylimit") || "7"); + limit = limit || parseInt(elem.getAttribute('data-activitylimit') || '7'); var minDate = new Date(); - var hasUserId = "false" !== elem.getAttribute("data-useractivity"); + var hasUserId = 'false' !== elem.getAttribute('data-useractivity'); if (hasUserId) { minDate.setTime(minDate.getTime() - 24 * 60 * 60 * 1000); // one day back @@ -61,22 +65,22 @@ define(["events", "globalize", "dom", "date-fns", "dfnshelper", "userSettings", minDate.setTime(minDate.getTime() - 7 * 24 * 60 * 60 * 1000); // one week back } - ApiClient.getJSON(ApiClient.getUrl("System/ActivityLog/Entries", { + ApiClient.getJSON(ApiClient.getUrl('System/ActivityLog/Entries', { startIndex: startIndex, limit: limit, minDate: minDate.toISOString(), hasUserId: hasUserId })).then(function (result) { - elem.setAttribute("data-activitystartindex", startIndex); - elem.setAttribute("data-activitylimit", limit); + elem.setAttribute('data-activitystartindex', startIndex); + elem.setAttribute('data-activitylimit', limit); if (!startIndex) { - var activityContainer = dom.parentWithClass(elem, "activityContainer"); + var activityContainer = dom.parentWithClass(elem, 'activityContainer'); if (activityContainer) { if (result.Items.length) { - activityContainer.classList.remove("hide"); + activityContainer.classList.remove('hide'); } else { - activityContainer.classList.add("hide"); + activityContainer.classList.add('hide'); } } } @@ -95,10 +99,10 @@ define(["events", "globalize", "dom", "date-fns", "dfnshelper", "userSettings", } function onListClick(e) { - var btnEntryInfo = dom.parentWithClass(e.target, "btnEntryInfo"); + var btnEntryInfo = dom.parentWithClass(e.target, 'btnEntryInfo'); if (btnEntryInfo) { - var id = btnEntryInfo.getAttribute("data-id"); + var id = btnEntryInfo.getAttribute('data-id'); var items = this.items; if (items) { @@ -114,7 +118,7 @@ define(["events", "globalize", "dom", "date-fns", "dfnshelper", "userSettings", } function showItemOverview(item) { - require(["alert"], function (alert) { + require(['alert'], function (alert) { alert({ text: item.Overview }); @@ -124,28 +128,28 @@ define(["events", "globalize", "dom", "date-fns", "dfnshelper", "userSettings", function ActivityLog(options) { this.options = options; var element = options.element; - element.classList.add("activityLogListWidget"); - element.addEventListener("click", onListClick.bind(this)); + element.classList.add('activityLogListWidget'); + element.addEventListener('click', onListClick.bind(this)); var apiClient = connectionManager.getApiClient(options.serverId); reloadData(this, element, apiClient); var onUpdate = onActivityLogUpdate.bind(this); this.updateFn = onUpdate; - events.on(serverNotifications, "ActivityLogEntry", onUpdate); - apiClient.sendMessage("ActivityLogEntryStart", "0,1500"); + events.on(serverNotifications, 'ActivityLogEntry', onUpdate); + apiClient.sendMessage('ActivityLogEntryStart', '0,1500'); } ActivityLog.prototype.destroy = function () { var options = this.options; if (options) { - options.element.classList.remove("activityLogListWidget"); - connectionManager.getApiClient(options.serverId).sendMessage("ActivityLogEntryStop", "0,1500"); + options.element.classList.remove('activityLogListWidget'); + connectionManager.getApiClient(options.serverId).sendMessage('ActivityLogEntryStop', '0,1500'); } var onUpdate = this.updateFn; if (onUpdate) { - events.off(serverNotifications, "ActivityLogEntry", onUpdate); + events.off(serverNotifications, 'ActivityLogEntry', onUpdate); } this.items = null; diff --git a/src/components/alphapicker/alphapicker.js b/src/components/alphaPicker/alphaPicker.js similarity index 95% rename from src/components/alphapicker/alphapicker.js rename to src/components/alphaPicker/alphaPicker.js index 455a43b46d..79f74879e5 100644 --- a/src/components/alphapicker/alphapicker.js +++ b/src/components/alphaPicker/alphaPicker.js @@ -67,7 +67,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b html += '
'; if (options.mode === 'keyboard') { - html += ''; + html += ''; } else { letters = ['#']; html += mapLetters(letters, vertical).join(''); @@ -77,7 +77,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b html += mapLetters(letters, vertical).join(''); if (options.mode === 'keyboard') { - html += ''; + html += ''; html += '
'; letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; @@ -132,7 +132,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b if (alphaPickerButton) { var value = alphaPickerButton.getAttribute('data-value'); - element.dispatchEvent(new CustomEvent("alphavalueclicked", { + element.dispatchEvent(new CustomEvent('alphavalueclicked', { cancelable: false, detail: { value: value @@ -262,7 +262,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b } if (applyValue) { - element.dispatchEvent(new CustomEvent("alphavaluechanged", { + element.dispatchEvent(new CustomEvent('alphavaluechanged', { cancelable: false, detail: { value: value diff --git a/src/components/alphapicker/style.css b/src/components/alphaPicker/style.css similarity index 100% rename from src/components/alphapicker/style.css rename to src/components/alphaPicker/style.css diff --git a/src/components/appfooter/appfooter.css b/src/components/appFooter/appFooter.css similarity index 100% rename from src/components/appfooter/appfooter.css rename to src/components/appFooter/appFooter.css diff --git a/src/components/appfooter/appfooter.js b/src/components/appFooter/appFooter.js similarity index 93% rename from src/components/appfooter/appfooter.js rename to src/components/appFooter/appFooter.js index 07d7701ff2..033a0b008d 100644 --- a/src/components/appfooter/appfooter.js +++ b/src/components/appFooter/appFooter.js @@ -1,4 +1,4 @@ -define(['browser', 'css!./appfooter'], function (browser) { +define(['browser', 'css!./appFooter'], function (browser) { 'use strict'; function render(options) { diff --git a/src/components/appRouter.js b/src/components/appRouter.js index 23934467be..0861cf7e00 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -1,4 +1,4 @@ -define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinManager', 'pluginManager', 'backdrop', 'browser', 'page', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, layoutManager, skinManager, pluginManager, backdrop, browser, page, appSettings, appHost, connectionManager) { +define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdrop', 'browser', 'page', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, skinManager, backdrop, browser, page, appSettings, appHost, connectionManager) { 'use strict'; var appRouter = { @@ -16,7 +16,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM show('/settings/settings.html'); }, showNowPlaying: function () { - show("/nowplaying.html"); + show('/nowplaying.html'); } }; @@ -26,11 +26,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM connectionManager.connect({ enableAutoLogin: appSettings.enableAutoLogin() }).then(function (result) { - handleConnectionResult(result, loading); + handleConnectionResult(result); }); } - function handleConnectionResult(result, loading) { + function handleConnectionResult(result) { switch (result.State) { case 'SignedIn': loading.hide(); @@ -200,8 +200,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM var apiClient = this; - if (data.status === 401) { - if (data.errorCode === "ParentalControl") { + if (data.status === 403) { + if (data.errorCode === 'ParentalControl') { var isCurrentAllowed = currentRouteInfo ? (currentRouteInfo.route.anonymous || currentRouteInfo.route.startup) : true; @@ -246,13 +246,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM } if (setQuality) { - - var quality = 100; - + var quality; var type = options.type || 'Primary'; if (browser.tv || browser.slow) { - + // TODO: wtf if (browser.chrome) { // webp support quality = type === 'Primary' ? 40 : 50; @@ -268,6 +266,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM } function getMaxBandwidth() { + /* eslint-disable compat/compat */ if (navigator.connection) { var max = navigator.connection.downlinkMax; if (max && max > 0 && max < Number.POSITIVE_INFINITY) { @@ -279,6 +278,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM return max; } } + /* eslint-enable compat/compat */ return null; } @@ -382,7 +382,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM if (firstResult.State !== 'SignedIn' && !route.anonymous) { - handleConnectionResult(firstResult, loading); + handleConnectionResult(firstResult); return; } } @@ -461,7 +461,6 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM return Promise.resolve(); } - var isHandlingBackToDefault; var isDummyBackToHome; function loadContent(ctx, route, html, request) { @@ -539,15 +538,15 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM } function param(name, url) { - name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); - var regexS = "[\\?&]" + name + "=([^&#]*)"; - var regex = new RegExp(regexS, "i"); + name = name.replace(/[\[]/, '\\\[').replace(/[\]]/, '\\\]'); + var regexS = '[\\?&]' + name + '=([^&#]*)'; + var regex = new RegExp(regexS, 'i'); var results = regex.exec(url || getWindowLocationSearch()); if (results == null) { - return ""; + return ''; } else { - return decodeURIComponent(results[1].replace(/\+/g, " ")); + return decodeURIComponent(results[1].replace(/\+/g, ' ')); } } @@ -577,7 +576,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM function showDirect(path) { return new Promise(function(resolve, reject) { - resolveOnNextShow = resolve, page.show(baseUrl()+path); + resolveOnNextShow = resolve; + page.show(baseUrl() + path); }); } @@ -586,8 +586,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM path = '/' + path; } - var baseRoute = baseUrl(); - path = path.replace(baseRoute, ''); + path = path.replace(baseUrl(), ''); if (currentRouteInfo && currentRouteInfo.path === path) { // can't use this with home right now due to the back menu @@ -618,10 +617,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM } function showItem(item, serverId, options) { + // TODO: Refactor this so it only gets items, not strings. if (typeof (item) === 'string') { var apiClient = serverId ? connectionManager.getApiClient(serverId) : connectionManager.currentApiClient(); - apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (item) { - appRouter.showItem(item, options); + apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (itemObject) { + appRouter.showItem(itemObject, options); }); } else { if (arguments.length === 2) { diff --git a/src/components/apphost.js b/src/components/apphost.js index 1f6695d9f8..f200b9a642 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -1,17 +1,17 @@ -define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], function (appSettings, browser, events, htmlMediaHelper, webSettings) { - "use strict"; +define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'globalize'], function (appSettings, browser, events, htmlMediaHelper, webSettings, globalize) { + 'use strict'; function getBaseProfileOptions(item) { var disableHlsVideoAudioCodecs = []; if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) { - if (browser.edge || browser.msie) { - disableHlsVideoAudioCodecs.push("mp3"); + if (browser.edge) { + disableHlsVideoAudioCodecs.push('mp3'); } - disableHlsVideoAudioCodecs.push("ac3"); - disableHlsVideoAudioCodecs.push("eac3"); - disableHlsVideoAudioCodecs.push("opus"); + disableHlsVideoAudioCodecs.push('ac3'); + disableHlsVideoAudioCodecs.push('eac3'); + disableHlsVideoAudioCodecs.push('opus'); } return { @@ -22,7 +22,7 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f function getDeviceProfileForWindowsUwp(item) { return new Promise(function (resolve, reject) { - require(["browserdeviceprofile", "environments/windows-uwp/mediacaps"], function (profileBuilder, uwpMediaCaps) { + require(['browserdeviceprofile', 'environments/windows-uwp/mediacaps'], function (profileBuilder, uwpMediaCaps) { var profileOptions = getBaseProfileOptions(item); profileOptions.supportsDts = uwpMediaCaps.supportsDTS(); profileOptions.supportsTrueHd = uwpMediaCaps.supportsDolby(); @@ -40,26 +40,15 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f } return new Promise(function (resolve) { - require(["browserdeviceprofile"], function (profileBuilder) { + require(['browserdeviceprofile'], function (profileBuilder) { var profile; if (window.NativeShell) { profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); } else { - profile = profileBuilder(getBaseProfileOptions(item)); - - if (item && !options.isRetry && "allcomplexformats" !== appSettings.get("subtitleburnin")) { - if (!browser.orsay && !browser.tizen) { - profile.SubtitleProfiles.push({ - Format: "ass", - Method: "External" - }); - profile.SubtitleProfiles.push({ - Format: "ssa", - Method: "External" - }); - } - } + var builderOpts = getBaseProfileOptions(item); + builderOpts.enableSsaRender = (item && !options.isRetry && 'allcomplexformats' !== appSettings.get('subtitleburnin')); + profile = profileBuilder(builderOpts); } resolve(profile); @@ -68,12 +57,12 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f } function escapeRegExp(str) { - return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); + return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); } function replaceAll(originalString, strReplace, strWith) { var strReplace2 = escapeRegExp(strReplace); - var reg = new RegExp(strReplace2, "ig"); + var reg = new RegExp(strReplace2, 'ig'); return originalString.replace(reg, strWith); } @@ -81,7 +70,7 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f var keys = []; if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), self.btoa) { - var result = replaceAll(btoa(keys.join("|")), "=", "1"); + var result = replaceAll(btoa(keys.join('|')), '=', '1'); return Promise.resolve(result); } @@ -89,7 +78,7 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f } function getDeviceId() { - var key = "_deviceId2"; + var key = '_deviceId2'; var deviceId = appSettings.get(key); if (deviceId) { @@ -104,18 +93,36 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f function getDeviceName() { var deviceName; - deviceName = browser.tizen ? "Samsung Smart TV" : browser.web0s ? "LG Smart TV" : browser.operaTv ? "Opera TV" : browser.xboxOne ? "Xbox One" : browser.ps4 ? "Sony PS4" : browser.chrome ? "Chrome" : browser.edge ? "Edge" : browser.firefox ? "Firefox" : browser.msie ? "Internet Explorer" : browser.opera ? "Opera" : browser.safari ? "Safari" : "Web Browser"; + if (browser.tizen) { + deviceName = 'Samsung Smart TV'; + } else if (browser.web0s) { + deviceName = 'LG Smart TV'; + } else if (browser.operaTv) { + deviceName = 'Opera TV'; + } else if (browser.xboxOne) { + deviceName = 'Xbox One'; + } else if (browser.ps4) { + deviceName = 'Sony PS4'; + } else if (browser.chrome) { + deviceName = 'Chrome'; + } else if (browser.edge) { + deviceName = 'Edge'; + } else if (browser.firefox) { + deviceName = 'Firefox'; + } else if (browser.opera) { + deviceName = 'Opera'; + } else if (browser.safari) { + deviceName = 'Safari'; + } else { + deviceName = 'Web Browser'; + } if (browser.ipad) { - deviceName += " iPad"; - } else { - if (browser.iphone) { - deviceName += " iPhone"; - } else { - if (browser.android) { - deviceName += " Android"; - } - } + deviceName += ' iPad'; + } else if (browser.iphone) { + deviceName += ' iPhone'; + } else if (browser.android) { + deviceName += ' Android'; } return deviceName; @@ -135,12 +142,12 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f } var element = document.documentElement; - return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement("video").webkitEnterFullscreen; + return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement('video').webkitEnterFullscreen; } function getSyncProfile() { return new Promise(function (resolve) { - require(["browserdeviceprofile", "appSettings"], function (profileBuilder, appSettings) { + require(['browserdeviceprofile', 'appSettings'], function (profileBuilder, appSettings) { var profile; if (window.NativeShell) { @@ -156,7 +163,7 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f } function getDefaultLayout() { - return "desktop"; + return 'desktop'; } function supportsHtmlMediaAutoplay() { @@ -173,20 +180,20 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f function supportsCue() { try { - var video = document.createElement("video"); - var style = document.createElement("style"); + var video = document.createElement('video'); + var style = document.createElement('style'); - style.textContent = "video::cue {background: inherit}"; + style.textContent = 'video::cue {background: inherit}'; document.body.appendChild(style); document.body.appendChild(video); - var cue = window.getComputedStyle(video, "::cue").background; + var cue = window.getComputedStyle(video, '::cue').background; document.body.removeChild(style); document.body.removeChild(video); return !!cue.length; } catch (err) { - console.error("error detecting cue support: " + err); + console.error('error detecting cue support: ' + err); return false; } } @@ -194,15 +201,15 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f function onAppVisible() { if (isHidden) { isHidden = false; - console.debug("triggering app resume event"); - events.trigger(appHost, "resume"); + console.debug('triggering app resume event'); + events.trigger(appHost, 'resume'); } } function onAppHidden() { if (!isHidden) { isHidden = true; - console.debug("app is hidden"); + console.debug('app is hidden'); } } @@ -210,92 +217,88 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f var features = []; if (navigator.share) { - features.push("sharing"); + features.push('sharing'); } if (!browser.edgeUwp && !browser.tv && !browser.xboxOne && !browser.ps4) { - features.push("filedownload"); + features.push('filedownload'); } if (browser.operaTv || browser.tizen || browser.orsay || browser.web0s) { - features.push("exit"); + features.push('exit'); } else { - features.push("exitmenu"); - features.push("plugins"); + features.push('exitmenu'); + features.push('plugins'); } if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.ps4) { - features.push("externallinks"); - features.push("externalpremium"); + features.push('externallinks'); + features.push('externalpremium'); } if (!browser.operaTv) { - features.push("externallinkdisplay"); + features.push('externallinkdisplay'); } if (supportsVoiceInput()) { - features.push("voiceinput"); - } - - if (!browser.tv && !browser.xboxOne) { - browser.ps4; + features.push('voiceinput'); } if (supportsHtmlMediaAutoplay()) { - features.push("htmlaudioautoplay"); - features.push("htmlvideoautoplay"); + features.push('htmlaudioautoplay'); + features.push('htmlvideoautoplay'); } if (browser.edgeUwp) { - features.push("sync"); + features.push('sync'); } if (supportsFullscreen()) { - features.push("fullscreenchange"); + features.push('fullscreenchange'); } if (browser.chrome || browser.edge && !browser.slow) { if (!browser.noAnimation && !browser.edgeUwp && !browser.xboxOne) { - features.push("imageanalysis"); + features.push('imageanalysis'); } } if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) { - features.push("physicalvolumecontrol"); + features.push('physicalvolumecontrol'); } if (!browser.tv && !browser.xboxOne && !browser.ps4) { - features.push("remotecontrol"); + features.push('remotecontrol'); } if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.edgeUwp) { - features.push("remotevideo"); + features.push('remotevideo'); } - features.push("displaylanguage"); - features.push("otherapppromotions"); - features.push("displaymode"); - features.push("targetblank"); - features.push("screensaver"); + features.push('displaylanguage'); + features.push('otherapppromotions'); + features.push('displaymode'); + features.push('targetblank'); + features.push('screensaver'); webSettings.enableMultiServer().then(enabled => { - if (enabled) features.push("multiserver"); + if (enabled) features.push('multiserver'); }); - if (!browser.orsay && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) { - features.push("subtitleappearancesettings"); + if (!browser.orsay && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) { + features.push('subtitleappearancesettings'); } if (!browser.orsay) { - features.push("subtitleburnsettings"); + features.push('subtitleburnsettings'); } if (!browser.tv && !browser.ps4 && !browser.xboxOne) { - features.push("fileinput"); + features.push('fileinput'); } if (browser.chrome) { - features.push("chromecast"); + features.push('chromecast'); } return features; @@ -316,7 +319,7 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f window.close(); } } catch (err) { - console.error("error closing application: " + err); + console.error('error closing application: ' + err); } } @@ -330,15 +333,15 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f return; } - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { exitPromise = actionsheet.show({ - title: Globalize.translate("MessageConfirmAppExit"), + title: globalize.translate('MessageConfirmAppExit'), items: [ - {id: "yes", name: Globalize.translate("Yes")}, - {id: "no", name: Globalize.translate("No")} + {id: 'yes', name: globalize.translate('Yes')}, + {id: 'no', name: globalize.translate('No')} ] }).then(function (value) { - if (value === "yes") { + if (value === 'yes') { doExit(); } }).finally(function () { @@ -349,17 +352,15 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f var deviceId; var deviceName; - var appName = "Jellyfin Web"; - var appVersion = "10.5.0"; - var visibilityChange; - var visibilityState; + var appName = 'Jellyfin Web'; + var appVersion = '10.6.0'; var appHost = { getWindowState: function () { - return document.windowState || "Normal"; + return document.windowState || 'Normal'; }, setWindowState: function (state) { - alert("setWindowState is not supported and should not be called"); + alert('setWindowState is not supported and should not be called'); }, exit: function () { if (!!window.appMode && browser.tizen) { @@ -376,7 +377,6 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f return -1 !== supportedFeatures.indexOf(command.toLowerCase()); }, preferVisualCards: browser.android || browser.chrome, - moreIcon: browser.android ? "more_vert" : "more_horiz", getSyncProfile: getSyncProfile, getDefaultLayout: function () { if (window.NativeShell) { @@ -412,58 +412,44 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f return {}; }, setThemeColor: function (color) { - var metaThemeColor = document.querySelector("meta[name=theme-color]"); + var metaThemeColor = document.querySelector('meta[name=theme-color]'); if (metaThemeColor) { - metaThemeColor.setAttribute("content", color); + metaThemeColor.setAttribute('content', color); } }, setUserScalable: function (scalable) { if (!browser.tv) { - var att = scalable ? "width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes" : "width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"; - document.querySelector("meta[name=viewport]").setAttribute("content", att); + var att = scalable ? 'width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes' : 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no'; + document.querySelector('meta[name=viewport]').setAttribute('content', att); } } }; - var doc = self.document; var isHidden = false; + var hidden; + var visibilityChange; - if (doc) { - if (void 0 !== doc.visibilityState) { - visibilityChange = "visibilitychange"; - visibilityState = "hidden"; + if (typeof document.hidden !== 'undefined') { /* eslint-disable-line compat/compat */ + hidden = 'hidden'; + visibilityChange = 'visibilitychange'; + } else if (typeof document.webkitHidden !== 'undefined') { + hidden = 'webkitHidden'; + visibilityChange = 'webkitvisibilitychange'; + } + + document.addEventListener(visibilityChange, function () { + /* eslint-disable-next-line compat/compat */ + if (document[hidden]) { + onAppHidden(); } else { - if (void 0 !== doc.mozHidden) { - visibilityChange = "mozvisibilitychange"; - visibilityState = "mozVisibilityState"; - } else { - if (void 0 !== doc.msHidden) { - visibilityChange = "msvisibilitychange"; - visibilityState = "msVisibilityState"; - } else { - if (void 0 !== doc.webkitHidden) { - visibilityChange = "webkitvisibilitychange"; - visibilityState = "webkitVisibilityState"; - } - } - } + onAppVisible(); } - } - - if (doc) { - doc.addEventListener(visibilityChange, function () { - if (document[visibilityState]) { - onAppHidden(); - } else { - onAppVisible(); - } - }); - } + }, false); if (self.addEventListener) { - self.addEventListener("focus", onAppVisible); - self.addEventListener("blur", onAppHidden); + self.addEventListener('focus', onAppVisible); + self.addEventListener('blur', onAppHidden); } return appHost; diff --git a/src/components/autoFocuser.js b/src/components/autoFocuser.js index 43c341bfdf..0a10cabd2f 100644 --- a/src/components/autoFocuser.js +++ b/src/components/autoFocuser.js @@ -5,8 +5,8 @@ * @module components/autoFocuser */ -import focusManager from "focusManager"; -import layoutManager from "layoutManager"; +import focusManager from 'focusManager'; +import layoutManager from 'layoutManager'; /** * Previously selected element. @@ -28,11 +28,11 @@ import layoutManager from "layoutManager"; return; } - window.addEventListener("focusin", function (e) { + window.addEventListener('focusin', function (e) { activeElement = e.target; }); - console.debug("AutoFocuser enabled"); + console.debug('AutoFocuser enabled'); } /** @@ -51,21 +51,21 @@ import layoutManager from "layoutManager"; if (activeElement) { // These elements are recreated - if (activeElement.classList.contains("btnPreviousPage")) { - candidates.push(container.querySelector(".btnPreviousPage")); - candidates.push(container.querySelector(".btnNextPage")); - } else if (activeElement.classList.contains("btnNextPage")) { - candidates.push(container.querySelector(".btnNextPage")); - candidates.push(container.querySelector(".btnPreviousPage")); - } else if (activeElement.classList.contains("btnSelectView")) { - candidates.push(container.querySelector(".btnSelectView")); + if (activeElement.classList.contains('btnPreviousPage')) { + candidates.push(container.querySelector('.btnPreviousPage')); + candidates.push(container.querySelector('.btnNextPage')); + } else if (activeElement.classList.contains('btnNextPage')) { + candidates.push(container.querySelector('.btnNextPage')); + candidates.push(container.querySelector('.btnPreviousPage')); + } else if (activeElement.classList.contains('btnSelectView')) { + candidates.push(container.querySelector('.btnSelectView')); } candidates.push(activeElement); } - candidates = candidates.concat(Array.from(container.querySelectorAll(".btnResume"))); - candidates = candidates.concat(Array.from(container.querySelectorAll(".btnPlay"))); + candidates = candidates.concat(Array.from(container.querySelectorAll('.btnResume'))); + candidates = candidates.concat(Array.from(container.querySelectorAll('.btnPlay'))); let focusedElement; @@ -81,7 +81,7 @@ import layoutManager from "layoutManager"; if (!focusedElement) { // FIXME: Multiple itemsContainers - const itemsContainer = container.querySelector(".itemsContainer"); + const itemsContainer = container.querySelector('.itemsContainer'); if (itemsContainer) { focusedElement = focusManager.autoFocus(itemsContainer); diff --git a/src/components/backdrop/backdrop.js b/src/components/backdrop/backdrop.js index ec5b411853..c15e35524c 100644 --- a/src/components/backdrop/backdrop.js +++ b/src/components/backdrop/backdrop.js @@ -1,4 +1,4 @@ -define(['browser', 'connectionManager', 'playbackManager', 'dom', "userSettings", 'css!./backdrop'], function (browser, connectionManager, playbackManager, dom, userSettings) { +define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings', 'css!./backdrop'], function (browser, connectionManager, playbackManager, dom, userSettings) { 'use strict'; function enableAnimation(elem) { @@ -180,7 +180,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', "userSettings" if (item.BackdropImageTags && item.BackdropImageTags.length > 0) { return item.BackdropImageTags.map(function (imgTag, index) { return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, { - type: "Backdrop", + type: 'Backdrop', tag: imgTag, maxWidth: dom.getScreenWidth(), index: index @@ -191,7 +191,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', "userSettings" if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) { return item.ParentBackdropImageTags.map(function (imgTag, index) { return apiClient.getScaledImageUrl(item.ParentBackdropItemId, Object.assign(imageOptions, { - type: "Backdrop", + type: 'Backdrop', tag: imgTag, maxWidth: dom.getScreenWidth(), index: index diff --git a/src/components/backdropscreensaver/plugin.js b/src/components/backdropscreensaver/plugin.js deleted file mode 100644 index 55de27a138..0000000000 --- a/src/components/backdropscreensaver/plugin.js +++ /dev/null @@ -1,56 +0,0 @@ -define(["connectionManager"], function (connectionManager) { - - return function () { - - var self = this; - - self.name = "Backdrop ScreenSaver"; - self.type = "screensaver"; - self.id = "backdropscreensaver"; - self.supportsAnonymous = false; - - var currentSlideshow; - - self.show = function () { - - var query = { - ImageTypes: "Backdrop", - EnableImageTypes: "Backdrop", - IncludeItemTypes: "Movie,Series,MusicArtist", - SortBy: "Random", - Recursive: true, - Fields: "Taglines", - ImageTypeLimit: 1, - StartIndex: 0, - Limit: 200 - }; - - var apiClient = connectionManager.currentApiClient(); - apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (result) { - - if (result.Items.length) { - - require(["slideshow"], function (slideshow) { - - var newSlideShow = new slideshow({ - showTitle: true, - cover: true, - items: result.Items - }); - - newSlideShow.show(); - currentSlideshow = newSlideShow; - }); - } - }); - }; - - self.hide = function () { - - if (currentSlideshow) { - currentSlideshow.hide(); - currentSlideshow = null; - } - }; - }; -}); diff --git a/src/components/cardbuilder/card.css b/src/components/cardbuilder/card.css index 3cd038cd09..c24fcf6ba6 100644 --- a/src/components/cardbuilder/card.css +++ b/src/components/cardbuilder/card.css @@ -306,6 +306,10 @@ button::-moz-focus-inner { text-align: left; } +.dialog .cardText { + text-overflow: initial; +} + .cardText-secondary { font-size: 86%; } diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index a4cf6edadc..e540a40211 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -286,7 +286,7 @@ import 'programStyles'; * @param {Object} options - Options for handling the items. */ function setCardData(items, options) { - options.shape = options.shape || "auto"; + options.shape = options.shape || 'auto'; const primaryImageAspectRatio = imageLoader.getPrimaryImageAspectRatio(items); @@ -503,94 +503,49 @@ import 'programStyles'; const primaryImageAspectRatio = item.PrimaryImageAspectRatio; let forceName = false; let imgUrl = null; + let imgTag = null; let coverImage = false; let uiAspect = null; + let imgType = null; + let itemId = null; if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", - maxWidth: width, - tag: item.ImageTags.Thumb - }); - + imgType = 'Thumb'; + imgTag = item.ImageTags.Thumb; } else if ((options.preferBanner || shape === 'banner') && item.ImageTags && item.ImageTags.Banner) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Banner", - maxWidth: width, - tag: item.ImageTags.Banner - }); - + imgType = 'Banner'; + imgTag = item.ImageTags.Banner; } else if (options.preferDisc && item.ImageTags && item.ImageTags.Disc) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Disc", - maxWidth: width, - tag: item.ImageTags.Disc - }); - + imgType = 'Disc'; + imgTag = item.ImageTags.Disc; } else if (options.preferLogo && item.ImageTags && item.ImageTags.Logo) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Logo", - maxWidth: width, - tag: item.ImageTags.Logo - }); - + imgType = 'Logo'; + imgTag = item.ImageTags.Logo; } else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) { - - imgUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, { - type: "Logo", - maxWidth: width, - tag: item.ParentLogoImageTag - }); - + imgType = 'Logo'; + imgTag = item.ParentLogoImageTag; + itemId = item.ParentLogoItemId; } else if (options.preferThumb && item.SeriesThumbImageTag && options.inheritThumb !== false) { - - imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { - type: "Thumb", - maxWidth: width, - tag: item.SeriesThumbImageTag - }); - + imgType = 'Thumb'; + imgTag = item.SeriesThumbImageTag; + itemId = item.SeriesId; } else if (options.preferThumb && item.ParentThumbItemId && options.inheritThumb !== false && item.MediaType !== 'Photo') { - - imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { - type: "Thumb", - maxWidth: width, - tag: item.ParentThumbImageTag - }); - + imgType = 'Thumb'; + imgTag = item.ParentThumbImageTag; + itemId = item.ParentThumbItemId; } else if (options.preferThumb && item.BackdropImageTags && item.BackdropImageTags.length) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Backdrop", - maxWidth: width, - tag: item.BackdropImageTags[0] - }); - + imgType = 'Backdrop'; + imgTag = item.BackdropImageTags[0]; forceName = true; - } else if (options.preferThumb && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false && item.Type === 'Episode') { - - imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { - type: "Backdrop", - maxWidth: width, - tag: item.ParentBackdropImageTags[0] - }); - + imgType = 'Backdrop'; + imgTag = item.ParentBackdropImageTags[0]; + itemId = item.ParentBackdropItemId; } else if (item.ImageTags && item.ImageTags.Primary) { - + imgType = 'Primary'; + imgTag = item.ImageTags.Primary; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Primary", - maxHeight: height, - maxWidth: width, - tag: item.ImageTags.Primary - }); - if (options.preferThumb && options.showTitle !== false) { forceName = true; } @@ -603,16 +558,11 @@ import 'programStyles'; } } else if (item.PrimaryImageTag) { - + imgType = 'Primary'; + imgTag = item.PrimaryImageTag; + itemId = item.PrimaryImageItemId; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; - imgUrl = apiClient.getScaledImageUrl(item.PrimaryImageItemId || item.Id || item.ItemId, { - type: "Primary", - maxHeight: height, - maxWidth: width, - tag: item.PrimaryImageTag - }); - if (options.preferThumb && options.showTitle !== false) { forceName = true; } @@ -624,30 +574,19 @@ import 'programStyles'; } } } else if (item.ParentPrimaryImageTag) { - - imgUrl = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, { - type: "Primary", - maxWidth: width, - tag: item.ParentPrimaryImageTag - }); + imgType = 'Primary'; + imgTag = item.ParentPrimaryImageTag; + itemId = item.ParentPrimaryImageItemId; } else if (item.SeriesPrimaryImageTag) { - - imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { - type: "Primary", - maxWidth: width, - tag: item.SeriesPrimaryImageTag - }); + imgType = 'Primary'; + imgTag = item.SeriesPrimaryImageTag; + itemId = item.SeriesId; } else if (item.AlbumId && item.AlbumPrimaryImageTag) { - + imgType = 'Primary'; + imgTag = item.AlbumPrimaryImageTag; + itemId = item.AlbumId; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; - imgUrl = apiClient.getScaledImageUrl(item.AlbumId, { - type: "Primary", - maxHeight: height, - maxWidth: width, - tag: item.AlbumPrimaryImageTag - }); - if (primaryImageAspectRatio) { uiAspect = getDesiredAspect(shape); if (uiAspect) { @@ -655,57 +594,46 @@ import 'programStyles'; } } } else if (item.Type === 'Season' && item.ImageTags && item.ImageTags.Thumb) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", - maxWidth: width, - tag: item.ImageTags.Thumb - }); - + imgType = 'Thumb'; + imgTag = item.ImageTags.Thumb; } else if (item.BackdropImageTags && item.BackdropImageTags.length) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Backdrop", - maxWidth: width, - tag: item.BackdropImageTags[0] - }); - + imgType = 'Backdrop'; + imgTag = item.BackdropImageTags[0]; } else if (item.ImageTags && item.ImageTags.Thumb) { - - imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", - maxWidth: width, - tag: item.ImageTags.Thumb - }); - + imgType = 'Thumb'; + imgTag = item.ImageTags.Thumb; } else if (item.SeriesThumbImageTag && options.inheritThumb !== false) { - - imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { - type: "Thumb", - maxWidth: width, - tag: item.SeriesThumbImageTag - }); - + imgType = 'Thumb'; + imgTag = item.SeriesThumbImageTag; + itemId = item.SeriesId; } else if (item.ParentThumbItemId && options.inheritThumb !== false) { - - imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { - type: "Thumb", - maxWidth: width, - tag: item.ParentThumbImageTag - }); - + imgType = 'Thumb'; + imgTag = item.ParentThumbImageTag; + itemId = item.ParentThumbItemId; } else if (item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false) { - - imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { - type: "Backdrop", - maxWidth: width, - tag: item.ParentBackdropImageTags[0] - }); - + imgType = 'Backdrop'; + imgTag = item.ParentBackdropImageTags[0]; + itemId = item.ParentBackdropItemId; } + if (!itemId) { + itemId = item.Id; + } + + if (imgTag && imgType) { + imgUrl = apiClient.getScaledImageUrl(itemId, { + type: imgType, + maxHeight: height, + maxWidth: width, + tag: imgTag + }); + } + + let blurHashes = options.imageBlurhashes || item.ImageBlurHashes || {}; + return { imgUrl: imgUrl, + blurhash: (blurHashes[imgType] || {})[imgTag], forceName: forceName, coverImage: coverImage }; @@ -778,7 +706,7 @@ import 'programStyles'; if (text) { html += "
"; html += text; - html += "
"; + html += ''; valid++; if (maxLines && valid >= maxLines) { @@ -835,7 +763,7 @@ import 'programStyles'; airTimeText += ' - ' + datetime.getDisplayTime(date); } } catch (e) { - console.error("error parsing date: " + item.StartDate); + console.error('error parsing date: ' + item.StartDate); } } @@ -869,11 +797,11 @@ import 'programStyles'; if (isOuterFooter && options.cardLayout && layoutManager.mobile) { if (options.cardFooterAside !== 'none') { - html += ''; + html += ''; } } - const cssClass = options.centerText ? "cardText cardTextCentered" : "cardText"; + const cssClass = options.centerText ? 'cardText cardTextCentered' : 'cardText'; const serverId = item.ServerId || options.serverId; let lines = []; @@ -907,7 +835,7 @@ import 'programStyles'; } } else { - const parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || ""; + const parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || ''; if (parentTitle || showTitle) { lines.push(parentTitle); @@ -946,7 +874,7 @@ import 'programStyles'; item.AlbumArtists[0].IsFolder = true; lines.push(getTextActionButton(item.AlbumArtists[0], null, serverId)); } else { - lines.push(isUsingLiveTvNaming(item) ? item.Name : (item.SeriesName || item.Series || item.Album || item.AlbumArtist || "")); + lines.push(isUsingLiveTvNaming(item) ? item.Name : (item.SeriesName || item.Series || item.Album || item.AlbumArtist || '')); } } @@ -993,7 +921,7 @@ import 'programStyles'; if (options.showYear || options.showSeriesYear) { if (item.Type === 'Series') { - if (item.Status === "Continuing") { + if (item.Status === 'Continuing') { lines.push(globalize.translate('SeriesYearToPresent', item.ProductionYear || '')); @@ -1105,7 +1033,7 @@ import 'programStyles'; html = '
' + html; //cardFooter - html += "
"; + html += ''; } } @@ -1191,7 +1119,7 @@ import 'programStyles'; counts.push(childText); } - } else if (item.Type === 'MusicGenre' || options.context === "MusicArtist") { + } else if (item.Type === 'MusicGenre' || options.context === 'MusicArtist') { if (item.AlbumCount) { @@ -1304,7 +1232,7 @@ import 'programStyles'; } if (options.cardClass) { - className += " " + options.cardClass; + className += ' ' + options.cardClass; } if (layoutManager.desktop) { @@ -1321,6 +1249,7 @@ import 'programStyles'; const imgInfo = getCardImageUrl(item, apiClient, options, shape); const imgUrl = imgInfo.imgUrl; + const blurhash = imgInfo.blurhash; const forceName = imgInfo.forceName; @@ -1356,13 +1285,13 @@ import 'programStyles'; if (options.showChannelLogo && item.ChannelPrimaryImageTag) { logoUrl = apiClient.getScaledImageUrl(item.ChannelId, { - type: "Primary", + type: 'Primary', height: logoHeight, tag: item.ChannelPrimaryImageTag }); } else if (options.showLogo && item.ParentLogoImageTag) { logoUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, { - type: "Logo", + type: 'Logo', height: logoHeight, tag: item.ParentLogoImageTag }); @@ -1418,15 +1347,15 @@ import 'programStyles'; const btnCssClass = 'cardOverlayButton cardOverlayButton-br itemAction'; if (options.centerPlayButton) { - overlayButtons += ''; + overlayButtons += ''; } if (overlayPlayButton && !item.IsPlaceHolder && (item.LocationType !== 'Virtual' || !item.MediaType || item.Type === 'Program') && item.Type !== 'Person') { - overlayButtons += ''; + overlayButtons += ''; } if (options.overlayMoreButton) { - overlayButtons += ''; + overlayButtons += ''; } } @@ -1445,15 +1374,20 @@ import 'programStyles'; cardContentClass += ' cardContent-shadow'; } + let blurhashAttrib = ''; + if (blurhash && blurhash.length > 0) { + blurhashAttrib = 'data-blurhash="' + blurhash + '"'; + } + if (layoutManager.tv) { // Don't use the IMG tag with safari because it puts a white border around it - cardImageContainerOpen = imgUrl ? ('
') : ('
'); + cardImageContainerOpen = imgUrl ? ('
') : ('
'); cardImageContainerClose = '
'; } else { // Don't use the IMG tag with safari because it puts a white border around it - cardImageContainerOpen = imgUrl ? (''; } @@ -1518,7 +1452,7 @@ import 'programStyles'; let actionAttribute; if (tagName === 'button') { - className += " itemAction"; + className += ' itemAction'; actionAttribute = ' data-action="' + action + '"'; } else { actionAttribute = ''; @@ -1560,7 +1494,7 @@ import 'programStyles'; const btnCssClass = 'cardOverlayButton cardOverlayButton-hover itemAction paper-icon-button-light'; if (playbackManager.canPlay(item)) { - html += ''; + html += ''; } html += '
'; @@ -1569,7 +1503,7 @@ import 'programStyles'; if (itemHelper.canMarkPlayed(item)) { require(['emby-playstatebutton']); - html += ''; + html += ''; } if (itemHelper.canRate(item)) { @@ -1577,10 +1511,10 @@ import 'programStyles'; const likes = userData.Likes == null ? '' : userData.Likes; require(['emby-ratingbutton']); - html += ''; + html += ''; } - html += ''; + html += ''; html += '
'; html += '
'; @@ -1596,27 +1530,27 @@ import 'programStyles'; */ export function getDefaultText(item, options) { if (item.CollectionType) { - return ''; + return ''; } switch (item.Type) { case 'MusicAlbum': - return 'album'; + return ''; case 'MusicArtist': case 'Person': - return 'person'; + return ''; case 'Movie': - return 'movie'; + return ''; case 'Series': - return 'tv'; + return ''; case 'Book': - return 'book'; + return ''; case 'Folder': - return 'folder'; + return ''; } if (options && options.defaultCardImageIcon) { - return '' + options.defaultCardImageIcon + ''; + return ''; } const defaultName = isUsingLiveTvNaming(item) ? item.Name : itemHelper.getDisplayName(item); @@ -1718,7 +1652,7 @@ import 'programStyles'; indicatorsElem = ensureIndicators(card, indicatorsElem); indicatorsElem.appendChild(playedIndicator); } - playedIndicator.innerHTML = 'check'; + playedIndicator.innerHTML = ''; } else { playedIndicator = card.querySelector('.playedIndicator'); @@ -1808,7 +1742,7 @@ import 'programStyles'; const icon = cell.querySelector('.timerIndicator'); if (!icon) { const indicatorsElem = ensureIndicators(cell); - indicatorsElem.insertAdjacentHTML('beforeend', ''); + indicatorsElem.insertAdjacentHTML('beforeend', ''); } cell.setAttribute('data-timerid', newTimerId); } diff --git a/src/components/cardbuilder/chaptercardbuilder.js b/src/components/cardbuilder/chaptercardbuilder.js index 16326b6c59..c6ee9ba3c5 100644 --- a/src/components/cardbuilder/chaptercardbuilder.js +++ b/src/components/cardbuilder/chaptercardbuilder.js @@ -1,13 +1,23 @@ -define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browser'], function (datetime, imageLoader, connectionManager, layoutManager, browser) { - 'use strict'; +/* eslint-disable indent */ - var enableFocusTransform = !browser.slow && !browser.edge; +/** + * Module for building cards from item data. + * @module components/cardBuilder/chaptercardbuilder + */ - function buildChapterCardsHtml(item, chapters, options) { +import datetime from 'datetime'; +import imageLoader from 'imageLoader'; +import connectionManager from 'connectionManager'; +import layoutManager from 'layoutManager'; +import browser from 'browser'; + + const enableFocusTransform = !browser.slow && !browser.edge; + + function buildChapterCardsHtml(item, chapters, options) { // TODO move card creation code to Card component - var className = 'card itemAction chapterCard'; + let className = 'card itemAction chapterCard'; if (layoutManager.tv) { className += ' show-focus'; @@ -17,12 +27,12 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse } } - var mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || []; - var videoStream = mediaStreams.filter(function (i) { - return i.Type === 'Video'; + const mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || []; + const videoStream = mediaStreams.filter(({Type}) => { + return Type === 'Video'; })[0] || {}; - var shape = (options.backdropShape || 'backdrop'); + let shape = (options.backdropShape || 'backdrop'); if (videoStream.Width && videoStream.Height) { @@ -31,24 +41,24 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse } } - className += ' ' + shape + 'Card'; + className += ` ${shape}Card`; if (options.block || options.rows) { className += ' block'; } - var html = ''; - var itemsInRow = 0; + let html = ''; + let itemsInRow = 0; - var apiClient = connectionManager.getApiClient(item.ServerId); + const apiClient = connectionManager.getApiClient(item.ServerId); - for (var i = 0, length = chapters.length; i < length; i++) { + for (let i = 0, length = chapters.length; i < length; i++) { if (options.rows && itemsInRow === 0) { html += '
'; } - var chapter = chapters[i]; + const chapter = chapters[i]; html += buildChapterCard(item, apiClient, chapter, i, options, className, shape); itemsInRow++; @@ -62,50 +72,50 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse return html; } - function getImgUrl(item, chapter, index, maxWidth, apiClient) { + function getImgUrl({Id}, {ImageTag}, index, maxWidth, apiClient) { - if (chapter.ImageTag) { + if (ImageTag) { - return apiClient.getScaledImageUrl(item.Id, { + return apiClient.getScaledImageUrl(Id, { maxWidth: maxWidth * 2, - tag: chapter.ImageTag, - type: "Chapter", - index: index + tag: ImageTag, + type: 'Chapter', + index }); } return null; } - function buildChapterCard(item, apiClient, chapter, index, options, className, shape) { + function buildChapterCard(item, apiClient, chapter, index, {width, coverImage}, className, shape) { - var imgUrl = getImgUrl(item, chapter, index, options.width || 400, apiClient); + const imgUrl = getImgUrl(item, chapter, index, width || 400, apiClient); - var cardImageContainerClass = 'cardContent cardContent-shadow cardImageContainer chapterCardImageContainer'; - if (options.coverImage) { + let cardImageContainerClass = 'cardContent cardContent-shadow cardImageContainer chapterCardImageContainer'; + if (coverImage) { cardImageContainerClass += ' coveredImage'; } - var dataAttributes = ' data-action="play" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-positionticks="' + chapter.StartPositionTicks + '"'; - var cardImageContainer = imgUrl ? ('
') : ('
'); + const dataAttributes = ` data-action="play" data-isfolder="${item.IsFolder}" data-id="${item.Id}" data-serverid="${item.ServerId}" data-type="${item.Type}" data-mediatype="${item.MediaType}" data-positionticks="${chapter.StartPositionTicks}"`; + let cardImageContainer = imgUrl ? (`
`) : (`
`); if (!imgUrl) { - cardImageContainer += ''; + cardImageContainer += ''; } - var nameHtml = ''; - nameHtml += '
' + chapter.Name + '
'; - nameHtml += '
' + datetime.getDisplayRunningTime(chapter.StartPositionTicks) + '
'; + let nameHtml = ''; + nameHtml += `
${chapter.Name}
`; + nameHtml += `
${datetime.getDisplayRunningTime(chapter.StartPositionTicks)}
`; - var cardBoxCssClass = 'cardBox'; - var cardScalableClass = 'cardScalable'; + const cardBoxCssClass = 'cardBox'; + const cardScalableClass = 'cardScalable'; - var html = '
'; + const html = `
`; return html; } - function buildChapterCards(item, chapters, options) { + export function buildChapterCards(item, chapters, options) { if (options.parentContainer) { // Abort if the container has been disposed @@ -121,15 +131,16 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse } } - var html = buildChapterCardsHtml(item, chapters, options); + const html = buildChapterCardsHtml(item, chapters, options); options.itemsContainer.innerHTML = html; imageLoader.lazyChildren(options.itemsContainer); } - return { - buildChapterCards: buildChapterCards - }; +/* eslint-enable indent */ + +export default { + buildChapterCards: buildChapterCards +}; -}); diff --git a/src/components/cardbuilder/peoplecardbuilder.js b/src/components/cardbuilder/peoplecardbuilder.js index 5d34d29e6e..3b9a26a704 100644 --- a/src/components/cardbuilder/peoplecardbuilder.js +++ b/src/components/cardbuilder/peoplecardbuilder.js @@ -1,7 +1,13 @@ -define(['cardBuilder'], function (cardBuilder) { - 'use strict'; +/* eslint-disable indent */ - function buildPeopleCards(items, options) { +/** + * Module for building cards from item data. + * @module components/cardBuilder/peoplecardbuilder + */ + +import cardBuilder from 'cardBuilder'; + + export function buildPeopleCards(items, options) { options = Object.assign(options || {}, { cardLayout: false, @@ -15,8 +21,8 @@ define(['cardBuilder'], function (cardBuilder) { cardBuilder.buildCards(items, options); } - return { - buildPeopleCards: buildPeopleCards - }; + /* eslint-enable indent */ -}); +export default { + buildPeopleCards: buildPeopleCards +}; diff --git a/src/components/castSenderApi.js b/src/components/castSenderApi.js index a1e7bd8755..b541c1f87a 100644 --- a/src/components/castSenderApi.js +++ b/src/components/castSenderApi.js @@ -1,7 +1,7 @@ define([], function() { 'use strict'; - if (window.appMode === "cordova" || window.appMode === "android") { + if (window.appMode === 'cordova' || window.appMode === 'android') { return { load: function () { window.chrome = window.chrome || {}; @@ -17,16 +17,16 @@ define([], function() { } return new Promise(function (resolve, reject) { - var fileref = document.createElement("script"); - fileref.setAttribute("type", "text/javascript"); + var fileref = document.createElement('script'); + fileref.setAttribute('type', 'text/javascript'); fileref.onload = function () { ccLoaded = true; resolve(); }; - fileref.setAttribute("src", "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"); - document.querySelector("head").appendChild(fileref); + fileref.setAttribute('src', 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js'); + document.querySelector('head').appendChild(fileref); }); } }; diff --git a/src/components/channelmapper/channelmapper.js b/src/components/channelMapper/channelMapper.js similarity index 61% rename from src/components/channelmapper/channelmapper.js rename to src/components/channelMapper/channelMapper.js index 2ea7a3a13a..f2ad88e713 100644 --- a/src/components/channelmapper/channelmapper.js +++ b/src/components/channelMapper/channelMapper.js @@ -1,33 +1,33 @@ -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"; +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 mapChannel(button, channelId, providerChannelId) { loading.show(); var providerId = options.providerId; connectionManager.getApiClient(options.serverId).ajax({ - type: "POST", - url: ApiClient.getUrl("LiveTv/ChannelMappings"), + type: 'POST', + url: ApiClient.getUrl('LiveTv/ChannelMappings'), data: { providerId: providerId, tunerChannelId: channelId, providerChannelId: providerChannelId }, - dataType: "json" + dataType: 'json' }).then(function (mapping) { - var listItem = dom.parentWithClass(button, "listItem"); - button.setAttribute("data-providerid", mapping.ProviderChannelId); - listItem.querySelector(".secondary").innerHTML = getMappingSecondaryName(mapping, currentMappingOptions.ProviderName); + var listItem = dom.parentWithClass(button, 'listItem'); + button.setAttribute('data-providerid', mapping.ProviderChannelId); + listItem.querySelector('.secondary').innerHTML = getMappingSecondaryName(mapping, currentMappingOptions.ProviderName); loading.hide(); }); } function onChannelsElementClick(e) { - var btnMap = dom.parentWithClass(e.target, "btnMap"); + var btnMap = dom.parentWithClass(e.target, 'btnMap'); if (btnMap) { - var channelId = btnMap.getAttribute("data-id"); - var providerChannelId = btnMap.getAttribute("data-providerid"); + var channelId = btnMap.getAttribute('data-id'); + var providerChannelId = btnMap.getAttribute('data-providerid'); var menuItems = currentMappingOptions.ProviderChannels.map(function (m) { return { name: m.Name, @@ -48,56 +48,56 @@ define(["dom", "dialogHelper", "loading", "connectionManager", "globalize", "act function getChannelMappingOptions(serverId, providerId) { var apiClient = connectionManager.getApiClient(serverId); - return apiClient.getJSON(apiClient.getUrl("LiveTv/ChannelMappingOptions", { + return apiClient.getJSON(apiClient.getUrl('LiveTv/ChannelMappingOptions', { providerId: providerId })); } function getMappingSecondaryName(mapping, providerName) { - return (mapping.ProviderChannelName || "") + " - " + providerName; + return (mapping.ProviderChannelName || '') + ' - ' + providerName; } function getTunerChannelHtml(channel, providerName) { - var html = ""; + var html = ''; html += '
'; - html += 'dvr'; + html += ''; html += '
'; html += '

'; html += channel.Name; - html += "

"; + html += ''; html += '
'; if (channel.ProviderChannelName) { html += getMappingSecondaryName(channel, providerName); } - html += "
"; - html += "
"; - html += ''; - return html += "
"; + html += '
'; + html += '
'; + html += ''; + return html += '
'; } function getEditorHtml() { - var html = ""; - html += '
'; + var html = ''; + html += '
'; html += '
'; html += ''; - html += "

" + globalize.translate("HeaderChannels") + "

"; + html += '

' + globalize.translate('HeaderChannels') + '

'; html += '
'; - html += "
"; - html += ""; - html += "
"; - return html += "
"; + html += '
'; + html += ''; + html += '
'; + return html += '
'; } function initEditor(dlg, options) { getChannelMappingOptions(options.serverId, options.providerId).then(function (result) { currentMappingOptions = result; - var channelsElement = dlg.querySelector(".channels"); + var channelsElement = dlg.querySelector('.channels'); channelsElement.innerHTML = result.TunerChannels.map(function (channel) { return getTunerChannelHtml(channel, result.ProviderName); - }).join(""); - channelsElement.addEventListener("click", onChannelsElementClick); + }).join(''); + channelsElement.addEventListener('click', onChannelsElementClick); }); } @@ -108,27 +108,27 @@ define(["dom", "dialogHelper", "loading", "connectionManager", "globalize", "act var dialogOptions = { removeOnClose: true }; - dialogOptions.size = "small"; + dialogOptions.size = 'small'; var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - dlg.classList.add("ui-body-a"); - dlg.classList.add("background-theme-a"); - var html = ""; - var title = globalize.translate("MapChannels"); + dlg.classList.add('formDialog'); + dlg.classList.add('ui-body-a'); + dlg.classList.add('background-theme-a'); + var html = ''; + var title = globalize.translate('MapChannels'); html += '
'; - html += ''; + html += ''; html += '

'; html += title; - html += "

"; - html += "
"; + html += ''; + html += ''; html += getEditorHtml(); dlg.innerHTML = html; initEditor(dlg, options); - dlg.querySelector(".btnCancel").addEventListener("click", function () { + dlg.querySelector('.btnCancel').addEventListener('click', function () { dialogHelper.close(dlg); }); return new Promise(function (resolve, reject) { - dlg.addEventListener("close", resolve); + dlg.addEventListener('close', resolve); dialogHelper.open(dlg); }); }; diff --git a/src/components/collectioneditor/collectioneditor.js b/src/components/collectionEditor/collectionEditor.js similarity index 94% rename from src/components/collectioneditor/collectioneditor.js rename to src/components/collectionEditor/collectionEditor.js index 49784df498..46b0640305 100644 --- a/src/components/collectioneditor/collectioneditor.js +++ b/src/components/collectionEditor/collectionEditor.js @@ -24,7 +24,7 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio function createCollection(apiClient, dlg) { - var url = apiClient.getUrl("Collections", { + var url = apiClient.getUrl('Collections', { Name: dlg.querySelector('#txtNewCollectionName').value, IsLocked: !dlg.querySelector('#chkEnableInternetMetadata').checked, @@ -32,9 +32,9 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio }); apiClient.ajax({ - type: "POST", + type: 'POST', url: url, - dataType: "json" + dataType: 'json' }).then(function (result) { @@ -56,13 +56,13 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio function addToCollection(apiClient, dlg, id) { - var url = apiClient.getUrl("Collections/" + id + "/Items", { + var url = apiClient.getUrl('Collections/' + id + '/Items', { Ids: dlg.querySelector('.fldSelectedItemIds').value || '' }); apiClient.ajax({ - type: "POST", + type: 'POST', url: url }).then(function () { @@ -93,8 +93,8 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio var options = { Recursive: true, - IncludeItemTypes: "BoxSet", - SortBy: "SortName", + IncludeItemTypes: 'BoxSet', + SortBy: 'SortName', EnableTotalRecordCount: false }; @@ -230,13 +230,13 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio var title = items.length ? globalize.translate('HeaderAddToCollection') : globalize.translate('NewCollection'); html += '
'; - html += ''; + html += ''; html += '

'; html += title; html += '

'; if (appHost.supports('externallinks')) { - html += 'info' + globalize.translate('Help') + ''; + html += '' + globalize.translate('Help') + ''; } html += '
'; diff --git a/src/components/confirm/confirm.js b/src/components/confirm/confirm.js index fa9a156679..517d6fa9be 100644 --- a/src/components/confirm/confirm.js +++ b/src/components/confirm/confirm.js @@ -1,4 +1,4 @@ -define(["browser", "dialog", "globalize"], function(browser, dialog, globalize) { +define(['browser', 'dialog', 'globalize'], function(browser, dialog, globalize) { 'use strict'; function replaceAll(str, find, replace) { diff --git a/src/components/deletehelper.js b/src/components/deletehelper.js deleted file mode 100644 index 2212fd4437..0000000000 --- a/src/components/deletehelper.js +++ /dev/null @@ -1,57 +0,0 @@ -define(['connectionManager', 'confirm', 'appRouter', 'globalize'], function (connectionManager, confirm, appRouter, globalize) { - 'use strict'; - - function alertText(options) { - - return new Promise(function (resolve, reject) { - - require(['alert'], function (alert) { - alert(options).then(resolve, resolve); - }); - }); - } - - function deleteItem(options) { - - var item = options.item; - var itemId = item.Id; - var parentId = item.SeasonId || item.SeriesId || item.ParentId; - var serverId = item.ServerId; - - var msg = globalize.translate('ConfirmDeleteItem'); - var title = globalize.translate('HeaderDeleteItem'); - var apiClient = connectionManager.getApiClient(item.ServerId); - - return confirm({ - - title: title, - text: msg, - confirmText: globalize.translate('Delete'), - primary: 'delete' - - }).then(function () { - - return apiClient.deleteItem(itemId).then(function () { - - if (options.navigate) { - if (parentId) { - appRouter.showItem(parentId, serverId); - } else { - appRouter.goHome(); - } - } - }, function (err) { - - var result = function () { - return Promise.reject(err); - }; - - return alertText(globalize.translate('ErrorDeletingItem')).then(result, result); - }); - }); - } - - return { - deleteItem: deleteItem - }; -}); diff --git a/src/components/dialogHelper/dialogHelper.js b/src/components/dialogHelper/dialogHelper.js index e3410776a8..8b08cde678 100644 --- a/src/components/dialogHelper/dialogHelper.js +++ b/src/components/dialogHelper/dialogHelper.js @@ -141,7 +141,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager', animateDialogOpen(dlg); if (isHistoryEnabled(dlg)) { - appRouter.pushState({ dialogId: hash }, "Dialog", '#' + hash); + appRouter.pushState({ dialogId: hash }, 'Dialog', '#' + hash); window.addEventListener('popstate', onHashChange); } else { diff --git a/src/components/dialogHelper/dialoghelper.css b/src/components/dialogHelper/dialoghelper.css index df2adf075f..98cfef1c5d 100644 --- a/src/components/dialogHelper/dialoghelper.css +++ b/src/components/dialogHelper/dialoghelper.css @@ -126,25 +126,10 @@ } @media all and (min-width: 80em) and (min-height: 45em) { - .dialog-medium { - width: 80%; - height: 80%; - } - - .dialog-medium-tall { - width: 80%; - height: 90%; - } - .dialog-small { width: 60%; height: 80%; } - - .dialog-fullscreen-border { - width: 90%; - height: 90%; - } } .noScroll { diff --git a/src/components/directorybrowser/directorybrowser.js b/src/components/directorybrowser/directorybrowser.js index fc976068e7..e08fcc8336 100644 --- a/src/components/directorybrowser/directorybrowser.js +++ b/src/components/directorybrowser/directorybrowser.js @@ -1,4 +1,4 @@ -define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-icon-button-light', 'css!./directorybrowser', 'formDialogStyle', 'emby-button'], function(loading, dialogHelper, dom) { +define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-input', 'paper-icon-button-light', 'css!./directorybrowser', 'formDialogStyle', 'emby-button'], function(loading, dialogHelper, dom, globalize) { 'use strict'; function getSystemInfo() { @@ -16,14 +16,14 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper- function refreshDirectoryBrowser(page, path, fileOptions, updatePathOnError) { if (path && typeof path !== 'string') { - throw new Error("invalid path"); + throw new Error('invalid path'); } loading.show(); var promises = []; - if ("Network" === path) { + if ('Network' === path) { promises.push(ApiClient.getNetworkDevices()); } else { if (path) { @@ -37,31 +37,31 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper- Promise.all(promises).then( function(responses) { var folders = responses[0]; - var parentPath = responses[1] || ""; - var html = ""; + var parentPath = responses[1] || ''; + var html = ''; - page.querySelector(".results").scrollTop = 0; - page.querySelector("#txtDirectoryPickerPath").value = path || ""; + page.querySelector('.results').scrollTop = 0; + page.querySelector('#txtDirectoryPickerPath').value = path || ''; if (path) { - html += getItem("lnkPath lnkDirectory", "", parentPath, "..."); + html += getItem('lnkPath lnkDirectory', '', parentPath, '...'); } for (var i = 0, length = folders.length; i < length; i++) { var folder = folders[i]; - var cssClass = "File" === folder.Type ? "lnkPath lnkFile" : "lnkPath lnkDirectory"; + var cssClass = 'File' === folder.Type ? 'lnkPath lnkFile' : 'lnkPath lnkDirectory'; html += getItem(cssClass, folder.Type, folder.Path, folder.Name); } if (!path) { - html += getItem("lnkPath lnkDirectory", "", "Network", Globalize.translate("ButtonNetwork")); + html += getItem('lnkPath lnkDirectory', '', 'Network', globalize.translate('ButtonNetwork')); } - page.querySelector(".results").innerHTML = html; + page.querySelector('.results').innerHTML = html; loading.hide(); }, function() { if (updatePathOnError) { - page.querySelector("#txtDirectoryPickerPath").value = ""; - page.querySelector(".results").innerHTML = ""; + page.querySelector('#txtDirectoryPickerPath').value = ''; + page.querySelector('.results').innerHTML = ''; loading.hide(); } } @@ -69,74 +69,73 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper- } function getItem(cssClass, type, path, name) { - var html = ""; + var html = ''; html += '
'; html += '
'; html += '
'; html += name; - html += "
"; - html += "
"; - html += ''; - html += "
"; + html += ''; + html += ''; + html += ''; + html += ''; return html; } function getEditorHtml(options, systemInfo) { - var html = ""; + var html = ''; html += '
'; html += '
'; if (!options.pathReadOnly) { - var instruction = options.instruction ? options.instruction + "

" : ""; + var instruction = options.instruction ? options.instruction + '

' : ''; html += '
'; html += instruction; - html += Globalize.translate("MessageDirectoryPickerInstruction", "\\\\server", "\\\\192.168.1.101"); - if ("bsd" === systemInfo.OperatingSystem.toLowerCase()) { - html += "
"; - html += "
"; - html += Globalize.translate("MessageDirectoryPickerBSDInstruction"); - html += "
"; - } else if ("linux" === systemInfo.OperatingSystem.toLowerCase()) { - html += "
"; - html += "
"; - html += Globalize.translate("MessageDirectoryPickerLinuxInstruction"); - html += "
"; + if ('bsd' === systemInfo.OperatingSystem.toLowerCase()) { + html += '
'; + html += '
'; + html += globalize.translate('MessageDirectoryPickerBSDInstruction'); + html += '
'; + } else if ('linux' === systemInfo.OperatingSystem.toLowerCase()) { + html += '
'; + html += '
'; + html += globalize.translate('MessageDirectoryPickerLinuxInstruction'); + html += '
'; } - html += "
"; + html += '
'; } html += '
'; html += '
'; html += '
'; var labelKey; if (options.includeFiles !== true) { - labelKey = "LabelFolder"; + labelKey = 'LabelFolder'; } else { - labelKey = "LabelPath"; + labelKey = 'LabelPath'; } - var readOnlyAttribute = options.pathReadOnly ? " readonly" : ""; - html += ''; - html += "
"; + var readOnlyAttribute = options.pathReadOnly ? ' readonly' : ''; + html += ''; + html += '
'; if (!readOnlyAttribute) { - html += ''; + html += ''; } - html += "
"; + html += ''; if (!readOnlyAttribute) { html += '
'; } if (options.enableNetworkSharePath) { html += '
'; - html += ''; + html += ''; html += '
'; - html += Globalize.translate("LabelOptionalNetworkPathHelp"); - html += "
"; - html += "
"; + html += globalize.translate('LabelOptionalNetworkPathHelp', '\\\\server', '\\\\192.168.1.101'); + html += ''; + html += ''; } html += '
'; - html += '"; - html += "
"; - html += ""; - html += ""; - html += ""; - html += ""; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; return html; } @@ -148,15 +147,15 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper- } function alertTextWithOptions(options) { - require(["alert"], function(alert) { + require(['alert'], function(alert) { alert(options); }); } function validatePath(path, validateWriteable, apiClient) { return apiClient.ajax({ - type: "POST", - url: apiClient.getUrl("Environment/ValidatePath"), + type: 'POST', + url: apiClient.getUrl('Environment/ValidatePath'), data: { ValidateWriteable: validateWriteable, Path: path @@ -164,14 +163,14 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper- }).catch(function(response) { if (response) { if (response.status === 404) { - alertText(Globalize.translate("PathNotFound")); + alertText(globalize.translate('PathNotFound')); return Promise.reject(); } if (response.status === 500) { if (validateWriteable) { - alertText(Globalize.translate("WriteAccessRequired")); + alertText(globalize.translate('WriteAccessRequired')); } else { - alertText(Globalize.translate("PathNotFound")); + alertText(globalize.translate('PathNotFound')); } return Promise.reject(); } @@ -181,37 +180,37 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper- } function initEditor(content, options, fileOptions) { - content.addEventListener("click", function(e) { - var lnkPath = dom.parentWithClass(e.target, "lnkPath"); + content.addEventListener('click', function(e) { + var lnkPath = dom.parentWithClass(e.target, 'lnkPath'); if (lnkPath) { - var path = lnkPath.getAttribute("data-path"); - if (lnkPath.classList.contains("lnkFile")) { - content.querySelector("#txtDirectoryPickerPath").value = path; + var path = lnkPath.getAttribute('data-path'); + if (lnkPath.classList.contains('lnkFile')) { + content.querySelector('#txtDirectoryPickerPath').value = path; } else { refreshDirectoryBrowser(content, path, fileOptions, true); } } }); - content.addEventListener("click", function(e) { - if (dom.parentWithClass(e.target, "btnRefreshDirectories")) { - var path = content.querySelector("#txtDirectoryPickerPath").value; + content.addEventListener('click', function(e) { + if (dom.parentWithClass(e.target, 'btnRefreshDirectories')) { + var path = content.querySelector('#txtDirectoryPickerPath').value; refreshDirectoryBrowser(content, path, fileOptions); } }); - content.addEventListener("change", function(e) { - var txtDirectoryPickerPath = dom.parentWithTag(e.target, "INPUT"); - if (txtDirectoryPickerPath && "txtDirectoryPickerPath" === txtDirectoryPickerPath.id) { + content.addEventListener('change', function(e) { + var txtDirectoryPickerPath = dom.parentWithTag(e.target, 'INPUT'); + if (txtDirectoryPickerPath && 'txtDirectoryPickerPath' === txtDirectoryPickerPath.id) { refreshDirectoryBrowser(content, txtDirectoryPickerPath.value, fileOptions); } }); - content.querySelector("form").addEventListener("submit", function(e) { + content.querySelector('form').addEventListener('submit', function(e) { if (options.callback) { - var networkSharePath = this.querySelector("#txtNetworkPath"); + var networkSharePath = this.querySelector('#txtNetworkPath'); networkSharePath = networkSharePath ? networkSharePath.value : null; - var path = this.querySelector("#txtDirectoryPickerPath").value; + var path = this.querySelector('#txtDirectoryPickerPath').value; validatePath(path, options.validateWriteable, ApiClient).then(options.callback(path, networkSharePath)); } e.preventDefault(); @@ -224,11 +223,11 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper- if (options.path) { return Promise.resolve(options.path); } else { - return ApiClient.getJSON(ApiClient.getUrl("Environment/DefaultDirectoryBrowser")).then( + return ApiClient.getJSON(ApiClient.getUrl('Environment/DefaultDirectoryBrowser')).then( function(result) { - return result.Path || ""; + return result.Path || ''; }, function() { - return ""; + return ''; } ); } @@ -253,35 +252,35 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper- var systemInfo = responses[0]; var initialPath = responses[1]; var dlg = dialogHelper.createDialog({ - size: "medium-tall", + size: 'small', removeOnClose: true, scrollY: false }); - dlg.classList.add("ui-body-a"); - dlg.classList.add("background-theme-a"); - dlg.classList.add("directoryPicker"); - dlg.classList.add("formDialog"); + dlg.classList.add('ui-body-a'); + dlg.classList.add('background-theme-a'); + dlg.classList.add('directoryPicker'); + dlg.classList.add('formDialog'); - var html = ""; + var html = ''; html += '
'; - html += ''; + html += ''; html += '

'; - html += options.header || Globalize.translate("HeaderSelectPath"); - html += "

"; - html += "
"; + html += options.header || globalize.translate('HeaderSelectPath'); + html += ''; + html += ''; html += getEditorHtml(options, systemInfo); dlg.innerHTML = html; initEditor(dlg, options, fileOptions); - dlg.addEventListener("close", onDialogClosed); + dlg.addEventListener('close', onDialogClosed); dialogHelper.open(dlg); - dlg.querySelector(".btnCloseDialog").addEventListener("click", function() { + dlg.querySelector('.btnCloseDialog').addEventListener('click', function() { dialogHelper.close(dlg); }); currentDialog = dlg; - dlg.querySelector("#txtDirectoryPickerPath").value = initialPath; - var txtNetworkPath = dlg.querySelector("#txtNetworkPath"); + dlg.querySelector('#txtDirectoryPickerPath').value = initialPath; + var txtNetworkPath = dlg.querySelector('#txtNetworkPath'); if (txtNetworkPath) { - txtNetworkPath.value = options.networkSharePath || ""; + txtNetworkPath.value = options.networkSharePath || ''; } if (!options.pathReadOnly) { refreshDirectoryBrowser(dlg, initialPath, fileOptions, true); diff --git a/src/components/displaysettings/displaysettings.js b/src/components/displaySettings/displaySettings.js similarity index 95% rename from src/components/displaysettings/displaysettings.js rename to src/components/displaySettings/displaySettings.js index da407c11f1..c4eb35f49f 100644 --- a/src/components/displaysettings/displaysettings.js +++ b/src/components/displaySettings/displaySettings.js @@ -1,5 +1,5 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', 'apphost', 'focusManager', 'datetime', 'globalize', 'loading', 'connectionManager', 'skinManager', 'dom', 'events', 'emby-select', 'emby-checkbox', 'emby-button'], function (require, browser, layoutManager, appSettings, pluginManager, appHost, focusManager, datetime, globalize, loading, connectionManager, skinManager, dom, events) { - "use strict"; + 'use strict'; function fillThemes(select, isDashboard) { select.innerHTML = skinManager.getThemes().map(function (t) { @@ -181,11 +181,15 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', ' context.querySelector('#chkThemeSong').checked = userSettings.enableThemeSongs(); context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos(); context.querySelector('#chkFadein').checked = userSettings.enableFastFadein(); + context.querySelector('#chkBlurhash').checked = userSettings.enableBlurhash(); context.querySelector('#chkBackdrops').checked = userSettings.enableBackdrops(); + context.querySelector('#chkDetailsBanner').checked = userSettings.detailsBanner(); context.querySelector('#selectLanguage').value = userSettings.language() || ''; context.querySelector('.selectDateTimeLocale').value = userSettings.dateTimeLocale() || ''; + context.querySelector('#txtLibraryPageSize').value = userSettings.libraryPageSize(); + selectDashboardTheme.value = userSettings.dashboardTheme() || ''; selectTheme.value = userSettings.theme() || ''; @@ -215,10 +219,14 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', ' userSettingsInstance.soundEffects(context.querySelector('.selectSoundEffects').value); userSettingsInstance.screensaver(context.querySelector('.selectScreensaver').value); + userSettingsInstance.libraryPageSize(context.querySelector('#txtLibraryPageSize').value); + userSettingsInstance.skin(context.querySelector('.selectSkin').value); userSettingsInstance.enableFastFadein(context.querySelector('#chkFadein').checked); + userSettingsInstance.enableBlurhash(context.querySelector('#chkBlurhash').checked); userSettingsInstance.enableBackdrops(context.querySelector('#chkBackdrops').checked); + userSettingsInstance.detailsBanner(context.querySelector('#chkDetailsBanner').checked); if (user.Id === apiClient.getCurrentUserId()) { skinManager.setTheme(userSettingsInstance.theme()); @@ -265,7 +273,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', ' } function embed(options, self) { - require(['text!./displaysettings.template.html'], function (template) { + require(['text!./displaySettings.template.html'], function (template) { options.element.innerHTML = globalize.translateDocument(template, 'core'); options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self)); if (options.enableSaveButton) { diff --git a/src/components/displaysettings/displaysettings.template.html b/src/components/displaySettings/displaySettings.template.html similarity index 87% rename from src/components/displaysettings/displaysettings.template.html rename to src/components/displaySettings/displaySettings.template.html index 4ef8c8b1ca..ab01b4b6ae 100644 --- a/src/components/displaysettings/displaysettings.template.html +++ b/src/components/displaySettings/displaySettings.template.html @@ -56,7 +56,7 @@ @@ -143,12 +143,33 @@ -
+
+ +
${LabelLibraryPageSizeHelp}
+
+ +
-
${EnableFastImageFadeInHelp}
+
${EnableFasterAnimationsHelp}
+
+ +
+ +
${EnableBlurhashHelp}
+
+ +
+ +
${EnableDetailsBannerHelp}
diff --git a/src/components/favoriteitems.js b/src/components/favoriteitems.js index 08c9d9acc5..38715043e7 100644 --- a/src/components/favoriteitems.js +++ b/src/components/favoriteitems.js @@ -1,41 +1,41 @@ -define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoader", "globalize", "layoutManager", "scrollStyles", "emby-itemscontainer"], function (loading, libraryBrowser, cardBuilder, dom, appHost, imageLoader, globalize, layoutManager) { - "use strict"; +define(['loading', 'libraryBrowser', 'cardBuilder', 'dom', 'apphost', 'imageLoader', 'globalize', 'layoutManager', 'scrollStyles', 'emby-itemscontainer'], function (loading, libraryBrowser, cardBuilder, dom, appHost, imageLoader, globalize, layoutManager) { + 'use strict'; function enableScrollX() { return !layoutManager.desktop; } function getThumbShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop"; + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function getPosterShape() { - return enableScrollX() ? "overflowPortrait" : "portrait"; + return enableScrollX() ? 'overflowPortrait' : 'portrait'; } function getSquareShape() { - return enableScrollX() ? "overflowSquare" : "square"; + return enableScrollX() ? 'overflowSquare' : 'square'; } function getSections() { return [{ - name: "HeaderFavoriteMovies", - types: "Movie", - id: "favoriteMovies", + name: 'HeaderFavoriteMovies', + types: 'Movie', + id: 'favoriteMovies', shape: getPosterShape(), showTitle: false, overlayPlayButton: true }, { - name: "HeaderFavoriteShows", - types: "Series", - id: "favoriteShows", + name: 'HeaderFavoriteShows', + types: 'Series', + id: 'favoriteShows', shape: getPosterShape(), showTitle: false, overlayPlayButton: true }, { - name: "HeaderFavoriteEpisodes", - types: "Episode", - id: "favoriteEpisode", + name: 'HeaderFavoriteEpisodes', + types: 'Episode', + id: 'favoriteEpisode', shape: getThumbShape(), preferThumb: false, showTitle: true, @@ -44,9 +44,9 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad overlayText: false, centerText: true }, { - name: "HeaderFavoriteVideos", - types: "Video,MusicVideo", - id: "favoriteVideos", + name: 'HeaderFavoriteVideos', + types: 'Video,MusicVideo', + id: 'favoriteVideos', shape: getThumbShape(), preferThumb: true, showTitle: true, @@ -54,9 +54,9 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad overlayText: false, centerText: true }, { - name: "HeaderFavoriteArtists", - types: "MusicArtist", - id: "favoriteArtists", + name: 'HeaderFavoriteArtists', + types: 'MusicArtist', + id: 'favoriteArtists', shape: getSquareShape(), preferThumb: false, showTitle: true, @@ -66,9 +66,9 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad overlayPlayButton: true, coverImage: true }, { - name: "HeaderFavoriteAlbums", - types: "MusicAlbum", - id: "favoriteAlbums", + name: 'HeaderFavoriteAlbums', + types: 'MusicAlbum', + id: 'favoriteAlbums', shape: getSquareShape(), preferThumb: false, showTitle: true, @@ -78,9 +78,9 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad overlayPlayButton: true, coverImage: true }, { - name: "HeaderFavoriteSongs", - types: "Audio", - id: "favoriteSongs", + name: 'HeaderFavoriteSongs', + types: 'Audio', + id: 'favoriteSongs', shape: getSquareShape(), preferThumb: false, showTitle: true, @@ -88,7 +88,7 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad showParentTitle: true, centerText: true, overlayMoreButton: true, - action: "instantmix", + action: 'instantmix', coverImage: true }]; } @@ -96,13 +96,13 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad function loadSection(elem, userId, topParentId, section, isSingleSection) { var screenWidth = dom.getWindowSize().innerWidth; var options = { - SortBy: "SortName", - SortOrder: "Ascending", - Filters: "IsFavorite", + SortBy: 'SortName', + SortOrder: 'Ascending', + Filters: 'IsFavorite', Recursive: true, - Fields: "PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo', CollapseBoxSetItems: false, - ExcludeLocationTypes: "Virtual", + ExcludeLocationTypes: 'Virtual', EnableTotalRecordCount: false }; @@ -120,7 +120,7 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad var promise; - if ("MusicArtist" === section.types) { + if ('MusicArtist' === section.types) { promise = ApiClient.getArtists(userId, options); } else { options.IncludeItemTypes = section.types; @@ -128,25 +128,25 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad } return promise.then(function (result) { - var html = ""; + var html = ''; if (result.Items.length) { if (html += '
', !layoutManager.tv && options.Limit && result.Items.length >= options.Limit) { - html += ''; + html += ''; html += '

'; html += globalize.translate(section.name); - html += "

"; - html += ''; - html += "
"; + html += ''; + html += ''; + html += ''; } else { - html += '

' + globalize.translate(section.name) + "

"; + html += '

' + globalize.translate(section.name) + '

'; } - html += "
"; + html += '
'; if (enableScrollX()) { - var scrollXClass = "scrollX hiddenScrollX"; + var scrollXClass = 'scrollX hiddenScrollX'; if (layoutManager.tv) { - scrollXClass += " smoothScrollX"; + scrollXClass += ' smoothScrollX'; } html += '
'; @@ -154,7 +154,7 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad html += '
'; } - var supportsImageAnalysis = appHost.supports("imageanalysis"); + var supportsImageAnalysis = appHost.supports('imageanalysis'); var cardLayout = (appHost.preferVisualCards || supportsImageAnalysis) && section.autoCardLayout && section.showTitle; cardLayout = false; html += cardBuilder.getCardsHtml(result.Items, { @@ -172,7 +172,7 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad allowBottomPadding: !enableScrollX(), cardLayout: cardLayout }); - html += "
"; + html += '
'; } elem.innerHTML = html; @@ -183,7 +183,7 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad function loadSections(page, userId, topParentId, types) { loading.show(); var sections = getSections(); - var sectionid = getParameterByName("sectionid"); + var sectionid = getParameterByName('sectionid'); if (sectionid) { sections = sections.filter(function (s) { @@ -199,10 +199,10 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad var i; var length; - var elem = page.querySelector(".favoriteSections"); + var elem = page.querySelector('.favoriteSections'); if (!elem.innerHTML) { - var html = ""; + var html = ''; for (i = 0, length = sections.length; i < length; i++) { html += '
'; @@ -215,7 +215,7 @@ define(["loading", "libraryBrowser", "cardBuilder", "dom", "apphost", "imageLoad for (i = 0, length = sections.length; i < length; i++) { var section = sections[i]; - elem = page.querySelector(".section" + section.id); + elem = page.querySelector('.section' + section.id); promises.push(loadSection(elem, userId, topParentId, section, 1 === sections.length)); } diff --git a/src/components/fetchhelper.js b/src/components/fetchhelper.js index 0b8d451736..f626abefee 100644 --- a/src/components/fetchhelper.js +++ b/src/components/fetchhelper.js @@ -1,21 +1,19 @@ -define([], function () { - 'use strict'; +/* eslint-disable indent */ + export function getFetchPromise(request) { - function getFetchPromise(request) { - - var headers = request.headers || {}; + const headers = request.headers || {}; if (request.dataType === 'json') { headers.accept = 'application/json'; } - var fetchRequest = { + const fetchRequest = { headers: headers, method: request.type, credentials: 'same-origin' }; - var contentType = request.contentType; + let contentType = request.contentType; if (request.data) { @@ -33,12 +31,12 @@ define([], function () { headers['Content-Type'] = contentType; } - var url = request.url; + let url = request.url; if (request.query) { - var paramString = paramsToString(request.query); + const paramString = paramsToString(request.query); if (paramString) { - url += '?' + paramString; + url += `?${paramString}`; } } @@ -51,11 +49,11 @@ define([], function () { function fetchWithTimeout(url, options, timeoutMs) { - console.debug('fetchWithTimeout: timeoutMs: ' + timeoutMs + ', url: ' + url); + console.debug(`fetchWithTimeout: timeoutMs: ${timeoutMs}, url: ${url}`); return new Promise(function (resolve, reject) { - var timeout = setTimeout(reject, timeoutMs); + const timeout = setTimeout(reject, timeoutMs); options = options || {}; options.credentials = 'same-origin'; @@ -63,50 +61,47 @@ define([], function () { fetch(url, options).then(function (response) { clearTimeout(timeout); - console.debug('fetchWithTimeout: succeeded connecting to url: ' + url); + console.debug(`fetchWithTimeout: succeeded connecting to url: ${url}`); resolve(response); }, function (error) { clearTimeout(timeout); - console.debug('fetchWithTimeout: timed out connecting to url: ' + url); + console.debug(`fetchWithTimeout: timed out connecting to url: ${url}`); - reject(); + reject(error); }); }); } + /** + * @param params {Record} + * @returns {string} Query string + */ function paramsToString(params) { - - var values = []; - - for (var key in params) { - - var value = params[key]; - - if (value !== null && value !== undefined && value !== '') { - values.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)); - } - } - return values.join('&'); + return Object.entries(params) + // eslint-disable-next-line no-unused-vars + .filter(([_, v]) => v !== null && v !== undefined && v !== '') + .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`) + .join('&'); } - function ajax(request) { + export function ajax(request) { if (!request) { - throw new Error("Request cannot be null"); + throw new Error('Request cannot be null'); } request.headers = request.headers || {}; - console.debug('requesting url: ' + request.url); + console.debug(`requesting url: ${request.url}`); return getFetchPromise(request).then(function (response) { - console.debug('response status: ' + response.status + ', url: ' + request.url); + console.debug(`response status: ${response.status}, url: ${request.url}`); if (response.status < 400) { if (request.dataType === 'json' || request.headers.accept === 'application/json') { return response.json(); - } else if (request.dataType === 'text' || (response.headers.get('Content-Type') || '').toLowerCase().indexOf('text/') === 0) { + } else if (request.dataType === 'text' || (response.headers.get('Content-Type') || '').toLowerCase().startsWith('text/')) { return response.text(); } else { return response; @@ -115,12 +110,8 @@ define([], function () { return Promise.reject(response); } }, function (err) { - console.error('request failed to url: ' + request.url); + console.error(`request failed to url: ${request.url}`); throw err; }); } - return { - getFetchPromise: getFetchPromise, - ajax: ajax - }; -}); +/* eslint-enable indent */ diff --git a/src/components/filterdialog/filterdialog.js b/src/components/filterdialog/filterdialog.js index cbbb1919b0..041fa82e76 100644 --- a/src/components/filterdialog/filterdialog.js +++ b/src/components/filterdialog/filterdialog.js @@ -1,52 +1,59 @@ -define(["dom", "dialogHelper", "globalize", "connectionManager", "events", "browser", "require", "emby-checkbox", "emby-collapse", "css!./style"], function (dom, dialogHelper, globalize, connectionManager, events, browser, require) { - "use strict"; +import dom from 'dom'; +import dialogHelper from 'dialogHelper'; +import globalize from 'globalize'; +import connectionManager from 'connectionManager'; +import events from 'events'; +import 'emby-checkbox'; +import 'emby-collapse'; +import 'css!./style.css'; +/* eslint-disable indent */ function renderOptions(context, selector, cssClass, items, isCheckedFn) { - var elem = context.querySelector(selector); + const elem = context.querySelector(selector); if (items.length) { - elem.classList.remove("hide"); + elem.classList.remove('hide'); } else { - elem.classList.add("hide"); + elem.classList.add('hide'); } - var html = ""; + let html = ''; html += '
'; html += items.map(function (filter) { - var itemHtml = ""; - var checkedHtml = isCheckedFn(filter) ? " checked" : ""; - itemHtml += ""; + let itemHtml = ''; + const checkedHtml = isCheckedFn(filter) ? 'checked' : ''; + itemHtml += ''; return itemHtml; - }).join(""); - html += "
"; - elem.querySelector(".filterOptions").innerHTML = html; + }).join(''); + html += '
'; + elem.querySelector('.filterOptions').innerHTML = html; } function renderFilters(context, result, query) { if (result.Tags) { result.Tags.length = Math.min(result.Tags.length, 50); } - renderOptions(context, ".genreFilters", "chkGenreFilter", result.Genres, function (i) { - var delimeter = "|"; - return (delimeter + (query.Genres || "") + delimeter).indexOf(delimeter + i + delimeter) != -1; + renderOptions(context, '.genreFilters', 'chkGenreFilter', result.Genres, function (i) { + const delimeter = '|'; + return (delimeter + (query.Genres || '') + delimeter).includes(delimeter + i + delimeter); }); - renderOptions(context, ".officialRatingFilters", "chkOfficialRatingFilter", result.OfficialRatings, function (i) { - var delimeter = "|"; - return (delimeter + (query.OfficialRatings || "") + delimeter).indexOf(delimeter + i + delimeter) != -1; + renderOptions(context, '.officialRatingFilters', 'chkOfficialRatingFilter', result.OfficialRatings, function (i) { + const delimeter = '|'; + return (delimeter + (query.OfficialRatings || '') + delimeter).includes(delimeter + i + delimeter); }); - renderOptions(context, ".tagFilters", "chkTagFilter", result.Tags, function (i) { - var delimeter = "|"; - return (delimeter + (query.Tags || "") + delimeter).indexOf(delimeter + i + delimeter) != -1; + renderOptions(context, '.tagFilters', 'chkTagFilter', result.Tags, function (i) { + const delimeter = '|'; + return (delimeter + (query.Tags || '') + delimeter).includes(delimeter + i + delimeter); }); - renderOptions(context, ".yearFilters", "chkYearFilter", result.Years, function (i) { - var delimeter = ","; - return (delimeter + (query.Years || "") + delimeter).indexOf(delimeter + i + delimeter) != -1; + renderOptions(context, '.yearFilters', 'chkYearFilter', result.Years, function (i) { + const delimeter = ','; + return (delimeter + (query.Years || '') + delimeter).includes(delimeter + i + delimeter); }); } function loadDynamicFilters(context, apiClient, userId, itemQuery) { - return apiClient.getJSON(apiClient.getUrl("Items/Filters", { + return apiClient.getJSON(apiClient.getUrl('Items/Filters', { UserId: userId, ParentId: itemQuery.ParentId, IncludeItemTypes: itemQuery.IncludeItemTypes @@ -55,346 +62,372 @@ define(["dom", "dialogHelper", "globalize", "connectionManager", "events", "brow }); } + /** + * @param context {HTMLDivElement} Dialog + * @param options {any} Options + */ function updateFilterControls(context, options) { - var elems; - var i; - var length; - var query = options.query; + const query = options.query; - if (options.mode == "livetvchannels") { - context.querySelector(".chkFavorite").checked = query.IsFavorite == true; - context.querySelector(".chkLikes").checked = query.IsLiked == true; - context.querySelector(".chkDislikes").checked = query.IsDisliked == true; + if (options.mode === 'livetvchannels') { + context.querySelector('.chkFavorite').checked = query.IsFavorite === true; + context.querySelector('.chkLikes').checked = query.IsLiked === true; + context.querySelector('.chkDislikes').checked = query.IsDisliked === true; } else { - elems = context.querySelectorAll(".chkStandardFilter"); - for (i = 0, length = elems.length; i < length; i++) { - var chkStandardFilter = elems[i]; - var filters = "," + (query.Filters || ""); - var filterName = chkStandardFilter.getAttribute("data-filter"); - chkStandardFilter.checked = filters.indexOf("," + filterName) != -1; + for (const elem of context.querySelectorAll('.chkStandardFilter')) { + const filters = `,${query.Filters || ''}`; + const filterName = elem.getAttribute('data-filter'); + elem.checked = filters.includes(`,${filterName}`); } } - elems = context.querySelectorAll(".chkVideoTypeFilter"); - for (i = 0, length = elems.length; i < length; i++) { - var chkVideoTypeFilter = elems[i]; - var filters = "," + (query.VideoTypes || ""); - var filterName = chkVideoTypeFilter.getAttribute("data-filter"); - chkVideoTypeFilter.checked = filters.indexOf("," + filterName) != -1; + for (const elem of context.querySelectorAll('.chkVideoTypeFilter')) { + const filters = `,${query.VideoTypes || ''}`; + const filterName = elem.getAttribute('data-filter'); + elem.checked = filters.includes(`,${filterName}`); } - context.querySelector(".chk3DFilter").checked = query.Is3D == true; - context.querySelector(".chkHDFilter").checked = query.IsHD == true; - context.querySelector(".chk4KFilter").checked = query.Is4K == true; - context.querySelector(".chkSDFilter").checked = query.IsHD == true; - context.querySelector("#chkSubtitle").checked = query.HasSubtitles == true; - context.querySelector("#chkTrailer").checked = query.HasTrailer == true; - context.querySelector("#chkThemeSong").checked = query.HasThemeSong == true; - context.querySelector("#chkThemeVideo").checked = query.HasThemeVideo == true; - context.querySelector("#chkSpecialFeature").checked = query.HasSpecialFeature == true; - context.querySelector("#chkSpecialEpisode").checked = query.ParentIndexNumber == 0; - context.querySelector("#chkMissingEpisode").checked = query.IsMissing == true; - context.querySelector("#chkFutureEpisode").checked = query.IsUnaired == true; - for (i = 0, length = elems.length; i < length; i++) { - var chkStatus = elems[i]; - var filters = "," + (query.SeriesStatus || ""); - var filterName = chkStatus.getAttribute("data-filter"); - chkStatus.checked = filters.indexOf("," + filterName) != -1; + context.querySelector('.chk3DFilter').checked = query.Is3D === true; + context.querySelector('.chkHDFilter').checked = query.IsHD === true; + context.querySelector('.chk4KFilter').checked = query.Is4K === true; + context.querySelector('.chkSDFilter').checked = query.IsHD === true; + context.querySelector('#chkSubtitle').checked = query.HasSubtitles === true; + context.querySelector('#chkTrailer').checked = query.HasTrailer === true; + context.querySelector('#chkThemeSong').checked = query.HasThemeSong === true; + context.querySelector('#chkThemeVideo').checked = query.HasThemeVideo === true; + context.querySelector('#chkSpecialFeature').checked = query.HasSpecialFeature === true; + context.querySelector('#chkSpecialEpisode').checked = query.ParentIndexNumber === 0; + context.querySelector('#chkMissingEpisode').checked = query.IsMissing === true; + context.querySelector('#chkFutureEpisode').checked = query.IsUnaired === true; + for (const elem of context.querySelectorAll('.chkStatus')) { + const filters = `,${query.SeriesStatus || ''}`; + const filterName = elem.getAttribute('data-filter'); + elem.checked = filters.includes(`,${filterName}`); } } + /** + * @param instance {FilterDialog} An instance of FilterDialog + */ function triggerChange(instance) { - events.trigger(instance, "filterchange"); + events.trigger(instance, 'filterchange'); } function setVisibility(context, options) { - if (options.mode == "livetvchannels" || options.mode == "albums" || options.mode == "artists" || options.mode == "albumartists" || options.mode == "songs") { - hideByClass(context, "videoStandard"); + if (options.mode === 'livetvchannels' || options.mode === 'albums' || options.mode === 'artists' || options.mode === 'albumartists' || options.mode === 'songs') { + hideByClass(context, 'videoStandard'); } if (enableDynamicFilters(options.mode)) { - context.querySelector(".genreFilters").classList.remove("hide"); - context.querySelector(".officialRatingFilters").classList.remove("hide"); - context.querySelector(".tagFilters").classList.remove("hide"); - context.querySelector(".yearFilters").classList.remove("hide"); + context.querySelector('.genreFilters').classList.remove('hide'); + context.querySelector('.officialRatingFilters').classList.remove('hide'); + context.querySelector('.tagFilters').classList.remove('hide'); + context.querySelector('.yearFilters').classList.remove('hide'); } - if (options.mode == "movies" || options.mode == "episodes") { - context.querySelector(".videoTypeFilters").classList.remove("hide"); + if (options.mode === 'movies' || options.mode === 'episodes') { + context.querySelector('.videoTypeFilters').classList.remove('hide'); } - if (options.mode == "movies" || options.mode == "series" || options.mode == "episodes") { - context.querySelector(".features").classList.remove("hide"); + if (options.mode === 'movies' || options.mode === 'series' || options.mode === 'episodes') { + context.querySelector('.features').classList.remove('hide'); } - if (options.mode == "series") { - context.querySelector(".seriesStatus").classList.remove("hide"); + if (options.mode === 'series') { + context.querySelector('.seriesStatus').classList.remove('hide'); } - if (options.mode == "episodes") { - showByClass(context, "episodeFilter"); + if (options.mode === 'episodes') { + showByClass(context, 'episodeFilter'); } } function showByClass(context, className) { - var elems = context.querySelectorAll("." + className); - - for (var i = 0, length = elems.length; i < length; i++) { - elems[i].classList.remove("hide"); + for (const elem of context.querySelectorAll(`.${className}`)) { + elem.classList.remove('hide'); } } function hideByClass(context, className) { - var elems = context.querySelectorAll("." + className); - - for (var i = 0, length = elems.length; i < length; i++) { - elems[i].classList.add("hide"); + for (const elem of context.querySelectorAll(`.${className}`)) { + elem.classList.add('hide'); } } function enableDynamicFilters(mode) { - return mode == "movies" || mode == "series" || mode == "albums" || mode == "albumartists" || mode == "artists" || mode == "songs" || mode == "episodes"; + return mode === 'movies' || mode === 'series' || mode === 'albums' || mode === 'albumartists' || mode === 'artists' || mode === 'songs' || mode === 'episodes'; } - return function (options) { - function onFavoriteChange() { - var query = options.query; - query.StartIndex = 0; - query.IsFavorite = !!this.checked || null; - triggerChange(self); + class FilterDialog { + constructor(options) { + /** + * @private + */ + this.options = options; } - function onStandardFilterChange() { - var query = options.query; - var filterName = this.getAttribute("data-filter"); - var filters = query.Filters || ""; - filters = ("," + filters).replace("," + filterName, "").substring(1); + /** + * @private + */ + onFavoriteChange(elem) { + const query = this.options.query; + query.StartIndex = 0; + query.IsFavorite = !!elem.checked || null; + triggerChange(this); + } - if (this.checked) { - filters = filters ? filters + "," + filterName : filterName; + /** + * @private + */ + onStandardFilterChange(elem) { + const query = this.options.query; + const filterName = elem.getAttribute('data-filter'); + let filters = query.Filters || ''; + filters = (`,${filters}`).replace(`,${filterName}`, '').substring(1); + + if (elem.checked) { + filters = filters ? `${filters},${filterName}` : filterName; } query.StartIndex = 0; query.Filters = filters; - triggerChange(self); + triggerChange(this); } - function onVideoTypeFilterChange() { - var query = options.query; - var filterName = this.getAttribute("data-filter"); - var filters = query.VideoTypes || ""; - filters = ("," + filters).replace("," + filterName, "").substring(1); + /** + * @private + */ + onVideoTypeFilterChange(elem) { + const query = this.options.query; + const filterName = elem.getAttribute('data-filter'); + let filters = query.VideoTypes || ''; + filters = (`,${filters}`).replace(`,${filterName}`, '').substring(1); - if (this.checked) { - filters = filters ? filters + "," + filterName : filterName; + if (elem.checked) { + filters = filters ? `${filters},${filterName}` : filterName; } query.StartIndex = 0; query.VideoTypes = filters; - triggerChange(self); + triggerChange(this); } - function onStatusChange() { - var query = options.query; - var filterName = this.getAttribute("data-filter"); - var filters = query.SeriesStatus || ""; - filters = ("," + filters).replace("," + filterName, "").substring(1); + /** + * @private + */ + onStatusChange(elem) { + const query = this.options.query; + const filterName = elem.getAttribute('data-filter'); + let filters = query.SeriesStatus || ''; + filters = (`,${filters}`).replace(`,${filterName}`, '').substring(1); - if (this.checked) { - filters = filters ? filters + "," + filterName : filterName; + if (elem.checked) { + filters = filters ? `${filters},${filterName}` : filterName; } query.SeriesStatus = filters; query.StartIndex = 0; - triggerChange(self); + triggerChange(this); } - function bindEvents(context) { - var elems; - var i; - var length; - var query = options.query; + /** + * @param context {HTMLDivElement} The dialog + */ + bindEvents(context) { + const query = this.options.query; - if (options.mode == "livetvchannels") { - elems = context.querySelectorAll(".chkFavorite"); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("change", onFavoriteChange); + if (this.options.mode === 'livetvchannels') { + for (const elem of context.querySelectorAll('.chkFavorite')) { + elem.addEventListener('change', () => this.onFavoriteChange(elem)); } - context.querySelector(".chkLikes").addEventListener("change", function () { + + const chkLikes = context.querySelector('.chkLikes'); + chkLikes.addEventListener('change', () => { query.StartIndex = 0; - query.IsLiked = this.checked ? true : null; - triggerChange(self); + query.IsLiked = chkLikes.checked ? true : null; + triggerChange(this); }); - context.querySelector(".chkDislikes").addEventListener("change", function () { + const chkDislikes = context.querySelector('.chkDislikes'); + chkDislikes.addEventListener('change', () => { query.StartIndex = 0; - query.IsDisliked = this.checked ? true : null; - triggerChange(self); + query.IsDisliked = chkDislikes.checked ? true : null; + triggerChange(this); }); } else { - elems = context.querySelectorAll(".chkStandardFilter"); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("change", onStandardFilterChange); + for (const elem of context.querySelectorAll('.chkStandardFilter')) { + elem.addEventListener('change', () => this.onStandardFilterChange(elem)); } } - elems = context.querySelectorAll(".chkVideoTypeFilter"); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("change", onVideoTypeFilterChange); + + for (const elem of context.querySelectorAll('.chkVideoTypeFilter')) { + elem.addEventListener('change', () => this.onVideoTypeFilterChange(elem)); } - context.querySelector(".chk3DFilter").addEventListener("change", function () { + const chk3DFilter = context.querySelector('.chk3DFilter'); + chk3DFilter.addEventListener('change', () => { query.StartIndex = 0; - query.Is3D = this.checked ? true : null; - triggerChange(self); + query.Is3D = chk3DFilter.checked ? true : null; + triggerChange(this); }); - context.querySelector(".chk4KFilter").addEventListener("change", function () { + const chk4KFilter = context.querySelector('.chk4KFilter'); + chk4KFilter.addEventListener('change', () => { query.StartIndex = 0; - query.Is4K = this.checked ? true : null; - triggerChange(self); + query.Is4K = chk4KFilter.checked ? true : null; + triggerChange(this); }); - context.querySelector(".chkHDFilter").addEventListener("change", function () { + const chkHDFilter = context.querySelector('.chkHDFilter'); + chkHDFilter.addEventListener('change', () => { query.StartIndex = 0; - query.IsHD = this.checked ? true : null; - triggerChange(self); + query.IsHD = chkHDFilter.checked ? true : null; + triggerChange(this); }); - context.querySelector(".chkSDFilter").addEventListener("change", function () { + const chkSDFilter = context.querySelector('.chkSDFilter'); + chkSDFilter.addEventListener('change', () => { query.StartIndex = 0; - query.IsHD = this.checked ? false : null; - triggerChange(self); + query.IsHD = chkSDFilter.checked ? false : null; + triggerChange(this); }); - elems = context.querySelectorAll(".chkStatus"); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("change", onStatusChange); + for (const elem of context.querySelectorAll('.chkStatus')) { + elem.addEventListener('change', () => this.onStatusChange(elem)); } - context.querySelector("#chkTrailer").addEventListener("change", function () { + const chkTrailer = context.querySelector('#chkTrailer'); + chkTrailer.addEventListener('change', () => { query.StartIndex = 0; - query.HasTrailer = this.checked ? true : null; - triggerChange(self); + query.HasTrailer = chkTrailer.checked ? true : null; + triggerChange(this); }); - context.querySelector("#chkThemeSong").addEventListener("change", function () { + const chkThemeSong = context.querySelector('#chkThemeSong'); + chkThemeSong.addEventListener('change', () => { query.StartIndex = 0; - query.HasThemeSong = this.checked ? true : null; - triggerChange(self); + query.HasThemeSong = chkThemeSong.checked ? true : null; + triggerChange(this); }); - context.querySelector("#chkSpecialFeature").addEventListener("change", function () { + const chkSpecialFeature = context.querySelector('#chkSpecialFeature'); + chkSpecialFeature.addEventListener('change', () => { query.StartIndex = 0; - query.HasSpecialFeature = this.checked ? true : null; - triggerChange(self); + query.HasSpecialFeature = chkSpecialFeature.checked ? true : null; + triggerChange(this); }); - context.querySelector("#chkThemeVideo").addEventListener("change", function () { + const chkThemeVideo = context.querySelector('#chkThemeVideo'); + chkThemeVideo.addEventListener('change', () => { query.StartIndex = 0; - query.HasThemeVideo = this.checked ? true : null; - triggerChange(self); + query.HasThemeVideo = chkThemeVideo.checked ? true : null; + triggerChange(this); }); - context.querySelector("#chkMissingEpisode").addEventListener("change", function () { + const chkMissingEpisode = context.querySelector('#chkMissingEpisode'); + chkMissingEpisode.addEventListener('change', () => { query.StartIndex = 0; - query.IsMissing = this.checked ? true : false; - triggerChange(self); + query.IsMissing = !!chkMissingEpisode.checked; + triggerChange(this); }); - context.querySelector("#chkSpecialEpisode").addEventListener("change", function () { + const chkSpecialEpisode = context.querySelector('#chkSpecialEpisode'); + chkSpecialEpisode.addEventListener('change', () => { query.StartIndex = 0; - query.ParentIndexNumber = this.checked ? 0 : null; - triggerChange(self); + query.ParentIndexNumber = chkSpecialEpisode.checked ? 0 : null; + triggerChange(this); }); - context.querySelector("#chkFutureEpisode").addEventListener("change", function () { + const chkFutureEpisode = context.querySelector('#chkFutureEpisode'); + chkFutureEpisode.addEventListener('change', () => { query.StartIndex = 0; - if (this.checked) { + if (chkFutureEpisode.checked) { query.IsUnaired = true; query.IsVirtualUnaired = null; } else { query.IsUnaired = null; query.IsVirtualUnaired = false; } - triggerChange(self); + triggerChange(this); }); - context.querySelector("#chkSubtitle").addEventListener("change", function () { + const chkSubtitle = context.querySelector('#chkSubtitle'); + chkSubtitle.addEventListener('change', () => { query.StartIndex = 0; - query.HasSubtitles = this.checked ? true : null; - triggerChange(self); + query.HasSubtitles = chkSubtitle.checked ? true : null; + triggerChange(this); }); - context.addEventListener("change", function (e) { - var chkGenreFilter = dom.parentWithClass(e.target, "chkGenreFilter"); + context.addEventListener('change', (e) => { + const chkGenreFilter = dom.parentWithClass(e.target, 'chkGenreFilter'); if (chkGenreFilter) { - var filterName = chkGenreFilter.getAttribute("data-filter"); - var filters = query.Genres || ""; - var delimiter = "|"; - filters = (delimiter + filters).replace(delimiter + filterName, "").substring(1); + const filterName = chkGenreFilter.getAttribute('data-filter'); + let filters = query.Genres || ''; + const delimiter = '|'; + filters = (delimiter + filters).replace(delimiter + filterName, '').substring(1); if (chkGenreFilter.checked) { filters = filters ? (filters + delimiter + filterName) : filterName; } query.StartIndex = 0; query.Genres = filters; - triggerChange(self); + triggerChange(this); return; } - var chkTagFilter = dom.parentWithClass(e.target, "chkTagFilter"); + const chkTagFilter = dom.parentWithClass(e.target, 'chkTagFilter'); if (chkTagFilter) { - var filterName = chkTagFilter.getAttribute("data-filter"); - var filters = query.Tags || ""; - var delimiter = "|"; - filters = (delimiter + filters).replace(delimiter + filterName, "").substring(1); + const filterName = chkTagFilter.getAttribute('data-filter'); + let filters = query.Tags || ''; + const delimiter = '|'; + filters = (delimiter + filters).replace(delimiter + filterName, '').substring(1); if (chkTagFilter.checked) { filters = filters ? (filters + delimiter + filterName) : filterName; } query.StartIndex = 0; query.Tags = filters; - triggerChange(self); + triggerChange(this); return; } - var chkYearFilter = dom.parentWithClass(e.target, "chkYearFilter"); + const chkYearFilter = dom.parentWithClass(e.target, 'chkYearFilter'); if (chkYearFilter) { - var filterName = chkYearFilter.getAttribute("data-filter"); - var filters = query.Years || ""; - var delimiter = ","; - filters = (delimiter + filters).replace(delimiter + filterName, "").substring(1); + const filterName = chkYearFilter.getAttribute('data-filter'); + let filters = query.Years || ''; + const delimiter = ','; + filters = (delimiter + filters).replace(delimiter + filterName, '').substring(1); if (chkYearFilter.checked) { filters = filters ? (filters + delimiter + filterName) : filterName; } query.StartIndex = 0; query.Years = filters; - triggerChange(self); + triggerChange(this); return; } - var chkOfficialRatingFilter = dom.parentWithClass(e.target, "chkOfficialRatingFilter"); + const chkOfficialRatingFilter = dom.parentWithClass(e.target, 'chkOfficialRatingFilter'); if (chkOfficialRatingFilter) { - var filterName = chkOfficialRatingFilter.getAttribute("data-filter"); - var filters = query.OfficialRatings || ""; - var delimiter = "|"; - filters = (delimiter + filters).replace(delimiter + filterName, "").substring(1); + const filterName = chkOfficialRatingFilter.getAttribute('data-filter'); + let filters = query.OfficialRatings || ''; + const delimiter = '|'; + filters = (delimiter + filters).replace(delimiter + filterName, '').substring(1); if (chkOfficialRatingFilter.checked) { filters = filters ? (filters + delimiter + filterName) : filterName; } query.StartIndex = 0; query.OfficialRatings = filters; - triggerChange(self); - return; + triggerChange(this); } }); } - var self = this; - - self.show = function () { - return new Promise(function (resolve, reject) { - require(["text!./filterdialog.template.html"], function (template) { - var dlg = dialogHelper.createDialog({ + show() { + return import('text!./filterdialog.template.html').then(({default: template}) => { + return new Promise((resolve) => { + const dlg = dialogHelper.createDialog({ removeOnClose: true, modal: false }); - dlg.classList.add("ui-body-a"); - dlg.classList.add("background-theme-a"); - dlg.classList.add("formDialog"); - dlg.classList.add("filterDialog"); + dlg.classList.add('ui-body-a'); + dlg.classList.add('background-theme-a'); + dlg.classList.add('formDialog'); + dlg.classList.add('filterDialog'); dlg.innerHTML = globalize.translateDocument(template); - setVisibility(dlg, options); + setVisibility(dlg, this.options); dialogHelper.open(dlg); - dlg.addEventListener("close", resolve); - updateFilterControls(dlg, options); - bindEvents(dlg); - if (enableDynamicFilters(options.mode)) { - dlg.classList.add("dynamicFilterDialog"); - var apiClient = connectionManager.getApiClient(options.serverId); - loadDynamicFilters(dlg, apiClient, apiClient.getCurrentUserId(), options.query); + dlg.addEventListener('close', resolve); + updateFilterControls(dlg, this.options); + this.bindEvents(dlg); + if (enableDynamicFilters(this.options.mode)) { + dlg.classList.add('dynamicFilterDialog'); + const apiClient = connectionManager.getApiClient(this.options.serverId); + loadDynamicFilters(dlg, apiClient, apiClient.getCurrentUserId(), this.options.query); } }); }); - }; - }; -}); + } + } + +/* eslint-enable indent */ + +export default FilterDialog; diff --git a/src/components/filtermenu/filtermenu.js b/src/components/filtermenu/filtermenu.js index 6b08ddab16..c189856e7b 100644 --- a/src/components/filtermenu/filtermenu.js +++ b/src/components/filtermenu/filtermenu.js @@ -279,7 +279,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost', var html = ''; html += '
'; - html += ''; + html += ''; html += '

${Filters}

'; html += '
'; diff --git a/src/components/focusManager.js b/src/components/focusManager.js index 1da58d75c8..f2022cc445 100644 --- a/src/components/focusManager.js +++ b/src/components/focusManager.js @@ -117,7 +117,7 @@ define(['dom', 'scrollManager'], function (dom, scrollManager) { return false; } - if (elem.getAttribute('tabindex') === "-1") { + if (elem.getAttribute('tabindex') === '-1') { return false; } diff --git a/src/components/formdialog.css b/src/components/formdialog.css index 788331da94..94695f4865 100644 --- a/src/components/formdialog.css +++ b/src/components/formdialog.css @@ -19,6 +19,10 @@ margin-bottom: 0; } +.formDialogHeaderTitle:first-child { + margin-left: 1em; +} + .formDialogContent:not(.no-grow) { flex-grow: 1; } @@ -46,10 +50,16 @@ right: 0; display: flex; position: absolute; - padding: 1.25em 1em; + padding: 1em 1em; /* Without this emby-checkbox is able to appear on top */ z-index: 1; + align-items: flex-end; + justify-content: flex-end; + flex-wrap: wrap; +} + +.layout-tv .formDialogFooter { align-items: center; justify-content: center; flex-wrap: wrap; @@ -69,8 +79,12 @@ .formDialogFooterItem { margin: 0.5em !important; - flex-grow: 1; text-align: center; + flex-basis: 12em; +} + +.layout-tv .formDialogFooterItem { + flex-grow: 1; flex-basis: 0; } diff --git a/src/components/fullscreenManager.js b/src/components/fullscreenManager.js deleted file mode 100644 index 8ae31073a2..0000000000 --- a/src/components/fullscreenManager.js +++ /dev/null @@ -1,103 +0,0 @@ -define(['events', 'dom', 'apphost', 'browser'], function (events, dom, appHost, browser) { - 'use strict'; - - function fullscreenManager() { - - } - - fullscreenManager.prototype.requestFullscreen = function (element) { - - element = element || document.documentElement; - - if (element.requestFullscreen) { - element.requestFullscreen(); - return; - } else if (element.mozRequestFullScreen) { - element.mozRequestFullScreen(); - return; - } else if (element.webkitRequestFullscreen) { - element.webkitRequestFullscreen(); - return; - } else if (element.msRequestFullscreen) { - element.msRequestFullscreen(); - return; - } - - // Hack - This is only available for video elements in ios safari - if (element.tagName !== 'VIDEO') { - element = document.querySelector('video') || element; - } - if (element.webkitEnterFullscreen) { - element.webkitEnterFullscreen(); - } - }; - - fullscreenManager.prototype.exitFullscreen = function () { - - if (!this.isFullScreen()) { - return; - } - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } else if (document.webkitCancelFullscreen) { - document.webkitCancelFullscreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } - }; - - // TODO: use screenfull.js - fullscreenManager.prototype.isFullScreen = function () { - return document.fullscreen || - document.mozFullScreen || - document.webkitIsFullScreen || - document.msFullscreenElement || /* IE/Edge syntax */ - document.fullscreenElement || /* Standard syntax */ - document.webkitFullscreenElement || /* Chrome, Safari and Opera syntax */ - document.mozFullScreenElement; /* Firefox syntax */ - }; - - var manager = new fullscreenManager(); - - function onFullScreenChange() { - events.trigger(manager, 'fullscreenchange'); - } - - dom.addEventListener(document, 'fullscreenchange', onFullScreenChange, { - passive: true - }); - - dom.addEventListener(document, 'webkitfullscreenchange', onFullScreenChange, { - passive: true - }); - - dom.addEventListener(document, 'mozfullscreenchange', onFullScreenChange, { - passive: true - }); - - function isTargetValid(target) { - return !dom.parentWithTag(target, ['BUTTON', 'INPUT', 'TEXTAREA']); - } - if (appHost.supports("fullscreenchange") && (browser.edgeUwp || -1 !== navigator.userAgent.toLowerCase().indexOf("electron"))) { - - dom.addEventListener(window, 'dblclick', function (e) { - - if (isTargetValid(e.target)) { - if (manager.isFullScreen()) { - manager.exitFullscreen(); - } else { - manager.requestFullscreen(); - } - } - - }, { - passive: true - }); - } - - return manager; -}); diff --git a/src/components/groupedcards.js b/src/components/groupedcards.js index ad638ecdd9..602c4310f4 100644 --- a/src/components/groupedcards.js +++ b/src/components/groupedcards.js @@ -1,28 +1,28 @@ -define(["dom", "appRouter", "connectionManager"], function (dom, appRouter, connectionManager) { - "use strict"; +define(['dom', 'appRouter', 'connectionManager'], function (dom, appRouter, connectionManager) { + 'use strict'; function onGroupedCardClick(e, card) { - var itemId = card.getAttribute("data-id"); - var serverId = card.getAttribute("data-serverid"); + var itemId = card.getAttribute('data-id'); + var serverId = card.getAttribute('data-serverid'); var apiClient = connectionManager.getApiClient(serverId); var userId = apiClient.getCurrentUserId(); - var playedIndicator = card.querySelector(".playedIndicator"); + var playedIndicator = card.querySelector('.playedIndicator'); var playedIndicatorHtml = playedIndicator ? playedIndicator.innerHTML : null; var options = { - Limit: parseInt(playedIndicatorHtml || "10"), - Fields: "PrimaryImageAspectRatio,DateCreated", + Limit: parseInt(playedIndicatorHtml || '10'), + Fields: 'PrimaryImageAspectRatio,DateCreated', ParentId: itemId, GroupItems: false }; - var actionableParent = dom.parentWithTag(e.target, ["A", "BUTTON", "INPUT"]); + var actionableParent = dom.parentWithTag(e.target, ['A', 'BUTTON', 'INPUT']); - if (!actionableParent || actionableParent.classList.contains("cardContent")) { - apiClient.getJSON(apiClient.getUrl("Users/" + userId + "/Items/Latest", options)).then(function (items) { + if (!actionableParent || actionableParent.classList.contains('cardContent')) { + apiClient.getJSON(apiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) { if (1 === items.length) { return void appRouter.showItem(items[0]); } - var url = "itemdetails.html?id=" + itemId + "&serverId=" + serverId; + var url = 'itemdetails.html?id=' + itemId + '&serverId=' + serverId; Dashboard.navigate(url); }); e.stopPropagation(); @@ -32,7 +32,7 @@ define(["dom", "appRouter", "connectionManager"], function (dom, appRouter, conn } function onItemsContainerClick(e) { - var groupedCard = dom.parentWithClass(e.target, "groupedCard"); + var groupedCard = dom.parentWithClass(e.target, 'groupedCard'); if (groupedCard) { onGroupedCardClick(e, groupedCard); diff --git a/src/components/guide/guide-settings.template.html b/src/components/guide/guide-settings.template.html index 02701db3d2..edb2ffa8d3 100644 --- a/src/components/guide/guide-settings.template.html +++ b/src/components/guide/guide-settings.template.html @@ -1,5 +1,7 @@
- +

${Settings}

diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index a00baaa6f0..223d3a2063 100644 --- a/src/components/guide/guide.js +++ b/src/components/guide/guide.js @@ -1,4 +1,4 @@ -define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', 'scrollHelper', 'serverNotifications', 'loading', 'datetime', 'focusManager', 'playbackManager', 'userSettings', 'imageLoader', 'events', 'layoutManager', 'itemShortcuts', 'dom', 'css!./guide.css', 'programStyles', 'material-icons', 'scrollStyles', 'emby-button', 'paper-icon-button-light', 'emby-tabs', 'emby-scroller', 'flexStyles', 'registerElement'], function (require, inputManager, browser, globalize, connectionManager, scrollHelper, serverNotifications, loading, datetime, focusManager, playbackManager, userSettings, imageLoader, events, layoutManager, itemShortcuts, dom) { +define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', 'scrollHelper', 'serverNotifications', 'loading', 'datetime', 'focusManager', 'playbackManager', 'userSettings', 'imageLoader', 'events', 'layoutManager', 'itemShortcuts', 'dom', 'css!./guide.css', 'programStyles', 'material-icons', 'scrollStyles', 'emby-programcell', 'emby-button', 'paper-icon-button-light', 'emby-tabs', 'emby-scroller', 'flexStyles', 'registerElement'], function (require, inputManager, browser, globalize, connectionManager, scrollHelper, serverNotifications, loading, datetime, focusManager, playbackManager, userSettings, imageLoader, events, layoutManager, itemShortcuts, dom) { 'use strict'; function showViewSettings(instance) { @@ -227,7 +227,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', channelQuery.Limit = channelLimit; channelQuery.AddCurrentProgram = false; channelQuery.EnableUserData = false; - channelQuery.EnableImageTypes = "Primary"; + channelQuery.EnableImageTypes = 'Primary'; var categories = self.categoryOptions.categories || []; var displayMovieContent = !categories.length || categories.indexOf('movies') !== -1; @@ -261,8 +261,8 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', } if (userSettings.get('livetv-channelorder') === 'DatePlayed') { - channelQuery.SortBy = "DatePlayed"; - channelQuery.SortOrder = "Descending"; + channelQuery.SortBy = 'DatePlayed'; + channelQuery.SortOrder = 'Descending'; } else { channelQuery.SortBy = null; channelQuery.SortOrder = null; @@ -329,7 +329,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', ImageTypeLimit: 1, EnableImages: false, //EnableImageTypes: layoutManager.tv ? "Primary,Backdrop" : "Primary", - SortBy: "StartDate", + SortBy: 'StartDate', EnableTotalRecordCount: false, EnableUserData: false }; @@ -415,7 +415,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', var status; if (item.Type === 'SeriesTimer') { - return ''; + return ''; } else if (item.TimerId || item.SeriesTimerId) { status = item.Status || 'Cancelled'; @@ -429,13 +429,13 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', if (item.SeriesTimerId) { if (status !== 'Cancelled') { - return ''; + return ''; } - return ''; + return ''; } - return ''; + return ''; } function getChannelProgramsHtml(context, date, channel, programs, options, listInfo) { @@ -502,7 +502,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', var endPercent = (renderEndMs - renderStartMs) / msPerDay; endPercent *= 100; - var cssClass = "programCell itemAction"; + var cssClass = 'programCell itemAction'; var accentCssClass = null; var displayInnerContent = true; @@ -525,11 +525,11 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', } if (displayInnerContent && enableColorCodedBackgrounds && accentCssClass) { - cssClass += " programCell-" + accentCssClass; + cssClass += ' programCell-' + accentCssClass; } if (now >= startDateLocalMs && now < endDateLocalMs) { - cssClass += " programCell-active"; + cssClass += ' programCell-active'; } var timerAttributes = ''; @@ -545,11 +545,11 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', html += ''; if (displayInnerContent) { - var guideProgramNameClass = "guideProgramName"; + var guideProgramNameClass = 'guideProgramName'; html += '
'; - html += '
'; + html += '
'; html += '
' + program.Name; @@ -577,7 +577,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', html += '
'; if (program.IsHD && options.showHdIcon) { - //html += 'hd'; + //html += ''; if (layoutManager.tv) { html += '
HD
'; } else { @@ -630,7 +630,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', var url = apiClient.getScaledImageUrl(channel.Id, { maxHeight: 220, tag: channel.ImageTags.Primary, - type: "Primary" + type: 'Primary' }); html += '
'; @@ -1105,7 +1105,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', var icon = cell.querySelector('.timerIcon'); if (!icon) { - cell.querySelector('.guideProgramName').insertAdjacentHTML('beforeend', ''); + cell.querySelector('.guideProgramName').insertAdjacentHTML('beforeend', ''); } if (newTimerId) { @@ -1252,18 +1252,5 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', }); } - var ProgramCellPrototype = Object.create(HTMLButtonElement.prototype); - - ProgramCellPrototype.detachedCallback = function () { - this.posLeft = null; - this.posWidth = null; - this.guideProgramName = null; - }; - - document.registerElement('emby-programcell', { - prototype: ProgramCellPrototype, - extends: 'button' - }); - return Guide; }); diff --git a/src/components/guide/tvguide.template.html b/src/components/guide/tvguide.template.html index ef3c4b58ca..730de55909 100644 --- a/src/components/guide/tvguide.template.html +++ b/src/components/guide/tvguide.template.html @@ -9,8 +9,8 @@
-
@@ -29,10 +29,10 @@
- -
diff --git a/src/components/headroom/headroom.css b/src/components/headroom/headroom.css deleted file mode 100644 index df985892ff..0000000000 --- a/src/components/headroom/headroom.css +++ /dev/null @@ -1,11 +0,0 @@ -.headroom { - transition: transform 140ms linear; -} - -.headroom--pinned { - transform: none; -} - -.headroom--unpinned:not(.headroomDisabled) { - transform: translateY(-100%); -} diff --git a/src/components/headroom/headroom.js b/src/components/headroom/headroom.js deleted file mode 100644 index 3c0ada3ce9..0000000000 --- a/src/components/headroom/headroom.js +++ /dev/null @@ -1,343 +0,0 @@ -/*! - * headroom.js v0.7.0 - Give your page some headroom. Hide your header until you need it - * Copyright (c) 2014 Nick Williams - http://wicky.nillia.ms/headroom.js - * License: MIT - */ - -define(['dom', 'layoutManager', 'browser', 'css!./headroom'], function (dom, layoutManager, browser) { - - 'use strict'; - - /* exported features */ - - var requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; - - /** - * Handles debouncing of events via requestAnimationFrame - * @see http://www.html5rocks.com/en/tutorials/speed/animations/ - * @param {Function} callback The callback to handle whichever event - */ - function Debouncer(callback) { - this.callback = callback; - this.ticking = false; - } - Debouncer.prototype = { - constructor: Debouncer, - - /** - * dispatches the event to the supplied callback - * @private - */ - update: function () { - if (this.callback) { - this.callback(); - } - this.ticking = false; - }, - - /** - * Attach this as the event listeners - */ - handleEvent: function () { - if (!this.ticking) { - requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this))); - this.ticking = true; - } - } - }; - - function onHeadroomClearedExternally() { - this.state = null; - } - - /** - * UI enhancement for fixed headers. - * Hides header when scrolling down - * Shows header when scrolling up - * @constructor - * @param {DOMElement} elem the header element - * @param {Object} options options for the widget - */ - function Headroom(elems, options) { - options = Object.assign(Headroom.options, options || {}); - - this.lastKnownScrollY = 0; - this.elems = elems; - - this.scroller = options.scroller; - - this.debouncer = onScroll.bind(this); - this.offset = options.offset; - this.initialised = false; - - this.initialClass = options.initialClass; - this.unPinnedClass = options.unPinnedClass; - this.pinnedClass = options.pinnedClass; - - this.state = 'clear'; - - this.options = { - offset: 0, - scroller: window, - initialClass: 'headroom', - unPinnedClass: 'headroom--unpinned', - pinnedClass: 'headroom--pinned' - }; - - this.add = function (elem) { - - if (browser.supportsCssAnimation()) { - elem.classList.add(this.initialClass); - elem.addEventListener('clearheadroom', onHeadroomClearedExternally.bind(this)); - this.elems.push(elem); - } - }; - - this.remove = function (elem) { - - elem.classList.remove(this.unPinnedClass); - elem.classList.remove(this.initialClass); - elem.classList.remove(this.pinnedClass); - - var i = this.elems.indexOf(elem); - if (i !== -1) { - this.elems.splice(i, 1); - } - }; - - this.pause = function () { - this.paused = true; - }; - - this.resume = function () { - this.paused = false; - }; - - /** - * Unattaches events and removes any classes that were added - */ - this.destroy = function () { - - this.initialised = false; - - for (var i = 0, length = this.elems.length; i < length; i++) { - - var classList = this.elems[i].classList; - - classList.remove(this.unPinnedClass); - classList.remove(this.initialClass); - classList.remove(this.pinnedClass); - } - - var scrollEventName = this.scroller.getScrollEventName ? this.scroller.getScrollEventName() : 'scroll'; - - dom.removeEventListener(this.scroller, scrollEventName, this.debouncer, { - capture: false, - passive: true - }); - }; - - /** - * Attaches the scroll event - * @private - */ - this.attachEvent = function () { - if (!this.initialised) { - this.lastKnownScrollY = this.getScrollY(); - this.initialised = true; - - var scrollEventName = this.scroller.getScrollEventName ? this.scroller.getScrollEventName() : 'scroll'; - - dom.addEventListener(this.scroller, scrollEventName, this.debouncer, { - capture: false, - passive: true - }); - - this.update(); - } - }; - - /** - * Unpins the header if it's currently pinned - */ - this.clear = function () { - - if (this.state === 'clear') { - return; - } - - this.state = 'clear'; - - var unpinnedClass = this.unPinnedClass; - var pinnedClass = this.pinnedClass; - - for (var i = 0, length = this.elems.length; i < length; i++) { - var classList = this.elems[i].classList; - - classList.remove(unpinnedClass); - //classList.remove(pinnedClass); - } - }; - - /** - * Unpins the header if it's currently pinned - */ - this.pin = function () { - - if (this.state === 'pin') { - return; - } - - this.state = 'pin'; - - var unpinnedClass = this.unPinnedClass; - var pinnedClass = this.pinnedClass; - - for (var i = 0, length = this.elems.length; i < length; i++) { - var classList = this.elems[i].classList; - - classList.remove(unpinnedClass); - classList.add(pinnedClass); - } - }; - - /** - * Unpins the header if it's currently pinned - */ - this.unpin = function () { - - if (this.state === 'unpin') { - return; - } - - this.state = 'unpin'; - - var unpinnedClass = this.unPinnedClass; - var pinnedClass = this.pinnedClass; - - for (var i = 0, length = this.elems.length; i < length; i++) { - var classList = this.elems[i].classList; - - classList.add(unpinnedClass); - //classList.remove(pinnedClass); - } - }; - - /** - * Gets the Y scroll position - * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY - * @return {Number} pixels the page has scrolled along the Y-axis - */ - this.getScrollY = function () { - - var scroller = this.scroller; - - if (scroller.getScrollPosition) { - return scroller.getScrollPosition(); - } - - var pageYOffset = scroller.pageYOffset; - if (pageYOffset !== undefined) { - return pageYOffset; - } - - var scrollTop = scroller.scrollTop; - if (scrollTop !== undefined) { - return scrollTop; - } - - return (document.documentElement || document.body).scrollTop; - }; - - /** - * determine if it is appropriate to unpin - * @param {int} currentScrollY the current y scroll position - * @return {bool} true if should unpin, false otherwise - */ - this.shouldUnpin = function (currentScrollY) { - var scrollingDown = currentScrollY > this.lastKnownScrollY; - var pastOffset = currentScrollY >= this.offset; - - return scrollingDown && pastOffset; - }; - - /** - * determine if it is appropriate to pin - * @param {int} currentScrollY the current y scroll position - * @return {bool} true if should pin, false otherwise - */ - this.shouldPin = function (currentScrollY) { - var scrollingUp = currentScrollY < this.lastKnownScrollY; - var pastOffset = currentScrollY <= this.offset; - - return scrollingUp || pastOffset; - }; - - /** - * Handles updating the state of the widget - */ - this.update = function () { - - if (this.paused) { - return; - } - - var currentScrollY = this.getScrollY(); - - var lastKnownScrollY = this.lastKnownScrollY; - - var isTv = layoutManager.tv; - - if (currentScrollY <= (isTv ? 120 : 10)) { - this.clear(); - } else if (this.shouldUnpin(currentScrollY)) { - this.unpin(); - } else if (this.shouldPin(currentScrollY)) { - - var toleranceExceeded = Math.abs(currentScrollY - lastKnownScrollY) >= 14; - - if (currentScrollY && isTv) { - this.unpin(); - } else if (toleranceExceeded) { - this.clear(); - } - } else if (isTv) { - //this.clear(); - } - - this.lastKnownScrollY = currentScrollY; - }; - - if (browser.supportsCssAnimation()) { - for (var i = 0, length = this.elems.length; i < length; i++) { - this.elems[i].classList.add(this.initialClass); - this.elems[i].addEventListener('clearheadroom', onHeadroomClearedExternally.bind(this)); - } - - this.attachEvent(); - } - } - - function onScroll() { - - if (this.paused) { - return; - } - - requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this))); - } - - /** - * Default options - * @type {Object} - */ - Headroom.options = { - offset: 0, - scroller: window, - initialClass: 'headroom', - unPinnedClass: 'headroom--unpinned', - pinnedClass: 'headroom--pinned' - }; - - return Headroom; -}); diff --git a/src/components/homescreensettings/homescreensettings.js b/src/components/homeScreenSettings/homeScreenSettings.js similarity index 94% rename from src/components/homescreensettings/homescreensettings.js rename to src/components/homeScreenSettings/homeScreenSettings.js index ccc6fa75f4..9eae944d26 100644 --- a/src/components/homescreensettings/homescreensettings.js +++ b/src/components/homeScreenSettings/homeScreenSettings.js @@ -1,5 +1,5 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loading', 'connectionManager', 'homeSections', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-checkbox'], function (require, appHost, layoutManager, focusManager, globalize, loading, connectionManager, homeSections, dom, events) { - "use strict"; + 'use strict'; var numConfigurableSections = 7; @@ -37,18 +37,19 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa var list = []; if (type === 'movies') { - list.push({ name: globalize.translate('Movies'), value: 'movies', isDefault: true }); - list.push({ name: globalize.translate('Suggestions'), value: 'suggestions' }); - + list.push({ + name: globalize.translate('Genres'), + value: 'genres' + }); list.push({ name: globalize.translate('Favorites'), value: 'favorites' @@ -58,7 +59,6 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa value: 'collections' }); } else if (type === 'tvshows') { - list.push({ name: globalize.translate('Shows'), value: 'shows', @@ -68,49 +68,45 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa name: globalize.translate('Suggestions'), value: 'suggestions' }); - list.push({ name: globalize.translate('Latest'), value: 'latest' }); + list.push({ + name: globalize.translate('Genres'), + value: 'genres' + }); list.push({ name: globalize.translate('Favorites'), value: 'favorites' }); } else if (type === 'music') { - list.push({ name: globalize.translate('Suggestions'), value: 'suggestions', isDefault: true }); - list.push({ name: globalize.translate('Albums'), value: 'albums' }); - list.push({ name: globalize.translate('HeaderAlbumArtists'), value: 'albumartists' }); - list.push({ name: globalize.translate('Artists'), value: 'artists' }); - list.push({ name: globalize.translate('Playlists'), value: 'playlists' }); - list.push({ name: globalize.translate('Genres'), value: 'genres' }); } else if (type === 'livetv') { - list.push({ name: globalize.translate('Suggestions'), value: 'suggestions', @@ -149,7 +145,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa currentHtml += '
'; - currentHtml += ''; + currentHtml += ''; currentHtml += '
'; @@ -159,8 +155,8 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa currentHtml += '
'; - currentHtml += ''; - currentHtml += ''; + currentHtml += ''; + currentHtml += ''; currentHtml += '
'; @@ -276,7 +272,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa updateHomeSectionValues(context, userSettings); var promise1 = apiClient.getUserViews({ IncludeHidden: true }, user.Id); - var promise2 = apiClient.getJSON(apiClient.getUrl("Users/" + user.Id + "/GroupingOptions")); + var promise2 = apiClient.getJSON(apiClient.getUrl('Users/' + user.Id + '/GroupingOptions')); Promise.all([promise1, promise2]).then(function (responses) { @@ -363,17 +359,17 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa user.Configuration.HidePlayedInLatest = context.querySelector('.chkHidePlayedFromLatest').checked; - user.Configuration.LatestItemsExcludes = getCheckboxItems(".chkIncludeInLatest", context, false).map(function (i) { + user.Configuration.LatestItemsExcludes = getCheckboxItems('.chkIncludeInLatest', context, false).map(function (i) { return i.getAttribute('data-folderid'); }); - user.Configuration.MyMediaExcludes = getCheckboxItems(".chkIncludeInMyMedia", context, false).map(function (i) { + user.Configuration.MyMediaExcludes = getCheckboxItems('.chkIncludeInMyMedia', context, false).map(function (i) { return i.getAttribute('data-folderid'); }); - user.Configuration.GroupedFolders = getCheckboxItems(".chkGroupFolder", context, true).map(function (i) { + user.Configuration.GroupedFolders = getCheckboxItems('.chkGroupFolder', context, true).map(function (i) { return i.getAttribute('data-folderid'); }); @@ -470,7 +466,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa function embed(options, self) { - require(['text!./homescreensettings.template.html'], function (template) { + require(['text!./homeScreenSettings.template.html'], function (template) { for (var i = 1; i <= numConfigurableSections; i++) { template = template.replace('{section' + i + 'label}', globalize.translate('LabelHomeScreenSectionValue', i)); diff --git a/src/components/homescreensettings/homescreensettings.template.html b/src/components/homeScreenSettings/homeScreenSettings.template.html similarity index 95% rename from src/components/homescreensettings/homescreensettings.template.html rename to src/components/homeScreenSettings/homeScreenSettings.template.html index d5bae685b8..8515f3f0ad 100644 --- a/src/components/homescreensettings/homescreensettings.template.html +++ b/src/components/homeScreenSettings/homeScreenSettings.template.html @@ -10,6 +10,13 @@
${LabelPleaseRestart}
+
+ +
+
- ${HideWatchedContentFromLatestMedia} - -
-

${HeaderLibraryFolders}

diff --git a/src/components/homesections/homesections.js b/src/components/homesections/homesections.js index 94e36f9d28..535f1cd687 100644 --- a/src/components/homesections/homesections.js +++ b/src/components/homesections/homesections.js @@ -64,21 +64,21 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la } else { var noLibDescription; if (user['Policy'] && user['Policy']['IsAdministrator']) { - noLibDescription = Globalize.translate("NoCreatedLibraries", '', ''); + noLibDescription = globalize.translate('NoCreatedLibraries', '
', ''); } else { - noLibDescription = Globalize.translate("AskAdminToCreateLibrary"); + noLibDescription = globalize.translate('AskAdminToCreateLibrary'); } html += '
'; - html += '

' + Globalize.translate("MessageNothingHere") + '

'; + html += '

' + globalize.translate('MessageNothingHere') + '

'; html += '

' + noLibDescription + '

'; html += '
'; elem.innerHTML = html; - var createNowLink = elem.querySelector("#button-createLibrary"); + var createNowLink = elem.querySelector('#button-createLibrary'); if (createNowLink) { - createNowLink.addEventListener("click", function () { - Dashboard.navigate("library.html"); + createNowLink.addEventListener('click', function () { + Dashboard.navigate('library.html'); }); } } @@ -131,7 +131,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la } else if (section === 'librarytiles' || section === 'smalllibrarytiles' || section === 'smalllibrarytiles-automobile' || section === 'librarytiles-automobile') { loadLibraryTiles(elem, apiClient, user, userSettings, 'smallBackdrop', userViews, allSections); } else if (section === 'librarybuttons') { - loadlibraryButtons(elem, apiClient, user, userSettings, userViews, allSections); + loadlibraryButtons(elem, apiClient, user, userSettings, userViews); } else if (section === 'resume') { loadResumeVideo(elem, apiClient, userId); } else if (section === 'resumeaudio') { @@ -172,7 +172,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la } function getLibraryButtonsHtml(items) { - var html = ""; + var html = ''; html += '
'; html += '

' + globalize.translate('HeaderMyMedia') + '

'; @@ -183,7 +183,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la for (var i = 0, length = items.length; i < length; i++) { var item = items[i]; var icon = imageHelper.getLibraryIcon(item.CollectionType); - html += '' + item.Name + ''; + html += '' + item.Name + ''; } html += '
'; @@ -229,9 +229,9 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la var options = { Limit: limit, - Fields: "PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo,Path', ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Thumb", + EnableImageTypes: 'Primary,Backdrop,Thumb', ParentId: parentId }; @@ -243,9 +243,9 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la return function (items) { var cardLayout = false; var shape; - if (itemType === 'Channel' || viewType === 'movies' || viewType === 'books') { + if (itemType === 'Channel' || viewType === 'movies' || viewType === 'books' || viewType === 'tvshows') { shape = getPortraitShape(); - } else if (viewType === 'music') { + } else if (viewType === 'music' || viewType === 'homevideos') { shape = getSquareShape(); } else { shape = getThumbShape(); @@ -282,7 +282,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la html += '

'; html += globalize.translate('LatestFromLibrary', parent.Name); html += '

'; - html += ''; + html += ''; html += ''; } else { html += '

' + globalize.translate('LatestFromLibrary', parent.Name) + '

'; @@ -386,9 +386,9 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la var options = { Limit: limit, Recursive: true, - Fields: "PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo', ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Thumb", + EnableImageTypes: 'Primary,Backdrop,Thumb', EnableTotalRecordCount: false, MediaTypes: 'Video' }; @@ -459,9 +459,9 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la var options = { Limit: limit, Recursive: true, - Fields: "PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo', ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Thumb", + EnableImageTypes: 'Primary,Backdrop,Thumb', EnableTotalRecordCount: false, MediaTypes: 'Audio' }; @@ -524,9 +524,9 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la IsAiring: true, limit: 24, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Thumb,Backdrop", + EnableImageTypes: 'Primary,Thumb,Backdrop', EnableTotalRecordCount: false, - Fields: "ChannelInfo,PrimaryImageAspectRatio" + Fields: 'ChannelInfo,PrimaryImageAspectRatio' }); }; } @@ -565,9 +565,9 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la IsAiring: true, limit: 1, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Thumb,Backdrop", + EnableImageTypes: 'Primary,Thumb,Backdrop', EnableTotalRecordCount: false, - Fields: "ChannelInfo,PrimaryImageAspectRatio" + Fields: 'ChannelInfo,PrimaryImageAspectRatio' }).then(function (result) { var html = ''; if (result.Items.length) { @@ -630,7 +630,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la html += '

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

'; - html += ''; + html += ''; html += ''; } else { @@ -667,10 +667,10 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la var apiClient = connectionManager.getApiClient(serverId); return apiClient.getNextUpEpisodes({ Limit: enableScrollX() ? 24 : 15, - Fields: "PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo,Path', UserId: apiClient.getCurrentUserId(), ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', EnableTotalRecordCount: false }); }; @@ -705,7 +705,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la html += '

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

'; - html += ''; + html += ''; html += ''; } else { html += '

' + globalize.translate('HeaderNextUp') + '

'; @@ -739,7 +739,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la return apiClient.getLiveTvRecordings({ userId: apiClient.getCurrentUserId(), Limit: enableScrollX() ? 12 : 5, - Fields: "PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo', EnableTotalRecordCount: false, IsLibraryItem: activeRecordingsOnly ? null : false, IsInProgress: activeRecordingsOnly ? true : null diff --git a/src/components/htmlMediaHelper.js b/src/components/htmlMediaHelper.js index 1968ecb7d8..3f17eeb336 100644 --- a/src/components/htmlMediaHelper.js +++ b/src/components/htmlMediaHelper.js @@ -2,12 +2,12 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve 'use strict'; function getSavedVolume() { - return appSettings.get("volume") || 1; + return appSettings.get('volume') || 1; } function saveVolume(value) { if (value) { - appSettings.set("volume", value); + appSettings.set('volume', value); } } @@ -31,7 +31,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve } function enableHlsShakaPlayer(item, mediaSource, mediaType) { - + /* eslint-disable-next-line compat/compat */ if (!!window.MediaSource && !!MediaSource.isTypeSupported) { if (canPlayNativeHls()) { @@ -109,7 +109,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve var now = Date.now(); if (window.performance && window.performance.now) { - now = performance.now(); + now = performance.now(); // eslint-disable-line compat/compat } if (!recoverDecodingErrorDate || (now - recoverDecodingErrorDate) > 3000) { @@ -162,7 +162,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve } } - function seekOnPlaybackStart(instance, element, ticks) { + function seekOnPlaybackStart(instance, element, ticks, onMediaReady) { var seconds = (ticks || 0) / 10000000; @@ -175,9 +175,10 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve if (element.duration >= seconds) { // media is ready, seek immediately setCurrentTimeIfNeeded(element, seconds); + if (onMediaReady) onMediaReady(); } else { // update video player position when media is ready to be sought - var events = ["durationchange", "loadeddata", "play", "loadedmetadata"]; + var events = ['durationchange', 'loadeddata', 'play', 'loadedmetadata']; var onMediaChange = function(e) { if (element.currentTime === 0 && element.duration >= seconds) { // seek only when video position is exactly zero, @@ -189,10 +190,11 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve events.map(function(name) { element.removeEventListener(name, onMediaChange); }); + if (onMediaReady) onMediaReady(); } }; events.map(function (name) { - element.addEventListener(name, onMediaChange); + return element.addEventListener(name, onMediaChange); }); } } @@ -371,13 +373,13 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve onErrorInternal(instance, 'network'); } } else { - console.debug("fatal network error encountered, try to recover"); + console.debug('fatal network error encountered, try to recover'); hls.startLoad(); } break; case Hls.ErrorTypes.MEDIA_ERROR: - console.debug("fatal media error encountered, try to recover"); + console.debug('fatal media error encountered, try to recover'); var currentReject = reject; reject = null; handleHlsJsMediaError(instance, currentReject); @@ -407,7 +409,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve elem.src = ''; elem.innerHTML = ''; - elem.removeAttribute("src"); + elem.removeAttribute('src'); destroyHlsPlayer(instance); destroyFlvPlayer(instance); diff --git a/src/components/imagedownloader/imagedownloader.js b/src/components/imageDownloader/imageDownloader.js similarity index 90% rename from src/components/imagedownloader/imagedownloader.js rename to src/components/imageDownloader/imageDownloader.js index 9df083aea2..a3965279cf 100644 --- a/src/components/imagedownloader/imagedownloader.js +++ b/src/components/imageDownloader/imageDownloader.js @@ -109,15 +109,15 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image html += ''; var startAtDisplay = totalRecordCount ? startIndex + 1 : 0; - html += globalize.translate("ListPaging", startAtDisplay, recordsEnd, totalRecordCount); + html += globalize.translate('ListPaging', startAtDisplay, recordsEnd, totalRecordCount); html += ''; if (showControls) { html += '
'; - html += ''; - html += ''; + html += ''; + html += ''; html += '
'; } @@ -144,7 +144,7 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image } function getDisplayUrl(url, apiClient) { - return apiClient.getUrl("Images/Remote", { imageUrl: url }); + return apiClient.getUrl('Images/Remote', { imageUrl: url }); } function getRemoteImageHtml(image, imageType, apiClient) { @@ -155,21 +155,21 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image var html = ''; - var cssClass = "card scalableCard imageEditorCard"; + var cssClass = 'card scalableCard imageEditorCard'; var cardBoxCssClass = 'cardBox visualCardBox'; var shape = 'backdrop'; - if (imageType === "Backdrop" || imageType === "Art" || imageType === "Thumb" || imageType === "Logo") { + if (imageType === 'Backdrop' || imageType === 'Art' || imageType === 'Thumb' || imageType === 'Logo') { shape = 'backdrop'; - } else if (imageType === "Banner") { + } else if (imageType === 'Banner') { shape = 'banner'; - } else if (imageType === "Disc") { + } else if (imageType === 'Disc') { shape = 'square'; } else { - if (currentItemType === "Episode") { + if (currentItemType === 'Episode') { shape = 'backdrop'; - } else if (currentItemType === "MusicAlbum" || currentItemType === "MusicArtist") { + } else if (currentItemType === 'MusicAlbum' || currentItemType === 'MusicArtist') { shape = 'square'; } else { shape = 'portrait'; @@ -203,9 +203,9 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image html += '
'; if (layoutManager.tv || !appHost.supports('externallinks')) { - html += '
'; + html += '
'; } else { - html += ''; + html += ''; } html += '
'; @@ -241,18 +241,18 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image html += '
'; - if (image.RatingType === "Likes") { - html += image.CommunityRating + (image.CommunityRating === 1 ? " like" : " likes"); + if (image.RatingType === 'Likes') { + html += image.CommunityRating + (image.CommunityRating === 1 ? ' like' : ' likes'); } else { if (image.CommunityRating) { html += image.CommunityRating.toFixed(1); if (image.VoteCount) { - html += ' • ' + image.VoteCount + (image.VoteCount === 1 ? " vote" : " votes"); + html += ' • ' + image.VoteCount + (image.VoteCount === 1 ? ' vote' : ' votes'); } } else { - html += "Unrated"; + html += 'Unrated'; } } @@ -262,7 +262,7 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image if (enableFooterButtons) { html += '
'; - html += ''; + html += ''; html += '
'; } @@ -320,7 +320,7 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image function showEditor(itemId, serverId, itemType) { loading.show(); - require(['text!./imagedownloader.template.html'], function (template) { + require(['text!./imageDownloader.template.html'], function (template) { var apiClient = connectionManager.getApiClient(serverId); @@ -334,7 +334,7 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image if (layoutManager.tv) { dialogOptions.size = 'fullscreen'; } else { - dialogOptions.size = 'fullscreen-border'; + dialogOptions.size = 'small'; } var dlg = dialogHelper.createDialog(dialogOptions); diff --git a/src/components/imagedownloader/imagedownloader.template.html b/src/components/imageDownloader/imageDownloader.template.html similarity index 94% rename from src/components/imagedownloader/imagedownloader.template.html rename to src/components/imageDownloader/imageDownloader.template.html index dec796fb05..b88ea73923 100644 --- a/src/components/imagedownloader/imagedownloader.template.html +++ b/src/components/imageDownloader/imageDownloader.template.html @@ -1,11 +1,11 @@
- +

${Search}

-
+
diff --git a/src/components/imageoptionseditor/imageoptionseditor.js b/src/components/imageOptionsEditor/imageOptionsEditor.js similarity index 54% rename from src/components/imageoptionseditor/imageoptionseditor.js rename to src/components/imageOptionsEditor/imageOptionsEditor.js index c9dc678099..257921dfa2 100644 --- a/src/components/imageoptionseditor/imageoptionseditor.js +++ b/src/components/imageOptionsEditor/imageOptionsEditor.js @@ -1,11 +1,11 @@ -define(["globalize", "dom", "dialogHelper", "emby-checkbox", "emby-select", "emby-input"], function (globalize, dom, dialogHelper) { - "use strict"; +define(['globalize', 'dom', 'dialogHelper', 'emby-checkbox', 'emby-select', 'emby-input'], function (globalize, dom, dialogHelper) { + 'use strict'; function getDefaultImageConfig(itemType, type) { return { Type: type, MinWidth: 0, - Limit: "Primary" === type ? 1 : 0 + Limit: 'Primary' === type ? 1 : 0 }; } @@ -21,27 +21,27 @@ define(["globalize", "dom", "dialogHelper", "emby-checkbox", "emby-select", "emb function setVisibilityOfBackdrops(elem, visible) { if (visible) { - elem.classList.remove("hide"); - elem.querySelector("input").setAttribute("required", "required"); + elem.classList.remove('hide'); + elem.querySelector('input').setAttribute('required', 'required'); } else { - elem.classList.add("hide"); - elem.querySelector("input").setAttribute("required", ""); - elem.querySelector("input").removeAttribute("required"); + elem.classList.add('hide'); + elem.querySelector('input').setAttribute('required', ''); + elem.querySelector('input').removeAttribute('required'); } } function loadValues(context, itemType, options, availableOptions) { var supportedImageTypes = availableOptions.SupportedImageTypes || []; - setVisibilityOfBackdrops(context.querySelector(".backdropFields"), -1 != supportedImageTypes.indexOf("Backdrop")); - setVisibilityOfBackdrops(context.querySelector(".screenshotFields"), -1 != supportedImageTypes.indexOf("Screenshot")); - Array.prototype.forEach.call(context.querySelectorAll(".imageType"), function (i) { - var imageType = i.getAttribute("data-imagetype"); - var container = dom.parentWithTag(i, "LABEL"); + setVisibilityOfBackdrops(context.querySelector('.backdropFields'), -1 != supportedImageTypes.indexOf('Backdrop')); + setVisibilityOfBackdrops(context.querySelector('.screenshotFields'), -1 != supportedImageTypes.indexOf('Screenshot')); + Array.prototype.forEach.call(context.querySelectorAll('.imageType'), function (i) { + var imageType = i.getAttribute('data-imagetype'); + var container = dom.parentWithTag(i, 'LABEL'); if (-1 == supportedImageTypes.indexOf(imageType)) { - container.classList.add("hide"); + container.classList.add('hide'); } else { - container.classList.remove("hide"); + container.classList.remove('hide'); } if (getImageConfig(options, availableOptions, imageType, itemType).Limit) { @@ -50,31 +50,31 @@ define(["globalize", "dom", "dialogHelper", "emby-checkbox", "emby-select", "emb i.checked = false; } }); - var backdropConfig = getImageConfig(options, availableOptions, "Backdrop", itemType); - context.querySelector("#txtMaxBackdrops").value = backdropConfig.Limit; - context.querySelector("#txtMinBackdropDownloadWidth").value = backdropConfig.MinWidth; - var screenshotConfig = getImageConfig(options, availableOptions, "Screenshot", itemType); - context.querySelector("#txtMaxScreenshots").value = screenshotConfig.Limit; - context.querySelector("#txtMinScreenshotDownloadWidth").value = screenshotConfig.MinWidth; + var backdropConfig = getImageConfig(options, availableOptions, 'Backdrop', itemType); + context.querySelector('#txtMaxBackdrops').value = backdropConfig.Limit; + context.querySelector('#txtMinBackdropDownloadWidth').value = backdropConfig.MinWidth; + var screenshotConfig = getImageConfig(options, availableOptions, 'Screenshot', itemType); + context.querySelector('#txtMaxScreenshots').value = screenshotConfig.Limit; + context.querySelector('#txtMinScreenshotDownloadWidth').value = screenshotConfig.MinWidth; } function saveValues(context, options) { - options.ImageOptions = Array.prototype.map.call(context.querySelectorAll(".imageType:not(.hide)"), function (c) { + options.ImageOptions = Array.prototype.map.call(context.querySelectorAll('.imageType:not(.hide)'), function (c) { return { - Type: c.getAttribute("data-imagetype"), + Type: c.getAttribute('data-imagetype'), Limit: c.checked ? 1 : 0, MinWidth: 0 }; }); options.ImageOptions.push({ - Type: "Backdrop", - Limit: context.querySelector("#txtMaxBackdrops").value, - MinWidth: context.querySelector("#txtMinBackdropDownloadWidth").value + Type: 'Backdrop', + Limit: context.querySelector('#txtMaxBackdrops').value, + MinWidth: context.querySelector('#txtMinBackdropDownloadWidth').value }); options.ImageOptions.push({ - Type: "Screenshot", - Limit: context.querySelector("#txtMaxScreenshots").value, - MinWidth: context.querySelector("#txtMinScreenshotDownloadWidth").value + Type: 'Screenshot', + Limit: context.querySelector('#txtMaxScreenshots').value, + MinWidth: context.querySelector('#txtMinScreenshotDownloadWidth').value }); } @@ -82,23 +82,23 @@ define(["globalize", "dom", "dialogHelper", "emby-checkbox", "emby-select", "emb this.show = function (itemType, options, availableOptions) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest(); - xhr.open("GET", "components/imageoptionseditor/imageoptionseditor.template.html", true); + xhr.open('GET', 'components/imageOptionsEditor/imageOptionsEditor.template.html', true); xhr.onload = function (e) { var template = this.response; var dlg = dialogHelper.createDialog({ - size: "medium-tall", + size: 'small', removeOnClose: true, scrollY: false }); - dlg.classList.add("formDialog"); + dlg.classList.add('formDialog'); dlg.innerHTML = globalize.translateDocument(template); - dlg.addEventListener("close", function () { + dlg.addEventListener('close', function () { saveValues(dlg, options); }); loadValues(dlg, itemType, options, availableOptions); dialogHelper.open(dlg).then(resolve, resolve); - dlg.querySelector(".btnCancel").addEventListener("click", function () { + dlg.querySelector('.btnCancel').addEventListener('click', function () { dialogHelper.close(dlg); }); }; diff --git a/src/components/imageoptionseditor/imageoptionseditor.template.html b/src/components/imageOptionsEditor/imageOptionsEditor.template.html similarity index 97% rename from src/components/imageoptionseditor/imageoptionseditor.template.html rename to src/components/imageOptionsEditor/imageOptionsEditor.template.html index 910d8fdd6e..cbbc3b85f5 100644 --- a/src/components/imageoptionseditor/imageoptionseditor.template.html +++ b/src/components/imageOptionsEditor/imageOptionsEditor.template.html @@ -1,5 +1,5 @@
- +

${HeaderImageOptions}

diff --git a/src/components/imageuploader/imageuploader.js b/src/components/imageUploader/imageUploader.js similarity index 91% rename from src/components/imageuploader/imageuploader.js rename to src/components/imageUploader/imageUploader.js index 400646f669..e078a9fa30 100644 --- a/src/components/imageuploader/imageuploader.js +++ b/src/components/imageUploader/imageUploader.js @@ -74,7 +74,7 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', ' return false; } - if (!file.type.startsWith("image/")) { + if (!file.type.startsWith('image/')) { require(['toast'], function (toast) { toast(globalize.translate('MessageImageFileTypeAllowed')); }); @@ -87,9 +87,9 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', ' var dlg = dom.parentWithClass(this, 'dialog'); var imageType = dlg.querySelector('#selectImageType').value; - if (imageType === "None") { - require(["toast"], function(toast) { - toast(globalize.translate("MessageImageTypeNotSelected")); + if (imageType === 'None') { + require(['toast'], function(toast) { + toast(globalize.translate('MessageImageTypeNotSelected')); }); e.preventDefault(); return false; @@ -112,11 +112,11 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', ' page.querySelector('form').addEventListener('submit', onSubmit); - page.querySelector('#uploadImage').addEventListener("change", function () { + page.querySelector('#uploadImage').addEventListener('change', function () { setFiles(page, this.files); }); - page.querySelector('.btnBrowse').addEventListener("click", function () { + page.querySelector('.btnBrowse').addEventListener('click', function () { page.querySelector('#uploadImage').click(); }); } @@ -125,7 +125,7 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', ' options = options || {}; - require(['text!./imageuploader.template.html'], function (template) { + require(['text!./imageUploader.template.html'], function (template) { currentItemId = options.itemId; currentServerId = options.serverId; @@ -137,7 +137,7 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', ' if (layoutManager.tv) { dialogOptions.size = 'fullscreen'; } else { - dialogOptions.size = 'fullscreen-border'; + dialogOptions.size = 'small'; } var dlg = dialogHelper.createDialog(dialogOptions); diff --git a/src/components/imageuploader/imageuploader.template.html b/src/components/imageUploader/imageUploader.template.html similarity index 93% rename from src/components/imageuploader/imageuploader.template.html rename to src/components/imageUploader/imageUploader.template.html index 1b83b50955..9c6a139c04 100644 --- a/src/components/imageuploader/imageuploader.template.html +++ b/src/components/imageUploader/imageUploader.template.html @@ -1,11 +1,11 @@
- +

${HeaderUploadImage}

-
+
@@ -14,7 +14,7 @@

${HeaderAddUpdateImage}

diff --git a/src/components/imageuploader/style.css b/src/components/imageUploader/style.css similarity index 100% rename from src/components/imageuploader/style.css rename to src/components/imageUploader/style.css diff --git a/src/components/imageeditor/imageeditor.js b/src/components/imageeditor/imageeditor.js index 20aebf4305..2927a0b120 100644 --- a/src/components/imageeditor/imageeditor.js +++ b/src/components/imageeditor/imageeditor.js @@ -99,10 +99,10 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', var html = ''; - var cssClass = "card scalableCard imageEditorCard"; + var cssClass = 'card scalableCard imageEditorCard'; var cardBoxCssClass = 'cardBox visualCardBox'; - cssClass += " backdropCard backdropCard-scalable"; + cssClass += ' backdropCard backdropCard-scalable'; if (tagName === 'button') { cssClass += ' btnImageCard'; @@ -132,7 +132,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', var imageUrl = getImageUrl(currentItem, apiClient, image.ImageType, image.ImageIndex, { maxWidth: imageSize }); - html += '
'; + html += '
'; html += '
'; html += '
'; @@ -152,26 +152,26 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', if (enableFooterButtons) { html += '
'; - if (image.ImageType === "Backdrop" || image.ImageType === "Screenshot") { + if (image.ImageType === 'Backdrop' || image.ImageType === 'Screenshot') { if (index > 0) { - html += ''; + html += ''; } else { - html += ''; + html += ''; } if (index < numImages - 1) { - html += ''; + html += ''; } else { - html += ''; + html += ''; } } else { if (imageProviders.length) { - html += ''; + html += ''; } } - html += ''; + html += ''; html += '
'; } @@ -251,7 +251,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', function renderStandardImages(page, apiClient, item, imageInfos, imageProviders) { var images = imageInfos.filter(function (i) { - return i.ImageType !== "Screenshot" && i.ImageType !== "Backdrop" && i.ImageType !== "Chapter"; + return i.ImageType !== 'Screenshot' && i.ImageType !== 'Backdrop' && i.ImageType !== 'Chapter'; }); renderImages(page, item, apiClient, images, imageProviders, page.querySelector('#images')); @@ -260,7 +260,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', function renderBackdrops(page, apiClient, item, imageInfos, imageProviders) { var images = imageInfos.filter(function (i) { - return i.ImageType === "Backdrop"; + return i.ImageType === 'Backdrop'; }).sort(function (a, b) { return a.ImageIndex - b.ImageIndex; @@ -277,7 +277,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', function renderScreenshots(page, apiClient, item, imageInfos, imageProviders) { var images = imageInfos.filter(function (i) { - return i.ImageType === "Screenshot"; + return i.ImageType === 'Screenshot'; }).sort(function (a, b) { return a.ImageIndex - b.ImageIndex; @@ -425,7 +425,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', addListeners(context, 'btnDeleteImage', 'click', function () { var type = this.getAttribute('data-imagetype'); var index = this.getAttribute('data-index'); - index = index === "null" ? null : parseInt(index); + index = index === 'null' ? null : parseInt(index); var apiClient = connectionManager.getApiClient(currentItem.ServerId); deleteImage(context, currentItem.Id, type, index, apiClient, true); }); @@ -457,7 +457,7 @@ define(['dialogHelper', 'connectionManager', 'loading', 'dom', 'layoutManager', if (layoutManager.tv) { dialogOptions.size = 'fullscreen'; } else { - dialogOptions.size = 'fullscreen-border'; + dialogOptions.size = 'small'; } var dlg = dialogHelper.createDialog(dialogOptions); diff --git a/src/components/imageeditor/imageeditor.template.html b/src/components/imageeditor/imageeditor.template.html index 37dce874c0..ce24e724bd 100644 --- a/src/components/imageeditor/imageeditor.template.html +++ b/src/components/imageeditor/imageeditor.template.html @@ -1,21 +1,21 @@
- +

${HeaderEditImages}

-
+

${Images}

@@ -27,10 +27,10 @@

${Backdrops}

@@ -42,10 +42,10 @@

${Screenshots}

diff --git a/src/components/images/imageFetcher.js b/src/components/images/imageFetcher.js deleted file mode 100644 index 1e13cebc77..0000000000 --- a/src/components/images/imageFetcher.js +++ /dev/null @@ -1,38 +0,0 @@ -define(['dom'], function (dom) { - 'use strict'; - - function loadImage(elem, url) { - - if (!elem) { - return Promise.reject('elem cannot be null'); - } - - if (elem.tagName !== "IMG") { - - elem.style.backgroundImage = "url('" + url + "')"; - return Promise.resolve(); - - //return loadImageIntoImg(document.createElement('img'), url).then(function () { - // elem.style.backgroundImage = "url('" + url + "')"; - // return Promise.resolve(); - //}); - - } - return loadImageIntoImg(elem, url); - } - - function loadImageIntoImg(elem, url) { - return new Promise(function (resolve, reject) { - - dom.addEventListener(elem, 'load', resolve, { - once: true - }); - elem.setAttribute("src", url); - }); - } - - return { - loadImage: loadImage - }; - -}); diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index 764be06fd1..f7183515c5 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -1,54 +1,150 @@ -define(['lazyLoader', 'imageFetcher', 'layoutManager', 'browser', 'appSettings', 'userSettings', 'require', 'css!./style'], function (lazyLoader, imageFetcher, layoutManager, browser, appSettings, userSettings, require) { - 'use strict'; - - var requestIdleCallback = window.requestIdleCallback || function (fn) { - fn(); - }; - - var self = {}; - - function fillImage(elem, source, enableEffects) { - - if (!elem) { - throw new Error('elem cannot be null'); - } - - if (!source) { - source = elem.getAttribute('data-src'); - } +import * as lazyLoader from 'lazyLoader'; +import * as userSettings from 'userSettings'; +import * as blurhash from 'blurhash'; +import 'css!./style'; +/* eslint-disable indent */ + export function lazyImage(elem, source = elem.getAttribute('data-src')) { if (!source) { return; } - fillImageElement(elem, source, enableEffects); + fillImageElement(elem, source); } - function fillImageElement(elem, source, enableEffects) { - imageFetcher.loadImage(elem, source).then(function () { + async function itemBlurhashing(target, blurhashstr) { + if (blurhash.isBlurhashValid(blurhashstr)) { + // Although the default values recommended by Blurhash developers is 32x32, a size of 18x18 seems to be the sweet spot for us, + // improving the performance and reducing the memory usage, while retaining almost full blur quality. + // Lower values had more visible pixelation + let width = 18; + let height = 18; + let pixels; + try { + pixels = blurhash.decode(blurhashstr, width, height); + } catch (err) { + console.error('Blurhash decode error: ', err); + target.classList.add('non-blurhashable'); + return; + } + let canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + let ctx = canvas.getContext('2d'); + let imgData = ctx.createImageData(width, height); - if (enableEffects !== false) { - fadeIn(elem); + imgData.data.set(pixels); + ctx.putImageData(imgData, 0, 0); + + let child = target.appendChild(canvas); + child.classList.add('blurhash-canvas'); + child.style.opacity = 1; + if (userSettings.enableFastFadein()) { + child.classList.add('lazy-blurhash-fadein-fast'); + } else { + child.classList.add('lazy-blurhash-fadein'); } - elem.removeAttribute("data-src"); - }); - } - - function fadeIn(elem) { - if (userSettings.enableFastFadein()) { - elem.classList.add('lazy-image-fadein-fast'); - } else { - elem.classList.add('lazy-image-fadein'); + target.classList.add('blurhashed'); + target.removeAttribute('data-blurhash'); } } - function lazyChildren(elem) { + function switchCanvas(elem) { + let child = elem.getElementsByClassName('blurhash-canvas')[0]; + if (child) { + child.style.opacity = elem.getAttribute('data-src') ? 1 : 0; + } + } + export function fillImage(entry) { + if (!entry) { + throw new Error('entry cannot be null'); + } + let target = entry.target; + var source = undefined; + + if (target) { + source = target.getAttribute('data-src'); + var blurhashstr = target.getAttribute('data-blurhash'); + } else { + source = entry; + } + + if (userSettings.enableBlurhash()) { + if (!target.classList.contains('blurhashed', 'non-blurhashable') && blurhashstr) { + itemBlurhashing(target, blurhashstr); + } else if (!blurhashstr && !target.classList.contains('blurhashed')) { + target.classList.add('non-blurhashable'); + } + } + + if (entry.intersectionRatio > 0) { + if (source) fillImageElement(target, source); + } else if (!source) { + emptyImageElement(target); + } + } + + function fillImageElement(elem, url) { + if (url === undefined) { + throw new TypeError('url cannot be undefined'); + } + + let preloaderImg = new Image(); + preloaderImg.src = url; + + // This is necessary here, so changing blurhash settings without reloading the page works + if (!userSettings.enableBlurhash() || elem.classList.contains('non-blurhashable')) { + elem.classList.add('lazy-hidden'); + } + + preloaderImg.addEventListener('load', () => { + if (elem.tagName !== 'IMG') { + elem.style.backgroundImage = "url('" + url + "')"; + } else { + elem.setAttribute('src', url); + } + elem.removeAttribute('data-src'); + + if (elem.classList.contains('non-blurhashable') || !userSettings.enableBlurhash()) { + elem.classList.remove('lazy-hidden'); + if (userSettings.enableFastFadein()) { + elem.classList.add('lazy-image-fadein-fast'); + } else { + elem.classList.add('lazy-image-fadein'); + } + } else { + switchCanvas(elem); + } + }); + } + + function emptyImageElement(elem) { + var url; + + if (elem.tagName !== 'IMG') { + url = elem.style.backgroundImage.slice(4, -1).replace(/"/g, ''); + elem.style.backgroundImage = 'none'; + } else { + url = elem.getAttribute('src'); + elem.setAttribute('src', ''); + } + elem.setAttribute('data-src', url); + + if (elem.classList.contains('non-blurhashable') || !userSettings.enableBlurhash()) { + elem.classList.remove('lazy-image-fadein-fast', 'lazy-image-fadein'); + elem.classList.add('lazy-hidden'); + } else { + switchCanvas(elem); + } + } + + export function lazyChildren(elem) { lazyLoader.lazyChildren(elem, fillImage); } - function getPrimaryImageAspectRatio(items) { + export function getPrimaryImageAspectRatio(items) { var values = []; @@ -108,7 +204,7 @@ define(['lazyLoader', 'imageFetcher', 'layoutManager', 'browser', 'appSettings', return result; } - function fillImages(elems) { + export function fillImages(elems) { for (var i = 0, length = elems.length; i < length; i++) { var elem = elems[0]; @@ -116,10 +212,11 @@ define(['lazyLoader', 'imageFetcher', 'layoutManager', 'browser', 'appSettings', } } - self.fillImages = fillImages; - self.lazyImage = fillImage; - self.lazyChildren = lazyChildren; - self.getPrimaryImageAspectRatio = getPrimaryImageAspectRatio; - - return self; -}); +/* eslint-enable indent */ +export default { + fillImages: fillImages, + fillImage: fillImage, + lazyImage: lazyImage, + lazyChildren: lazyChildren, + getPrimaryImageAspectRatio: getPrimaryImageAspectRatio +}; diff --git a/src/components/images/style.css b/src/components/images/style.css index 2836dd0159..a709f732c5 100644 --- a/src/components/images/style.css +++ b/src/components/images/style.css @@ -1,44 +1,32 @@ .lazy-image-fadein { - opacity: 0; - animation: lazy-image-fadein 330ms ease-in normal both; - -webkit-animation-duration: 0.8s; - -moz-animation-duration: 0.8s; - -o-animation-duration: 0.8s; - animation-duration: 0.8s; - -webkit-animation-name: popInAnimation; - -moz-animation-name: popInAnimation; - -o-animation-name: popInAnimation; - animation-name: popInAnimation; - -webkit-animation-fill-mode: forwards; - -moz-animation-fill-mode: forwards; - -o-animation-fill-mode: forwards; - animation-fill-mode: forwards; - -webkit-animation-timing-function: cubic-bezier(0, 0, 0.5, 1); - -moz-animation-timing-function: cubic-bezier(0, 0, 0.5, 1); - -o-animation-timing-function: cubic-bezier(0, 0, 0.5, 1); - animation-timing-function: cubic-bezier(0, 0, 0.5, 1); + opacity: 1; + transition: opacity 0.7s; } .lazy-image-fadein-fast { - animation: lazy-image-fadein 160ms ease-in normal both; + opacity: 1; + transition: opacity 0.2s; } -@keyframes lazy-image-fadein { - from { - opacity: 0; - } - - to { - opacity: 1; - } +.lazy-hidden { + opacity: 0; } -@keyframes popInAnimation { - 0% { - opacity: 0; - } - - 100% { - opacity: 1; - } +.lazy-blurhash-fadein-fast { + transition: opacity 0.2s; +} + +.lazy-blurhash-fadein { + transition: opacity 0.7s; +} + +.blurhash-canvas { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100; } diff --git a/src/components/indicators/indicators.js b/src/components/indicators/indicators.js index e41ccb9775..604f480f1d 100644 --- a/src/components/indicators/indicators.js +++ b/src/components/indicators/indicators.js @@ -1,237 +1,183 @@ -define(['datetime', 'itemHelper', 'css!./indicators.css', 'material-icons'], function (datetime, itemHelper) { - 'use strict'; +import datetime from 'datetime'; +import itemHelper from 'itemHelper'; +import 'emby-progressbar'; +import 'css!./indicators.css'; +import 'material-icons'; - function enableProgressIndicator(item) { - if (item.MediaType === 'Video') { - if (item.Type !== 'TvChannel') { - return true; - } - } - - if (item.Type === 'AudioBook' || item.Type === 'AudioPodcast') { - return true; - } - - return false; +export function enableProgressIndicator(item) { + if (item.MediaType === 'Video' && item.Type !== 'TvChannel') { + return true; } - function getProgressHtml(pct, options) { - var containerClass = 'itemProgressBar'; - if (options) { - if (options.containerClass) { - containerClass += ' ' + options.containerClass; - } - } - - return '
'; + if (item.Type === 'AudioBook' || item.Type === 'AudioPodcast') { + return true; } - function getAutoTimeProgressHtml(pct, options, isRecording, start, end) { - var containerClass = 'itemProgressBar'; - if (options) { - if (options.containerClass) { - containerClass += ' ' + options.containerClass; - } - } + return false; +} - var foregroundClass = 'itemProgressBarForeground'; - if (isRecording) { - foregroundClass += ' itemProgressBarForeground-recording'; - } - - return '
'; +export function getProgressHtml(pct, options) { + let containerClass = 'itemProgressBar'; + if (options && options.containerClass) { + containerClass += ' ' + options.containerClass; } - function getProgressBarHtml(item, options) { - var pct; - if (enableProgressIndicator(item) && item.Type !== "Recording") { - var userData = options ? (options.userData || item.UserData) : item.UserData; - if (userData) { - pct = userData.PlayedPercentage; - if (pct && pct < 100) { - return getProgressHtml(pct, options); - } + return '
'; +} + +function getAutoTimeProgressHtml(pct, options, isRecording, start, end) { + let containerClass = 'itemProgressBar'; + if (options && options.containerClass) { + containerClass += ' ' + options.containerClass; + } + + let foregroundClass = 'itemProgressBarForeground'; + if (isRecording) { + foregroundClass += ' itemProgressBarForeground-recording'; + } + + return '
'; +} + +export function getProgressBarHtml(item, options) { + let pct; + if (enableProgressIndicator(item) && item.Type !== 'Recording') { + const userData = options && options.userData ? options.userData : item.UserData; + + if (userData) { + pct = userData.PlayedPercentage; + if (pct && pct < 100) { + return getProgressHtml(pct, options); } } + } - if ((item.Type === 'Program' || item.Type === 'Timer' || item.Type === 'Recording') && item.StartDate && item.EndDate) { - var startDate = 0; - var endDate = 1; + if ((item.Type === 'Program' || item.Type === 'Timer' || item.Type === 'Recording') && item.StartDate && item.EndDate) { + let startDate = 0; + let endDate = 1; + try { + startDate = datetime.parseISO8601Date(item.StartDate).getTime(); + endDate = datetime.parseISO8601Date(item.EndDate).getTime(); + } catch (err) { + console.error(err); + } + + const now = new Date().getTime(); + const total = endDate - startDate; + pct = 100 * ((now - startDate) / total); + + if (pct > 0 && pct < 100) { + const isRecording = item.Type === 'Timer' || item.Type === 'Recording' || item.TimerId; + return getAutoTimeProgressHtml(pct, options, isRecording, startDate, endDate); + } + } + + return ''; +} + +export function enablePlayedIndicator(item) { + return itemHelper.canMarkPlayed(item); +} + +export function getPlayedIndicatorHtml(item) { + if (enablePlayedIndicator(item)) { + let userData = item.UserData || {}; + if (userData.UnplayedItemCount) { + return '
' + userData.UnplayedItemCount + '
'; + } + + if (userData.PlayedPercentage && userData.PlayedPercentage >= 100 || (userData.Played)) { + return '
'; + } + } + + return ''; +} + +export function getChildCountIndicatorHtml(item, options) { + const minCount = options && options.minCount ? options.minCount : 0; + + if (item.ChildCount && item.ChildCount > minCount) { + return '
' + item.ChildCount + '
'; + } + + return ''; +} + +export function getTimerIndicator(item) { + let status; + + if (item.Type === 'SeriesTimer') { + return ''; + } else if (item.TimerId || item.SeriesTimerId) { + status = item.Status || 'Cancelled'; + } else if (item.Type === 'Timer') { + status = item.Status; + } else { + return ''; + } + + if (item.SeriesTimerId) { + if (status !== 'Cancelled') { + return ''; + } + + return ''; + } + + return ''; +} + +export function getSyncIndicator(item) { + if (item.SyncPercent === 100) { + return '
'; + } else if (item.SyncPercent != null) { + return '
'; + } + + return ''; +} + +export function getTypeIndicator(item) { + const iconT = { + 'Video' : 'videocam', + 'Folder' : 'folder', + 'PhotoAlbum' : 'photo_album', + 'Photo' : 'photo' + }; + + const icon = iconT[item.Type]; + return icon ? '
' : ''; +} + +export function getMissingIndicator(item) { + if (item.Type === 'Episode' && item.LocationType === 'Virtual') { + if (item.PremiereDate) { try { - startDate = datetime.parseISO8601Date(item.StartDate).getTime(); - endDate = datetime.parseISO8601Date(item.EndDate).getTime(); + const premiereDate = datetime.parseISO8601Date(item.PremiereDate).getTime(); + if (premiereDate > new Date().getTime()) { + return '
Unaired
'; + } } catch (err) { console.error(err); } - - var now = new Date().getTime(); - var total = endDate - startDate; - pct = 100 * ((now - startDate) / total); - - if (pct > 0 && pct < 100) { - var isRecording = item.Type === 'Timer' || item.Type === 'Recording' || item.TimerId; - return getAutoTimeProgressHtml(pct, options, isRecording, startDate, endDate); - } } - - return ''; + return '
Missing
'; } - function enablePlayedIndicator(item) { - return itemHelper.canMarkPlayed(item); - } + return ''; +} - function getPlayedIndicator(item) { - if (enablePlayedIndicator(item)) { - var userData = item.UserData || {}; - if (userData.UnplayedItemCount) { - return '
' + userData.UnplayedItemCount + '
'; - } - - if (userData.PlayedPercentage && userData.PlayedPercentage >= 100 || (userData.Played)) { - return '
check
'; - } - } - - return ''; - } - - function getCountIndicatorHtml(count) { - return '
' + count + '
'; - } - - function getChildCountIndicatorHtml(item, options) { - var minCount = 0; - if (options) { - minCount = options.minCount || minCount; - } - - if (item.ChildCount && item.ChildCount > minCount) { - return getCountIndicatorHtml(item.ChildCount); - } - - return ''; - } - - function getTimerIndicator(item) { - var status; - - if (item.Type === 'SeriesTimer') { - return ''; - } else if (item.TimerId || item.SeriesTimerId) { - status = item.Status || 'Cancelled'; - } else if (item.Type === 'Timer') { - status = item.Status; - } else { - return ''; - } - - if (item.SeriesTimerId) { - if (status !== 'Cancelled') { - return ''; - } - - return ''; - } - - return ''; - } - - function getSyncIndicator(item) { - if (item.SyncPercent === 100) { - return '
'; - } else if (item.SyncPercent != null) { - return '
'; - } - - return ''; - } - - function getTypeIndicator(item) { - if (item.Type === 'Video') { - return '
videocam
'; - } - if (item.Type === 'Folder') { - return '
folder
'; - } - if (item.Type === 'PhotoAlbum') { - return '
'; - } - if (item.Type === 'Photo') { - return '
photo
'; - } - - return ''; - } - - function getMissingIndicator(item) { - if (item.Type === 'Episode' && item.LocationType === 'Virtual') { - if (item.PremiereDate) { - try { - var premiereDate = datetime.parseISO8601Date(item.PremiereDate).getTime(); - if (premiereDate > new Date().getTime()) { - return '
Unaired
'; - } - } catch (err) { - console.error(err); - } - } - return '
Missing
'; - } - - return ''; - } - - var ProgressBarPrototype = Object.create(HTMLDivElement.prototype); - - function onAutoTimeProgress() { - var start = parseInt(this.getAttribute('data-starttime')); - var end = parseInt(this.getAttribute('data-endtime')); - - var now = new Date().getTime(); - var total = end - start; - var pct = 100 * ((now - start) / total); - - pct = Math.min(100, pct); - pct = Math.max(0, pct); - - var itemProgressBarForeground = this.querySelector('.itemProgressBarForeground'); - itemProgressBarForeground.style.width = pct + '%'; - } - - ProgressBarPrototype.attachedCallback = function () { - if (this.timeInterval) { - clearInterval(this.timeInterval); - } - - if (this.getAttribute('data-automode') === 'time') { - this.timeInterval = setInterval(onAutoTimeProgress.bind(this), 60000); - } - }; - - ProgressBarPrototype.detachedCallback = function () { - if (this.timeInterval) { - clearInterval(this.timeInterval); - this.timeInterval = null; - } - }; - - document.registerElement('emby-progressbar', { - prototype: ProgressBarPrototype, - extends: 'div' - }); - - return { - getProgressHtml: getProgressHtml, - getProgressBarHtml: getProgressBarHtml, - getPlayedIndicatorHtml: getPlayedIndicator, - getChildCountIndicatorHtml: getChildCountIndicatorHtml, - enableProgressIndicator: enableProgressIndicator, - getTimerIndicator: getTimerIndicator, - enablePlayedIndicator: enablePlayedIndicator, - getSyncIndicator: getSyncIndicator, - getTypeIndicator: getTypeIndicator, - getMissingIndicator: getMissingIndicator - }; -}); +export default { + getProgressHtml: getProgressHtml, + getProgressBarHtml: getProgressBarHtml, + getPlayedIndicatorHtml: getPlayedIndicatorHtml, + getChildCountIndicatorHtml: getChildCountIndicatorHtml, + enableProgressIndicator: enableProgressIndicator, + getTimerIndicator: getTimerIndicator, + enablePlayedIndicator: enablePlayedIndicator, + getSyncIndicator: getSyncIndicator, + getTypeIndicator: getTypeIndicator, + getMissingIndicator: getMissingIndicator +}; diff --git a/src/components/itemcontextmenu.js b/src/components/itemContextMenu.js similarity index 59% rename from src/components/itemcontextmenu.js rename to src/components/itemContextMenu.js index bdbcfc782b..f258f5fe4b 100644 --- a/src/components/itemcontextmenu.js +++ b/src/components/itemContextMenu.js @@ -1,5 +1,5 @@ -define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", "playbackManager", "loading", "appSettings", "browser", "actionsheet"], function (appHost, globalize, connectionManager, itemHelper, appRouter, playbackManager, loading, appSettings, browser, actionsheet) { - "use strict"; +define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'appRouter', 'playbackManager', 'loading', 'appSettings', 'browser', 'actionsheet'], function (appHost, globalize, connectionManager, itemHelper, appRouter, playbackManager, loading, appSettings, browser, actionsheet) { + 'use strict'; function getCommands(options) { var item = options.item; @@ -10,20 +10,20 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", var commands = []; - if (canPlay && item.MediaType !== "Photo") { + if (canPlay && item.MediaType !== 'Photo') { if (options.play !== false) { commands.push({ - name: globalize.translate("Play"), - id: "resume", - icon: "" + name: globalize.translate('Play'), + id: 'resume', + icon: 'play_arrow' }); } - if (options.playAllFromHere && item.Type !== "Program" && item.Type !== "TvChannel") { + if (options.playAllFromHere && item.Type !== 'Program' && item.Type !== 'TvChannel') { commands.push({ - name: globalize.translate("PlayAllFromHere"), - id: "playallfromhere", - icon: "" + name: globalize.translate('PlayAllFromHere'), + id: 'playallfromhere', + icon: 'play_arrow' }); } } @@ -31,17 +31,17 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", if (playbackManager.canQueue(item)) { if (options.queue !== false) { commands.push({ - name: globalize.translate("AddToPlayQueue"), - id: "queue", - icon: "playlist_add" + name: globalize.translate('AddToPlayQueue'), + id: 'queue', + icon: 'playlist_add' }); } if (options.queue !== false) { commands.push({ - name: globalize.translate("PlayNext"), - id: "queuenext", - icon: "playlist_add" + name: globalize.translate('PlayNext'), + id: 'queuenext', + icon: 'playlist_add' }); } @@ -53,24 +53,24 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", //} } - if (item.IsFolder || item.Type === "MusicArtist" || item.Type === "MusicGenre") { - if (item.CollectionType !== "livetv") { + if (item.IsFolder || item.Type === 'MusicArtist' || item.Type === 'MusicGenre') { + if (item.CollectionType !== 'livetv') { if (options.shuffle !== false) { commands.push({ - name: globalize.translate("Shuffle"), - id: "shuffle", - icon: "shuffle" + name: globalize.translate('Shuffle'), + id: 'shuffle', + icon: 'shuffle' }); } } } - if (item.MediaType === "Audio" || item.Type === "MusicAlbum" || item.Type === "MusicArtist" || item.Type === "MusicGenre") { + if (item.MediaType === 'Audio' || item.Type === 'MusicAlbum' || item.Type === 'MusicArtist' || item.Type === 'MusicGenre') { if (options.instantMix !== false && !itemHelper.isLocalItem(item)) { commands.push({ - name: globalize.translate("InstantMix"), - id: "instantmix", - icon: "explore" + name: globalize.translate('InstantMix'), + id: 'instantmix', + icon: 'explore' }); } } @@ -84,74 +84,74 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", if (!restrictOptions) { if (itemHelper.supportsAddingToCollection(item)) { commands.push({ - name: globalize.translate("AddToCollection"), - id: "addtocollection", - icon: "playlist_add" + name: globalize.translate('AddToCollection'), + id: 'addtocollection', + icon: 'playlist_add' }); } - if (itemHelper.supportsAddingToPlaylist(item)) { + if (itemHelper.supportsAddingToPlaylist(item) && options.playlist !== false) { commands.push({ - name: globalize.translate("AddToPlaylist"), - id: "addtoplaylist", - icon: "playlist_add" + name: globalize.translate('AddToPlaylist'), + id: 'addtoplaylist', + icon: 'playlist_add' }); } } - if ((item.Type === "Timer") && user.Policy.EnableLiveTvManagement && options.cancelTimer !== false) { + if ((item.Type === 'Timer') && user.Policy.EnableLiveTvManagement && options.cancelTimer !== false) { commands.push({ - name: globalize.translate("CancelRecording"), - id: "canceltimer", - icon: "cancel" + name: globalize.translate('CancelRecording'), + id: 'canceltimer', + icon: 'cancel' }); } - if ((item.Type === "Recording" && item.Status === "InProgress") && user.Policy.EnableLiveTvManagement && options.cancelTimer !== false) { + if ((item.Type === 'Recording' && item.Status === 'InProgress') && user.Policy.EnableLiveTvManagement && options.cancelTimer !== false) { commands.push({ - name: globalize.translate("CancelRecording"), - id: "canceltimer", - icon: "cancel" + name: globalize.translate('CancelRecording'), + id: 'canceltimer', + icon: 'cancel' }); } - if ((item.Type === "SeriesTimer") && user.Policy.EnableLiveTvManagement && options.cancelTimer !== false) { + if ((item.Type === 'SeriesTimer') && user.Policy.EnableLiveTvManagement && options.cancelTimer !== false) { commands.push({ - name: globalize.translate("CancelSeries"), - id: "cancelseriestimer", - icon: "cancel" + name: globalize.translate('CancelSeries'), + id: 'cancelseriestimer', + icon: 'cancel' }); } if (item.CanDelete && options.deleteItem !== false) { - if (item.Type === "Playlist" || item.Type === "BoxSet") { + if (item.Type === 'Playlist' || item.Type === 'BoxSet') { commands.push({ - name: globalize.translate("Delete"), - id: "delete", - icon: "delete" + name: globalize.translate('Delete'), + id: 'delete', + icon: 'delete' }); } else { commands.push({ - name: globalize.translate("DeleteMedia"), - id: "delete", - icon: "delete" + name: globalize.translate('DeleteMedia'), + id: 'delete', + icon: 'delete' }); } } // Books are promoted to major download Button and therefor excluded in the context menu - if ((item.CanDownload && appHost.supports("filedownload")) && item.Type !== "Book") { + if ((item.CanDownload && appHost.supports('filedownload')) && item.Type !== 'Book') { commands.push({ - name: globalize.translate("Download"), - id: "download", - icon: "file_download" + name: globalize.translate('Download'), + id: 'download', + icon: 'file_download' }); commands.push({ - name: globalize.translate("CopyStreamURL"), - id: "copy-stream", - icon: "content_copy" + name: globalize.translate('CopyStreamURL'), + id: 'copy-stream', + icon: 'content_copy' }); } @@ -163,12 +163,12 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", var canEdit = itemHelper.canEdit(user, item); if (canEdit) { - if (options.edit !== false && item.Type !== "SeriesTimer") { - var text = (item.Type === "Timer" || item.Type === "SeriesTimer") ? globalize.translate("Edit") : globalize.translate("EditMetadata"); + if (options.edit !== false && item.Type !== 'SeriesTimer') { + var text = (item.Type === 'Timer' || item.Type === 'SeriesTimer') ? globalize.translate('Edit') : globalize.translate('EditMetadata'); commands.push({ name: text, - id: "edit", - icon: "edit" + id: 'edit', + icon: 'edit' }); } } @@ -176,20 +176,20 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", if (itemHelper.canEditImages(user, item)) { if (options.editImages !== false) { commands.push({ - name: globalize.translate("EditImages"), - id: "editimages", - icon: "image" + name: globalize.translate('EditImages'), + id: 'editimages', + icon: 'image' }); } } if (canEdit) { - if (item.MediaType === "Video" && item.Type !== "TvChannel" && item.Type !== "Program" && item.LocationType !== "Virtual" && !(item.Type === "Recording" && item.Status !== "Completed")) { + if (item.MediaType === 'Video' && item.Type !== 'TvChannel' && item.Type !== 'Program' && item.LocationType !== 'Virtual' && !(item.Type === 'Recording' && item.Status !== 'Completed')) { if (options.editSubtitles !== false) { commands.push({ - name: globalize.translate("EditSubtitles"), - id: "editsubtitles", - icon: "closed_caption" + name: globalize.translate('EditSubtitles'), + id: 'editsubtitles', + icon: 'closed_caption' }); } } @@ -198,9 +198,9 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", if (options.identify !== false) { if (itemHelper.canIdentify(user, item)) { commands.push({ - name: globalize.translate("Identify"), - id: "identify", - icon: "edit" + name: globalize.translate('Identify'), + id: 'identify', + icon: 'edit' }); } } @@ -208,54 +208,54 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", if (item.MediaSources) { if (options.moremediainfo !== false) { commands.push({ - name: globalize.translate("MoreMediaInfo"), - id: "moremediainfo", - icon: "info" + name: globalize.translate('MoreMediaInfo'), + id: 'moremediainfo', + icon: 'info' }); } } - if (item.Type === "Program" && options.record !== false) { + if (item.Type === 'Program' && options.record !== false) { if (item.TimerId) { commands.push({ - name: Globalize.translate("ManageRecording"), - id: "record", - icon: "fiber_manual_record" + name: globalize.translate('ManageRecording'), + id: 'record', + icon: 'fiber_manual_record' }); } } - if (item.Type === "Program" && options.record !== false) { + if (item.Type === 'Program' && options.record !== false) { if (!item.TimerId) { commands.push({ - name: Globalize.translate("Record"), - id: "record", - icon: "fiber_manual_record" + name: globalize.translate('Record'), + id: 'record', + icon: 'fiber_manual_record' }); } } if (itemHelper.canRefreshMetadata(item, user)) { commands.push({ - name: globalize.translate("RefreshMetadata"), - id: "refresh", - icon: "refresh" + name: globalize.translate('RefreshMetadata'), + id: 'refresh', + icon: 'refresh' }); } if (item.PlaylistItemId && options.playlistId) { commands.push({ - name: globalize.translate("RemoveFromPlaylist"), - id: "removefromplaylist", - icon: "remove" + name: globalize.translate('RemoveFromPlaylist'), + id: 'removefromplaylist', + icon: 'remove' }); } if (options.collectionId) { commands.push({ - name: globalize.translate("RemoveFromCollection"), - id: "removefromcollection", - icon: "remove" + name: globalize.translate('RemoveFromCollection'), + id: 'removefromcollection', + icon: 'remove' }); } @@ -263,9 +263,9 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", if (options.share === true) { if (itemHelper.canShare(item, user)) { commands.push({ - name: globalize.translate("Share"), - id: "share", - icon: "share" + name: globalize.translate('Share'), + id: 'share', + icon: 'share' }); } } @@ -274,26 +274,26 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", if (options.sync !== false) { if (itemHelper.canSync(user, item)) { commands.push({ - name: globalize.translate("Sync"), - id: "sync", - icon: "sync" + name: globalize.translate('Sync'), + id: 'sync', + icon: 'sync' }); } } - if (options.openAlbum !== false && item.AlbumId && item.MediaType !== "Photo") { + if (options.openAlbum !== false && item.AlbumId && item.MediaType !== 'Photo') { commands.push({ - name: Globalize.translate("ViewAlbum"), - id: "album", - icon: "album" + name: globalize.translate('ViewAlbum'), + id: 'album', + icon: 'album' }); } if (options.openArtist !== false && item.ArtistItems && item.ArtistItems.length) { commands.push({ - name: Globalize.translate("ViewArtist"), - id: "artist", - icon: "person" + name: globalize.translate('ViewArtist'), + id: 'artist', + icon: 'person' }); } @@ -317,173 +317,181 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", return new Promise(function (resolve, reject) { switch (id) { - case "addtocollection": - require(["collectionEditor"], function (collectionEditor) { + case 'addtocollection': + require(['collectionEditor'], function (collectionEditor) { new collectionEditor().show({ items: [itemId], serverId: serverId }).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "addtoplaylist": - require(["playlistEditor"], function (playlistEditor) { + case 'addtoplaylist': + require(['playlistEditor'], function (playlistEditor) { new playlistEditor().show({ items: [itemId], serverId: serverId }).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "download": - require(["fileDownloader"], function (fileDownloader) { + case 'download': + require(['fileDownloader'], function (fileDownloader) { var downloadHref = apiClient.getItemDownloadUrl(itemId); fileDownloader.download([{ url: downloadHref, itemId: itemId, - serverId: serverId + serverId: serverId, + title: item.Name, + filename: item.Path.replace(/^.*[\\\/]/, '') }]); getResolveFunction(getResolveFunction(resolve, id), id)(); }); break; - case "copy-stream": + case 'copy-stream': var downloadHref = apiClient.getItemDownloadUrl(itemId); var textAreaCopy = function () { - var textArea = document.createElement("textarea"); + var textArea = document.createElement('textarea'); textArea.value = downloadHref; document.body.appendChild(textArea); textArea.focus(); textArea.select(); - if (document.execCommand("copy")) { - require(["toast"], function (toast) { - toast(globalize.translate("CopyStreamURLSuccess")); + + if (document.execCommand('copy')) { + require(['toast'], function (toast) { + toast(globalize.translate('CopyStreamURLSuccess')); }); } else { - prompt(globalize.translate("CopyStreamURL"), downloadHref); + prompt(globalize.translate('CopyStreamURL'), downloadHref); } document.body.removeChild(textArea); }; + + /* eslint-disable-next-line compat/compat */ if (navigator.clipboard === undefined) { textAreaCopy(); } else { + /* eslint-disable-next-line compat/compat */ navigator.clipboard.writeText(downloadHref).then(function () { - require(["toast"], function (toast) { - toast(globalize.translate("CopyStreamURLSuccess")); + require(['toast'], function (toast) { + toast(globalize.translate('CopyStreamURLSuccess')); }); - }, textAreaCopy); + }).catch(function () { + textAreaCopy(); + }); } getResolveFunction(resolve, id)(); break; - case "editsubtitles": - require(["subtitleEditor"], function (subtitleEditor) { + case 'editsubtitles': + require(['subtitleEditor'], function (subtitleEditor) { subtitleEditor.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "edit": + case 'edit': editItem(apiClient, item).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); break; - case "editimages": - require(["imageEditor"], function (imageEditor) { + case 'editimages': + require(['imageEditor'], function (imageEditor) { imageEditor.show({ itemId: itemId, serverId: serverId }).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "identify": - require(["itemIdentifier"], function (itemIdentifier) { + case 'identify': + require(['itemIdentifier'], function (itemIdentifier) { itemIdentifier.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "moremediainfo": - require(["itemMediaInfo"], function (itemMediaInfo) { + case 'moremediainfo': + require(['itemMediaInfo'], function (itemMediaInfo) { itemMediaInfo.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "refresh": + case 'refresh': refresh(apiClient, item); getResolveFunction(resolve, id)(); break; - case "open": + case 'open': appRouter.showItem(item); getResolveFunction(resolve, id)(); break; - case "play": + case 'play': play(item, false); getResolveFunction(resolve, id)(); break; - case "resume": + case 'resume': play(item, true); getResolveFunction(resolve, id)(); break; - case "queue": + case 'queue': play(item, false, true); getResolveFunction(resolve, id)(); break; - case "queuenext": + case 'queuenext': play(item, false, true, true); getResolveFunction(resolve, id)(); break; - case "record": - require(["recordingCreator"], function (recordingCreator) { + case 'record': + require(['recordingCreator'], function (recordingCreator) { recordingCreator.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; - case "shuffle": + case 'shuffle': playbackManager.shuffle(item); getResolveFunction(resolve, id)(); break; - case "instantmix": + case 'instantmix': playbackManager.instantMix(item); getResolveFunction(resolve, id)(); break; - case "delete": + case 'delete': deleteItem(apiClient, item).then(getResolveFunction(resolve, id, true, true), getResolveFunction(resolve, id)); break; - case "share": + case 'share': navigator.share({ title: item.Name, text: item.Overview, - url: "https://github.com/jellyfin/jellyfin" + url: `${apiClient.serverAddress()}/web/index.html#!/${appRouter.getRouteUrl(item)}` }); break; - case "album": + case 'album': appRouter.showItem(item.AlbumId, item.ServerId); getResolveFunction(resolve, id)(); break; - case "artist": + case 'artist': appRouter.showItem(item.ArtistItems[0].Id, item.ServerId); getResolveFunction(resolve, id)(); break; - case "playallfromhere": + case 'playallfromhere': getResolveFunction(resolve, id)(); break; - case "queueallfromhere": + case 'queueallfromhere': getResolveFunction(resolve, id)(); break; - case "removefromplaylist": + case 'removefromplaylist': apiClient.ajax({ - url: apiClient.getUrl("Playlists/" + options.playlistId + "/Items", { - EntryIds: [item.PlaylistItemId].join(",") + url: apiClient.getUrl('Playlists/' + options.playlistId + '/Items', { + EntryIds: [item.PlaylistItemId].join(',') }), - type: "DELETE" + type: 'DELETE' }).then(function () { getResolveFunction(resolve, id, true)(); }); break; - case "removefromcollection": + case 'removefromcollection': apiClient.ajax({ - type: "DELETE", - url: apiClient.getUrl("Collections/" + options.collectionId + "/Items", { + type: 'DELETE', + url: apiClient.getUrl('Collections/' + options.collectionId + '/Items', { - Ids: [item.Id].join(",") + Ids: [item.Id].join(',') }) }).then(function () { getResolveFunction(resolve, id, true)(); }); break; - case "canceltimer": + case 'canceltimer': deleteTimer(apiClient, item, resolve, id); break; - case "cancelseriestimer": + case 'cancelseriestimer': deleteSeriesTimer(apiClient, item, resolve, id); break; default: @@ -494,7 +502,7 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", } function deleteTimer(apiClient, item, resolve, command) { - require(["recordingHelper"], function (recordingHelper) { + require(['recordingHelper'], function (recordingHelper) { var timerId = item.TimerId || item.Id; recordingHelper.cancelTimerWithConfirmation(timerId, item.ServerId).then(function () { getResolveFunction(resolve, command, true)(); @@ -503,7 +511,7 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", } function deleteSeriesTimer(apiClient, item, resolve, command) { - require(["recordingHelper"], function (recordingHelper) { + require(['recordingHelper'], function (recordingHelper) { recordingHelper.cancelSeriesTimerWithConfirmation(item.Id, item.ServerId).then(function () { getResolveFunction(resolve, command, true)(); }); @@ -511,14 +519,14 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", } function play(item, resume, queue, queueNext) { - var method = queue ? (queueNext ? "queueNext" : "queue") : "play"; + var method = queue ? (queueNext ? 'queueNext' : 'queue') : 'play'; var startPosition = 0; if (resume && item.UserData && item.UserData.PlaybackPositionTicks) { startPosition = item.UserData.PlaybackPositionTicks; } - if (item.Type === "Program") { + if (item.Type === 'Program') { playbackManager[method]({ ids: [item.ChannelId], startPositionTicks: startPosition, @@ -536,16 +544,16 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", return new Promise(function (resolve, reject) { var serverId = apiClient.serverInfo().Id; - if (item.Type === "Timer") { - require(["recordingEditor"], function (recordingEditor) { + if (item.Type === 'Timer') { + require(['recordingEditor'], function (recordingEditor) { recordingEditor.show(item.Id, serverId).then(resolve, reject); }); - } else if (item.Type === "SeriesTimer") { - require(["seriesRecordingEditor"], function (recordingEditor) { + } else if (item.Type === 'SeriesTimer') { + require(['seriesRecordingEditor'], function (recordingEditor) { recordingEditor.show(item.Id, serverId).then(resolve, reject); }); } else { - require(["metadataEditor"], function (metadataEditor) { + require(['metadataEditor'], function (metadataEditor) { metadataEditor.show(item.Id, serverId).then(resolve, reject); }); } @@ -554,7 +562,7 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", function deleteItem(apiClient, item) { return new Promise(function (resolve, reject) { - require(["deleteHelper"], function (deleteHelper) { + require(['deleteHelper'], function (deleteHelper) { deleteHelper.deleteItem({ item: item, navigate: false @@ -566,11 +574,11 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", } function refresh(apiClient, item) { - require(["refreshDialog"], function (refreshDialog) { + require(['refreshDialog'], function (refreshDialog) { new refreshDialog({ itemIds: [item.Id], serverId: apiClient.serverInfo().Id, - mode: item.Type === "CollectionFolder" ? "scan" : null + mode: item.Type === 'CollectionFolder' ? 'scan' : null }).show(); }); } @@ -584,7 +592,7 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter", return actionsheet.show({ items: commands, positionTo: options.positionTo, - resolveOnClick: ["share"] + resolveOnClick: ['share'] }).then(function (id) { return executeCommand(options.item, id, options); }); diff --git a/src/components/itemhelper.js b/src/components/itemHelper.js similarity index 88% rename from src/components/itemhelper.js rename to src/components/itemHelper.js index 8e0883474a..b18f37110c 100644 --- a/src/components/itemhelper.js +++ b/src/components/itemHelper.js @@ -4,7 +4,7 @@ define(['apphost', 'globalize'], function (appHost, globalize) { function getDisplayName(item, options) { if (!item) { - throw new Error("null item passed into getDisplayName"); + throw new Error('null item passed into getDisplayName'); } options = options || {}; @@ -15,31 +15,31 @@ define(['apphost', 'globalize'], function (appHost, globalize) { var name = ((item.Type === 'Program' || item.Type === 'Recording') && (item.IsSeries || item.EpisodeTitle) ? item.EpisodeTitle : item.Name) || ''; - if (item.Type === "TvChannel") { + if (item.Type === 'TvChannel') { if (item.ChannelNumber) { return item.ChannelNumber + ' ' + name; } return name; } - if (item.Type === "Episode" && item.ParentIndexNumber === 0) { + if (item.Type === 'Episode' && item.ParentIndexNumber === 0) { name = globalize.translate('ValueSpecialEpisodeName', name); - } else if ((item.Type === "Episode" || item.Type === 'Program') && item.IndexNumber != null && item.ParentIndexNumber != null && options.includeIndexNumber !== false) { + } else if ((item.Type === 'Episode' || item.Type === 'Program') && item.IndexNumber != null && item.ParentIndexNumber != null && options.includeIndexNumber !== false) { var displayIndexNumber = item.IndexNumber; var number = displayIndexNumber; - var nameSeparator = " - "; + var nameSeparator = ' - '; if (options.includeParentInfo !== false) { - number = "S" + item.ParentIndexNumber + ":E" + number; + number = 'S' + item.ParentIndexNumber + ':E' + number; } else { - nameSeparator = ". "; + nameSeparator = '. '; } if (item.IndexNumberEnd) { displayIndexNumber = item.IndexNumberEnd; - number += "-" + displayIndexNumber; + number += '-' + displayIndexNumber; } if (number) { @@ -94,14 +94,14 @@ define(['apphost', 'globalize'], function (appHost, globalize) { return false; } - return item.MediaType || item.IsFolder || item.Type === "Genre" || item.Type === "MusicGenre" || item.Type === "MusicArtist"; + return item.MediaType || item.IsFolder || item.Type === 'Genre' || item.Type === 'MusicGenre' || item.Type === 'MusicArtist'; } function canEdit(user, item) { var itemType = item.Type; - if (itemType === "UserRootFolder" || itemType === "UserView") { + if (itemType === 'UserRootFolder' || itemType === 'UserView') { return false; } @@ -149,15 +149,15 @@ define(['apphost', 'globalize'], function (appHost, globalize) { var itemType = item.Type; - if (itemType === "Movie" || - itemType === "Trailer" || - itemType === "Series" || - itemType === "BoxSet" || - itemType === "Person" || - itemType === "Book" || - itemType === "MusicAlbum" || - itemType === "MusicArtist" || - itemType === "MusicVideo") { + if (itemType === 'Movie' || + itemType === 'Trailer' || + itemType === 'Series' || + itemType === 'BoxSet' || + itemType === 'Person' || + itemType === 'Book' || + itemType === 'MusicAlbum' || + itemType === 'MusicArtist' || + itemType === 'MusicVideo') { if (user.Policy.IsAdministrator) { @@ -259,11 +259,11 @@ define(['apphost', 'globalize'], function (appHost, globalize) { } } - if (item.Type === "Series" || - item.Type === "Season" || - item.Type === "BoxSet" || - item.MediaType === "Book" || - item.MediaType === "Recording") { + if (item.Type === 'Series' || + item.Type === 'Season' || + item.Type === 'BoxSet' || + item.MediaType === 'Book' || + item.MediaType === 'Recording') { return true; } diff --git a/src/components/itemMediaInfo/itemMediaInfo.js b/src/components/itemMediaInfo/itemMediaInfo.js index ff2ec626d6..81c84b6a23 100644 --- a/src/components/itemMediaInfo/itemMediaInfo.js +++ b/src/components/itemMediaInfo/itemMediaInfo.js @@ -1,145 +1,145 @@ -define(["dialogHelper", "require", "layoutManager", "globalize", "userSettings", "connectionManager", "loading", "focusManager", "dom", "apphost", "emby-select", "listViewStyle", "paper-icon-button-light", "css!./../formdialog", "material-icons", "emby-button", "flexStyles"], function (dialogHelper, require, layoutManager, globalize, userSettings, connectionManager, loading, focusManager, dom, appHost) { - "use strict"; +define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', 'connectionManager', 'loading', 'focusManager', 'dom', 'apphost', 'emby-select', 'listViewStyle', 'paper-icon-button-light', 'css!./../formdialog', 'material-icons', 'emby-button', 'flexStyles'], function (dialogHelper, require, layoutManager, globalize, userSettings, connectionManager, loading, focusManager, dom, appHost) { + 'use strict'; function setMediaInfo(user, page, item) { var html = item.MediaSources.map(function (version) { return getMediaSourceHtml(user, item, version); }).join('
'); if (item.MediaSources.length > 1) { - html = "
" + html; + html = '
' + html; } - var mediaInfoContent = page.querySelector("#mediaInfoContent"); + var mediaInfoContent = page.querySelector('#mediaInfoContent'); mediaInfoContent.innerHTML = html; } function getMediaSourceHtml(user, item, version) { - var html = ""; + var html = ''; if (version.Name) { - html += '

' + version.Name + "

"; + html += '

' + version.Name + '

'; } if (version.Container) { - html += createAttribute(globalize.translate("MediaInfoContainer"), version.Container) + "
"; + html += createAttribute(globalize.translate('MediaInfoContainer'), version.Container) + '
'; } if (version.Formats && version.Formats.length) { - html += createAttribute(globalize.translate("MediaInfoFormat"), version.Formats.join(",")) + "
"; + html += createAttribute(globalize.translate('MediaInfoFormat'), version.Formats.join(',')) + '
'; } if (version.Path && user && user.Policy.IsAdministrator) { - html += createAttribute(globalize.translate("MediaInfoPath"), version.Path) + "
"; + html += createAttribute(globalize.translate('MediaInfoPath'), version.Path) + '
'; } if (version.Size) { - var size = (version.Size / (1024 * 1024)).toFixed(0) + " MB"; - html += createAttribute(globalize.translate("MediaInfoSize"), size) + "
"; + var size = (version.Size / (1024 * 1024)).toFixed(0) + ' MB'; + html += createAttribute(globalize.translate('MediaInfoSize'), size) + '
'; } for (var i = 0, length = version.MediaStreams.length; i < length; i++) { var stream = version.MediaStreams[i]; - if (stream.Type === "Data") { + if (stream.Type === 'Data') { continue; } html += '
'; - var displayType = globalize.translate("MediaInfoStreamType" + stream.Type); - html += '

' + displayType + "

"; + var displayType = globalize.translate('MediaInfoStreamType' + stream.Type); + html += '

' + displayType + '

'; var attributes = []; if (stream.DisplayTitle) { - attributes.push(createAttribute("Title", stream.DisplayTitle)); + attributes.push(createAttribute('Title', stream.DisplayTitle)); } - if (stream.Language && stream.Type !== "Video") { - attributes.push(createAttribute(globalize.translate("MediaInfoLanguage"), stream.Language)); + if (stream.Language && stream.Type !== 'Video') { + attributes.push(createAttribute(globalize.translate('MediaInfoLanguage'), stream.Language)); } if (stream.Codec) { - attributes.push(createAttribute(globalize.translate("MediaInfoCodec"), stream.Codec.toUpperCase())); + attributes.push(createAttribute(globalize.translate('MediaInfoCodec'), stream.Codec.toUpperCase())); } if (stream.CodecTag) { - attributes.push(createAttribute(globalize.translate("MediaInfoCodecTag"), stream.CodecTag)); + attributes.push(createAttribute(globalize.translate('MediaInfoCodecTag'), stream.CodecTag)); } if (stream.IsAVC != null) { - attributes.push(createAttribute("AVC", (stream.IsAVC ? "Yes" : "No"))); + attributes.push(createAttribute('AVC', (stream.IsAVC ? 'Yes' : 'No'))); } if (stream.Profile) { - attributes.push(createAttribute(globalize.translate("MediaInfoProfile"), stream.Profile)); + attributes.push(createAttribute(globalize.translate('MediaInfoProfile'), stream.Profile)); } if (stream.Level) { - attributes.push(createAttribute(globalize.translate("MediaInfoLevel"), stream.Level)); + attributes.push(createAttribute(globalize.translate('MediaInfoLevel'), stream.Level)); } if (stream.Width || stream.Height) { - attributes.push(createAttribute(globalize.translate("MediaInfoResolution"), stream.Width + "x" + stream.Height)); + attributes.push(createAttribute(globalize.translate('MediaInfoResolution'), stream.Width + 'x' + stream.Height)); } - if (stream.AspectRatio && stream.Codec !== "mjpeg") { - attributes.push(createAttribute(globalize.translate("MediaInfoAspectRatio"), stream.AspectRatio)); + if (stream.AspectRatio && stream.Codec !== 'mjpeg') { + attributes.push(createAttribute(globalize.translate('MediaInfoAspectRatio'), stream.AspectRatio)); } - if (stream.Type === "Video") { + if (stream.Type === 'Video') { if (stream.IsAnamorphic != null) { - attributes.push(createAttribute(globalize.translate("MediaInfoAnamorphic"), (stream.IsAnamorphic ? "Yes" : "No"))); + attributes.push(createAttribute(globalize.translate('MediaInfoAnamorphic'), (stream.IsAnamorphic ? 'Yes' : 'No'))); } - attributes.push(createAttribute(globalize.translate("MediaInfoInterlaced"), (stream.IsInterlaced ? "Yes" : "No"))); + attributes.push(createAttribute(globalize.translate('MediaInfoInterlaced'), (stream.IsInterlaced ? 'Yes' : 'No'))); } if (stream.AverageFrameRate || stream.RealFrameRate) { - attributes.push(createAttribute(globalize.translate("MediaInfoFramerate"), (stream.AverageFrameRate || stream.RealFrameRate))); + attributes.push(createAttribute(globalize.translate('MediaInfoFramerate'), (stream.AverageFrameRate || stream.RealFrameRate))); } if (stream.ChannelLayout) { - attributes.push(createAttribute(globalize.translate("MediaInfoLayout"), stream.ChannelLayout)); + attributes.push(createAttribute(globalize.translate('MediaInfoLayout'), stream.ChannelLayout)); } if (stream.Channels) { - attributes.push(createAttribute(globalize.translate("MediaInfoChannels"), stream.Channels + " ch")); + attributes.push(createAttribute(globalize.translate('MediaInfoChannels'), stream.Channels + ' ch')); } - if (stream.BitRate && stream.Codec !== "mjpeg") { - attributes.push(createAttribute(globalize.translate("MediaInfoBitrate"), (parseInt(stream.BitRate / 1000)) + " kbps")); + if (stream.BitRate && stream.Codec !== 'mjpeg') { + attributes.push(createAttribute(globalize.translate('MediaInfoBitrate'), (parseInt(stream.BitRate / 1000)) + ' kbps')); } if (stream.SampleRate) { - attributes.push(createAttribute(globalize.translate("MediaInfoSampleRate"), stream.SampleRate + " Hz")); + attributes.push(createAttribute(globalize.translate('MediaInfoSampleRate'), stream.SampleRate + ' Hz')); } if (stream.BitDepth) { - attributes.push(createAttribute(globalize.translate("MediaInfoBitDepth"), stream.BitDepth + " bit")); + attributes.push(createAttribute(globalize.translate('MediaInfoBitDepth'), stream.BitDepth + ' bit')); } if (stream.PixelFormat) { - attributes.push(createAttribute(globalize.translate("MediaInfoPixelFormat"), stream.PixelFormat)); + attributes.push(createAttribute(globalize.translate('MediaInfoPixelFormat'), stream.PixelFormat)); } if (stream.RefFrames) { - attributes.push(createAttribute(globalize.translate("MediaInfoRefFrames"), stream.RefFrames)); + attributes.push(createAttribute(globalize.translate('MediaInfoRefFrames'), stream.RefFrames)); } if (stream.NalLengthSize) { - attributes.push(createAttribute("NAL", stream.NalLengthSize)); + attributes.push(createAttribute('NAL', stream.NalLengthSize)); } - if (stream.Type !== "Video") { - attributes.push(createAttribute(globalize.translate("MediaInfoDefault"), (stream.IsDefault ? "Yes" : "No"))); + if (stream.Type !== 'Video') { + attributes.push(createAttribute(globalize.translate('MediaInfoDefault'), (stream.IsDefault ? 'Yes' : 'No'))); } - if (stream.Type === "Subtitle") { - attributes.push(createAttribute(globalize.translate("MediaInfoForced"), (stream.IsForced ? "Yes" : "No"))); - attributes.push(createAttribute(globalize.translate("MediaInfoExternal"), (stream.IsExternal ? "Yes" : "No"))); + if (stream.Type === 'Subtitle') { + attributes.push(createAttribute(globalize.translate('MediaInfoForced'), (stream.IsForced ? 'Yes' : 'No'))); + attributes.push(createAttribute(globalize.translate('MediaInfoExternal'), (stream.IsExternal ? 'Yes' : 'No'))); } - if (stream.Type === "Video" && version.Timestamp) { - attributes.push(createAttribute(globalize.translate("MediaInfoTimestamp"), version.Timestamp)); + if (stream.Type === 'Video' && version.Timestamp) { + attributes.push(createAttribute(globalize.translate('MediaInfoTimestamp'), version.Timestamp)); } - html += attributes.join("
"); - html += "
"; + html += attributes.join('
'); + html += '
'; } return html; } function createAttribute(label, value) { - return '' + label + '' + value + ""; + return '' + label + '' + value + ''; } function showMediaInfoMore(itemId, serverId, template) { var apiClient = connectionManager.getApiClient(serverId); return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { var dialogOptions = { - size: "small", + size: 'small', removeOnClose: true, scrollY: false }; if (layoutManager.tv) { - dialogOptions.size = "fullscreen"; + dialogOptions.size = 'fullscreen'; } var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = ""; - html += globalize.translateDocument(template, "core"); + dlg.classList.add('formDialog'); + var html = ''; + html += globalize.translateDocument(template, 'core'); dlg.innerHTML = html; if (layoutManager.tv) { - dlg.querySelector(".formDialogContent"); + dlg.querySelector('.formDialogContent'); } dialogHelper.open(dlg); - dlg.querySelector(".btnCancel").addEventListener("click", function (e) { + dlg.querySelector('.btnCancel').addEventListener('click', function (e) { dialogHelper.close(dlg); }); apiClient.getCurrentUser().then(function (user) { @@ -152,7 +152,7 @@ define(["dialogHelper", "require", "layoutManager", "globalize", "userSettings", function showMediaInfo(itemId, serverId) { loading.show(); return new Promise(function (resolve, reject) { - require(["text!./itemMediaInfo.template.html"], function (template) { + require(['text!./itemMediaInfo.template.html'], function (template) { showMediaInfoMore(itemId, serverId, template).then(resolve, reject); }); }); diff --git a/src/components/itemMediaInfo/itemMediaInfo.template.html b/src/components/itemMediaInfo/itemMediaInfo.template.html index d5f583dcab..bc8ce59680 100644 --- a/src/components/itemMediaInfo/itemMediaInfo.template.html +++ b/src/components/itemMediaInfo/itemMediaInfo.template.html @@ -1,6 +1,6 @@

${HeaderMediaInfo}

diff --git a/src/components/itemidentifier/itemidentifier.js b/src/components/itemidentifier/itemidentifier.js index 9f89aef947..b335d1dfd3 100644 --- a/src/components/itemidentifier/itemidentifier.js +++ b/src/components/itemidentifier/itemidentifier.js @@ -1,5 +1,5 @@ -define(["dialogHelper", "loading", "connectionManager", "require", "globalize", "scrollHelper", "layoutManager", "focusManager", "browser", "emby-input", "emby-checkbox", "paper-icon-button-light", "css!./../formdialog", "material-icons", "cardStyle"], function (dialogHelper, loading, connectionManager, require, globalize, scrollHelper, layoutManager, focusManager, browser) { - "use strict"; +define(['dialogHelper', 'loading', 'connectionManager', 'require', 'globalize', 'scrollHelper', 'layoutManager', 'focusManager', 'browser', 'emby-input', 'emby-checkbox', 'paper-icon-button-light', 'css!./../formdialog', 'material-icons', 'cardStyle'], function (dialogHelper, loading, connectionManager, require, globalize, scrollHelper, layoutManager, focusManager, browser) { + 'use strict'; var enableFocusTransform = !browser.slow && !browser.edge; @@ -23,7 +23,7 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", var i; var length; - var identifyField = page.querySelectorAll(".identifyField"); + var identifyField = page.querySelectorAll('.identifyField'); var value; for (i = 0, length = identifyField.length; i < length; i++) { @@ -31,17 +31,17 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", if (value) { - if (identifyField[i].type === "number") { + if (identifyField[i].type === 'number') { value = parseInt(value); } - lookupInfo[identifyField[i].getAttribute("data-lookup")] = value; + lookupInfo[identifyField[i].getAttribute('data-lookup')] = value; } } var hasId = false; - var txtLookupId = page.querySelectorAll(".txtLookupId"); + var txtLookupId = page.querySelectorAll('.txtLookupId'); for (i = 0, length = txtLookupId.length; i < length; i++) { value = txtLookupId[i].value; @@ -49,12 +49,12 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", if (value) { hasId = true; } - lookupInfo.ProviderIds[txtLookupId[i].getAttribute("data-providerkey")] = value; + lookupInfo.ProviderIds[txtLookupId[i].getAttribute('data-providerkey')] = value; } if (!hasId && !lookupInfo.Name) { - require(["toast"], function (toast) { - toast(globalize.translate("PleaseEnterNameOrId")); + require(['toast'], function (toast) { + toast(globalize.translate('PleaseEnterNameOrId')); }); return; } @@ -74,11 +74,11 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", var apiClient = getApiClient(); apiClient.ajax({ - type: "POST", - url: apiClient.getUrl("Items/RemoteSearch/" + currentItemType), + type: 'POST', + url: apiClient.getUrl('Items/RemoteSearch/' + currentItemType), data: JSON.stringify(lookupInfo), - contentType: "application/json", - dataType: "json" + contentType: 'application/json', + dataType: 'json' }).then(function (results) { @@ -89,14 +89,14 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", function showIdentificationSearchResults(page, results) { - var identificationSearchResults = page.querySelector(".identificationSearchResults"); + var identificationSearchResults = page.querySelector('.identificationSearchResults'); - page.querySelector(".popupIdentifyForm").classList.add("hide"); - identificationSearchResults.classList.remove("hide"); - page.querySelector(".identifyOptionsForm").classList.add("hide"); - page.querySelector(".dialogContentInner").classList.remove("dialog-content-centered"); + page.querySelector('.popupIdentifyForm').classList.add('hide'); + identificationSearchResults.classList.remove('hide'); + page.querySelector('.identifyOptionsForm').classList.add('hide'); + page.querySelector('.dialogContentInner').classList.remove('dialog-content-centered'); - var html = ""; + var html = ''; var i; var length; for (i = 0, length = results.length; i < length; i++) { @@ -105,11 +105,11 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", html += getSearchResultHtml(result, i); } - var elem = page.querySelector(".identificationSearchResultList"); + var elem = page.querySelector('.identificationSearchResultList'); elem.innerHTML = html; function onSearchImageClick() { - var index = parseInt(this.getAttribute("data-index")); + var index = parseInt(this.getAttribute('data-index')); var currentResult = results[index]; @@ -122,10 +122,10 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", } } - var searchImages = elem.querySelectorAll(".card"); + var searchImages = elem.querySelectorAll('.card'); for (i = 0, length = searchImages.length; i < length; i++) { - searchImages[i].addEventListener("click", onSearchImageClick); + searchImages[i].addEventListener('click', onSearchImageClick); } if (layoutManager.tv) { @@ -143,13 +143,13 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", function showIdentifyOptions(page, identifyResult) { - var identifyOptionsForm = page.querySelector(".identifyOptionsForm"); + var identifyOptionsForm = page.querySelector('.identifyOptionsForm'); - page.querySelector(".popupIdentifyForm").classList.add("hide"); - page.querySelector(".identificationSearchResults").classList.add("hide"); - identifyOptionsForm.classList.remove("hide"); - page.querySelector("#chkIdentifyReplaceImages").checked = true; - page.querySelector(".dialogContentInner").classList.add("dialog-content-centered"); + page.querySelector('.popupIdentifyForm').classList.add('hide'); + page.querySelector('.identificationSearchResults').classList.add('hide'); + identifyOptionsForm.classList.remove('hide'); + page.querySelector('#chkIdentifyReplaceImages').checked = true; + page.querySelector('.dialogContentInner').classList.add('dialog-content-centered'); currentSearchResult = identifyResult; @@ -160,48 +160,48 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", lines.push(identifyResult.ProductionYear); } - var resultHtml = lines.join("
"); + var resultHtml = lines.join('
'); if (identifyResult.ImageUrl) { var displayUrl = getSearchImageDisplayUrl(identifyResult.ImageUrl, identifyResult.SearchProviderName); - resultHtml = '
' + resultHtml + "
"; + resultHtml = '
' + resultHtml + '
'; } - page.querySelector(".selectedSearchResult").innerHTML = resultHtml; + page.querySelector('.selectedSearchResult').innerHTML = resultHtml; - focusManager.focus(identifyOptionsForm.querySelector(".btnSubmit")); + focusManager.focus(identifyOptionsForm.querySelector('.btnSubmit')); } function getSearchResultHtml(result, index) { // TODO move card creation code to Card component - var html = ""; - var cssClass = "card scalableCard"; - var cardBoxCssClass = "cardBox"; + var html = ''; + var cssClass = 'card scalableCard'; + var cardBoxCssClass = 'cardBox'; var padderClass; - if (currentItemType === "Episode") { - cssClass += " backdropCard backdropCard-scalable"; - padderClass = "cardPadder-backdrop"; - } else if (currentItemType === "MusicAlbum" || currentItemType === "MusicArtist") { - cssClass += " squareCard squareCard-scalable"; - padderClass = "cardPadder-square"; + if (currentItemType === 'Episode') { + cssClass += ' backdropCard backdropCard-scalable'; + padderClass = 'cardPadder-backdrop'; + } else if (currentItemType === 'MusicAlbum' || currentItemType === 'MusicArtist') { + cssClass += ' squareCard squareCard-scalable'; + padderClass = 'cardPadder-square'; } else { - cssClass += " portraitCard portraitCard-scalable"; - padderClass = "cardPadder-portrait"; + cssClass += ' portraitCard portraitCard-scalable'; + padderClass = 'cardPadder-portrait'; } if (layoutManager.tv) { - cssClass += " show-focus"; + cssClass += ' show-focus'; if (enableFocusTransform) { - cssClass += " show-animation"; + cssClass += ' show-animation'; } } - cardBoxCssClass += " cardBox-bottompadded"; + cardBoxCssClass += ' cardBox-bottompadded'; html += '
"; + html += '
'; + html += '
'; var numLines = 2; - if (currentItemType === "MusicAlbum") { + if (currentItemType === 'MusicAlbum') { numLines++; } @@ -242,19 +242,19 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", } else { html += '
'; } - html += lines[i] || " "; - html += "
"; + html += lines[i] || ' '; + html += '
'; } - html += "
"; - html += ""; + html += '
'; + html += ''; return html; } function getSearchImageDisplayUrl(url, provider) { var apiClient = getApiClient(); - return apiClient.getUrl("Items/RemoteSearch/Image", { imageUrl: url, ProviderName: provider }); + return apiClient.getUrl('Items/RemoteSearch/Image', { imageUrl: url, ProviderName: provider }); } function submitIdentficationResult(page) { @@ -262,16 +262,16 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", loading.show(); var options = { - ReplaceAllImages: page.querySelector("#chkIdentifyReplaceImages").checked + ReplaceAllImages: page.querySelector('#chkIdentifyReplaceImages').checked }; var apiClient = getApiClient(); apiClient.ajax({ - type: "POST", - url: apiClient.getUrl("Items/RemoteSearch/Apply/" + currentItem.Id, options), + type: 'POST', + url: apiClient.getUrl('Items/RemoteSearch/Apply/' + currentItem.Id, options), data: JSON.stringify(currentSearchResult), - contentType: "application/json" + contentType: 'application/json' }).then(function () { @@ -292,45 +292,45 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", var apiClient = getApiClient(); - apiClient.getJSON(apiClient.getUrl("Items/" + item.Id + "/ExternalIdInfos")).then(function (idList) { + apiClient.getJSON(apiClient.getUrl('Items/' + item.Id + '/ExternalIdInfos')).then(function (idList) { - var html = ""; + var html = ''; for (var i = 0, length = idList.length; i < length; i++) { var idInfo = idList[i]; - var id = "txtLookup" + idInfo.Key; + var id = 'txtLookup' + idInfo.Key; html += '
'; var fullName = idInfo.Name; if (idInfo.Type) { - fullName = idInfo.Name + " " + globalize.translate(idInfo.Type); + fullName = idInfo.Name + ' ' + globalize.translate(idInfo.Type); } - var idLabel = globalize.translate("LabelDynamicExternalId", fullName); + var idLabel = globalize.translate('LabelDynamicExternalId', fullName); html += ''; - html += "
"; + html += '
'; } - page.querySelector("#txtLookupName").value = ""; + page.querySelector('#txtLookupName').value = ''; - if (item.Type === "Person" || item.Type === "BoxSet") { + if (item.Type === 'Person' || item.Type === 'BoxSet') { - page.querySelector(".fldLookupYear").classList.add("hide"); - page.querySelector("#txtLookupYear").value = ""; + page.querySelector('.fldLookupYear').classList.add('hide'); + page.querySelector('#txtLookupYear').value = ''; } else { - page.querySelector(".fldLookupYear").classList.remove("hide"); - page.querySelector("#txtLookupYear").value = ""; + page.querySelector('.fldLookupYear').classList.remove('hide'); + page.querySelector('#txtLookupYear').value = ''; } - page.querySelector(".identifyProviderIds").innerHTML = html; + page.querySelector('.identifyProviderIds').innerHTML = html; - page.querySelector(".formDialogHeaderTitle").innerHTML = globalize.translate("Identify"); + page.querySelector('.formDialogHeaderTitle').innerHTML = globalize.translate('Identify'); }); } @@ -338,7 +338,7 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", loading.show(); - require(["text!./itemidentifier.template.html"], function (template) { + require(['text!./itemidentifier.template.html'], function (template) { var apiClient = getApiClient(); @@ -348,30 +348,30 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", currentItemType = currentItem.Type; var dialogOptions = { - size: "fullscreen-border", + size: 'small', removeOnClose: true, scrollY: false }; if (layoutManager.tv) { - dialogOptions.size = "fullscreen"; + dialogOptions.size = 'fullscreen'; } var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - dlg.classList.add("recordingDialog"); + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); - var html = ""; - html += globalize.translateDocument(template, "core"); + var html = ''; + html += globalize.translateDocument(template, 'core'); dlg.innerHTML = html; // Has to be assigned a z-index after the call to .open() - dlg.addEventListener("close", onDialogClosed); + dlg.addEventListener('close', onDialogClosed); if (layoutManager.tv) { - scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), false); + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); } if (item.Path) { @@ -384,26 +384,26 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", dialogHelper.open(dlg); - dlg.querySelector(".popupIdentifyForm").addEventListener("submit", function (e) { + dlg.querySelector('.popupIdentifyForm').addEventListener('submit', function (e) { e.preventDefault(); searchForIdentificationResults(dlg); return false; }); - dlg.querySelector(".identifyOptionsForm").addEventListener("submit", function (e) { + dlg.querySelector('.identifyOptionsForm').addEventListener('submit', function (e) { e.preventDefault(); submitIdentficationResult(dlg); return false; }); - dlg.querySelector(".btnCancel").addEventListener("click", function (e) { + dlg.querySelector('.btnCancel').addEventListener('click', function (e) { dialogHelper.close(dlg); }); - dlg.classList.add("identifyDialog"); + dlg.classList.add('identifyDialog'); showIdentificationForm(dlg, item); loading.hide(); @@ -426,47 +426,47 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", currentItem = null; currentItemType = itemType; - require(["text!./itemidentifier.template.html"], function (template) { + require(['text!./itemidentifier.template.html'], function (template) { var dialogOptions = { - size: "fullscreen-border", + size: 'small', removeOnClose: true, scrollY: false }; if (layoutManager.tv) { - dialogOptions.size = "fullscreen"; + dialogOptions.size = 'fullscreen'; } var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - dlg.classList.add("recordingDialog"); + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); - var html = ""; - html += globalize.translateDocument(template, "core"); + var html = ''; + html += globalize.translateDocument(template, 'core'); dlg.innerHTML = html; if (layoutManager.tv) { - scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), false); + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); } dialogHelper.open(dlg); - dlg.querySelector(".btnCancel").addEventListener("click", function (e) { + dlg.querySelector('.btnCancel').addEventListener('click', function (e) { dialogHelper.close(dlg); }); - dlg.querySelector(".popupIdentifyForm").addEventListener("submit", function (e) { + dlg.querySelector('.popupIdentifyForm').addEventListener('submit', function (e) { e.preventDefault(); searchForIdentificationResults(dlg); return false; }); - dlg.addEventListener("close", function () { + dlg.addEventListener('close', function () { loading.hide(); var foundItem = hasChanges ? currentSearchResult : null; @@ -474,7 +474,7 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", resolveFunc(foundItem); }); - dlg.classList.add("identifyDialog"); + dlg.classList.add('identifyDialog'); showIdentificationFormFindNew(dlg, itemName, itemYear, itemType); }); @@ -482,20 +482,20 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize", function showIdentificationFormFindNew(dlg, itemName, itemYear, itemType) { - dlg.querySelector("#txtLookupName").value = itemName; + dlg.querySelector('#txtLookupName').value = itemName; - if (itemType === "Person" || itemType === "BoxSet") { + if (itemType === 'Person' || itemType === 'BoxSet') { - dlg.querySelector(".fldLookupYear").classList.add("hide"); - dlg.querySelector("#txtLookupYear").value = ""; + dlg.querySelector('.fldLookupYear').classList.add('hide'); + dlg.querySelector('#txtLookupYear').value = ''; } else { - dlg.querySelector(".fldLookupYear").classList.remove("hide"); - dlg.querySelector("#txtLookupYear").value = itemYear; + dlg.querySelector('.fldLookupYear').classList.remove('hide'); + dlg.querySelector('#txtLookupYear').value = itemYear; } - dlg.querySelector(".formDialogHeaderTitle").innerHTML = globalize.translate("Search"); + dlg.querySelector('.formDialogHeaderTitle').innerHTML = globalize.translate('Search'); } return { diff --git a/src/components/itemidentifier/itemidentifier.template.html b/src/components/itemidentifier/itemidentifier.template.html index 9b3e58a864..9ea0e945db 100644 --- a/src/components/itemidentifier/itemidentifier.template.html +++ b/src/components/itemidentifier/itemidentifier.template.html @@ -1,6 +1,6 @@

${Identify}

diff --git a/src/components/layoutManager.js b/src/components/layoutManager.js index 21bcdf5933..1428f3468d 100644 --- a/src/components/layoutManager.js +++ b/src/components/layoutManager.js @@ -45,7 +45,7 @@ define(['browser', 'appSettings', 'events'], function (browser, appSettings, eve // Take a guess at initial layout. The consuming app can override if (browser.mobile) { this.setLayout('mobile', false); - } else if (browser.tv || browser.xboxOne) { + } else if (browser.tv || browser.xboxOne || browser.ps4) { this.setLayout('tv', false); } else { this.setLayout(this.defaultLayout || 'tv', false); diff --git a/src/components/lazyLoader/lazyLoaderIntersectionObserver.js b/src/components/lazyLoader/lazyLoaderIntersectionObserver.js new file mode 100644 index 0000000000..8cf5b9cd0a --- /dev/null +++ b/src/components/lazyLoader/lazyLoaderIntersectionObserver.js @@ -0,0 +1,67 @@ +/* eslint-disable indent */ + export class LazyLoader { + constructor(options) { + this.options = options; + } + + createObserver() { + const callback = this.options.callback; + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach(entry => { + callback(entry); + }, + {rootMargin: '50%'}); + }); + + this.observer = observer; + } + + addElements(elements) { + let observer = this.observer; + + if (!observer) { + this.createObserver(); + observer = this.observer; + } + + Array.from(elements).forEach(element => { + observer.observe(element); + }); + } + + destroyObserver() { + const observer = this.observer; + + if (observer) { + observer.disconnect(); + this.observer = null; + } + } + + destroy() { + this.destroyObserver(); + this.options = null; + } + } + + function unveilElements(elements, root, callback) { + if (!elements.length) { + return; + } + const lazyLoader = new LazyLoader({ + callback: callback + }); + lazyLoader.addElements(elements); + } + + export function lazyChildren(elem, callback) { + unveilElements(elem.getElementsByClassName('lazy'), elem, callback); + } + +/* eslint-enable indent */ +export default { + LazyLoader: LazyLoader, + lazyChildren: lazyChildren +}; diff --git a/src/components/lazyloader/lazyedgehack.css b/src/components/lazyloader/lazyedgehack.css deleted file mode 100644 index e358872f16..0000000000 --- a/src/components/lazyloader/lazyedgehack.css +++ /dev/null @@ -1,5 +0,0 @@ -.lazy { - /* In edge, intersection observer will not fire on 0px sized elements */ - min-width: 0.1em; - min-height: 0.1em; -} diff --git a/src/components/lazyloader/lazyloader-intersectionobserver.js b/src/components/lazyloader/lazyloader-intersectionobserver.js deleted file mode 100644 index 5f83742601..0000000000 --- a/src/components/lazyloader/lazyloader-intersectionobserver.js +++ /dev/null @@ -1,103 +0,0 @@ -define(['require', 'browser'], function (require, browser) { - 'use strict'; - - function LazyLoader(options) { - - this.options = options; - } - - if (browser.edge) { - require(['css!./lazyedgehack']); - } - - LazyLoader.prototype.createObserver = function () { - - var observerOptions = {}; - var options = this.options; - var loadedCount = 0; - var callback = options.callback; - - observerOptions.rootMargin = "50%"; - - var observerId = 'obs' + new Date().getTime(); - - var self = this; - var observer = new IntersectionObserver(function (entries) { - for (var j = 0, length2 = entries.length; j < length2; j++) { - var entry = entries[j]; - - if (entry.intersectionRatio > 0) { - - // Stop watching and load the image - var target = entry.target; - - observer.unobserve(target); - - if (!target[observerId]) { - target[observerId] = 1; - callback(target); - loadedCount++; - - if (loadedCount >= self.elementCount) { - self.destroyObserver(); - } - } - } - } - }, - observerOptions - ); - - this.observer = observer; - }; - - LazyLoader.prototype.addElements = function (elements) { - - var observer = this.observer; - - if (!observer) { - this.createObserver(); - observer = this.observer; - } - - this.elementCount = (this.elementCount || 0) + elements.length; - - for (var i = 0, length = elements.length; i < length; i++) { - observer.observe(elements[i]); - } - }; - - LazyLoader.prototype.destroyObserver = function (elements) { - - var observer = this.observer; - - if (observer) { - observer.disconnect(); - this.observer = null; - } - }; - - LazyLoader.prototype.destroy = function (elements) { - - this.destroyObserver(); - this.options = null; - }; - - function unveilElements(elements, root, callback) { - - if (!elements.length) { - return; - } - var lazyLoader = new LazyLoader({ - callback: callback - }); - lazyLoader.addElements(elements); - } - - LazyLoader.lazyChildren = function (elem, callback) { - - unveilElements(elem.getElementsByClassName('lazy'), elem, callback); - }; - - return LazyLoader; -}); diff --git a/src/components/lazyloader/lazyloader-scroll.js b/src/components/lazyloader/lazyloader-scroll.js deleted file mode 100644 index d5120146ce..0000000000 --- a/src/components/lazyloader/lazyloader-scroll.js +++ /dev/null @@ -1,189 +0,0 @@ -define(['visibleinviewport', 'dom', 'browser'], function (visibleinviewport, dom, browser) { - 'use strict'; - - var thresholdX; - var thresholdY; - - var requestIdleCallback = window.requestIdleCallback || function (fn) { - fn(); - }; - - function resetThresholds() { - - var threshold = 0.3; - - thresholdX = screen.availWidth * threshold; - thresholdY = screen.availHeight * threshold; - } - - function resetThresholdsOnTimer() { - - setTimeout(resetThresholds, 500); - } - - if (browser.iOS) { - dom.addEventListener(window, "orientationchange", resetThresholdsOnTimer, { passive: true }); - dom.addEventListener(window, 'resize', resetThresholdsOnTimer, { passive: true }); - } else { - dom.addEventListener(window, "orientationchange", resetThresholds, { passive: true }); - dom.addEventListener(window, 'resize', resetThresholds, { passive: true }); - } - resetThresholds(); - - function isVisible(elem) { - return visibleinviewport(elem, true, thresholdX, thresholdY); - } - - var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); - var self = {}; - - function cancelAll(tokens) { - for (var i = 0, length = tokens.length; i < length; i++) { - - tokens[i] = true; - } - } - - function unveilElementsInternal(instance, callback) { - - var unveiledElements = []; - var cancellationTokens = []; - var loadedCount = 0; - - function unveilInternal(tokenIndex) { - - var anyFound = false; - var out = false; - - var elements = instance.elements; - // TODO: This out construct assumes left to right, top to bottom - - for (var i = 0, length = elements.length; i < length; i++) { - - if (cancellationTokens[tokenIndex]) { - return; - } - if (unveiledElements[i]) { - continue; - } - var elem = elements[i]; - if (!out && isVisible(elem)) { - anyFound = true; - unveiledElements[i] = true; - callback(elem); - loadedCount++; - } else { - - if (anyFound) { - out = true; - } - } - } - - if (loadedCount >= elements.length) { - dom.removeEventListener(document, 'focus', unveil, { - capture: true, - passive: true - }); - dom.removeEventListener(document, 'scroll', unveil, { - capture: true, - passive: true - }); - dom.removeEventListener(document, wheelEvent, unveil, { - capture: true, - passive: true - }); - dom.removeEventListener(window, 'resize', unveil, { - capture: true, - passive: true - }); - } - } - - function unveil() { - - cancelAll(cancellationTokens); - - var index = cancellationTokens.length; - cancellationTokens.length++; - - setTimeout(function () { - unveilInternal(index); - }, 1); - } - - dom.addEventListener(document, 'focus', unveil, { - capture: true, - passive: true - }); - dom.addEventListener(document, 'scroll', unveil, { - capture: true, - passive: true - }); - dom.addEventListener(document, wheelEvent, unveil, { - capture: true, - passive: true - }); - dom.addEventListener(window, 'resize', unveil, { - capture: true, - passive: true - }); - - unveil(); - } - - function LazyLoader(options) { - - this.options = options; - } - - LazyLoader.prototype.createObserver = function () { - - unveilElementsInternal(this, this.options.callback); - this.observer = 1; - }; - - LazyLoader.prototype.addElements = function (elements) { - - this.elements = this.elements || []; - - for (var i = 0, length = elements.length; i < length; i++) { - this.elements.push(elements[i]); - } - - var observer = this.observer; - - if (!observer) { - this.createObserver(); - } - - }; - - LazyLoader.prototype.destroyObserver = function (elements) { - - }; - - LazyLoader.prototype.destroy = function (elements) { - - this.destroyObserver(); - this.options = null; - }; - - function unveilElements(elements, root, callback) { - - if (!elements.length) { - return; - } - var lazyLoader = new LazyLoader({ - callback: callback - }); - lazyLoader.addElements(elements); - } - - LazyLoader.lazyChildren = function (elem, callback) { - - unveilElements(elem.getElementsByClassName('lazy'), elem, callback); - }; - - return LazyLoader; -}); diff --git a/src/components/libraryoptionseditor/libraryoptionseditor.js b/src/components/libraryoptionseditor/libraryoptionseditor.js index 08197299e0..832a6ffc5e 100644 --- a/src/components/libraryoptionseditor/libraryoptionseditor.js +++ b/src/components/libraryoptionseditor/libraryoptionseditor.js @@ -1,130 +1,136 @@ -define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], function(globalize, dom) { - "use strict"; +define(['globalize', 'dom', 'emby-checkbox', 'emby-select', 'emby-input'], function(globalize, dom) { + 'use strict'; function populateLanguages(parent) { return ApiClient.getCultures().then(function(languages) { - populateLanguagesIntoSelect(parent.querySelector("#selectLanguage"), languages); - populateLanguagesIntoList(parent.querySelector(".subtitleDownloadLanguages"), languages); + populateLanguagesIntoSelect(parent.querySelector('#selectLanguage'), languages); + populateLanguagesIntoList(parent.querySelector('.subtitleDownloadLanguages'), languages); }); } function populateLanguagesIntoSelect(select, languages) { - var html = ""; + var html = ''; html += ""; for (var i = 0; i < languages.length; i++) { var culture = languages[i]; - html += ""; + html += "'; } select.innerHTML = html; } function populateLanguagesIntoList(element, languages) { - var html = ""; + var html = ''; for (var i = 0; i < languages.length; i++) { var culture = languages[i]; - html += '"; + html += ''; } element.innerHTML = html; } function populateCountries(select) { return ApiClient.getCountries().then(function(allCountries) { - var html = ""; + var html = ''; html += ""; for (var i = 0; i < allCountries.length; i++) { var culture = allCountries[i]; - html += ""; + html += "'; } select.innerHTML = html; }); } function populateRefreshInterval(select) { - var html = ""; - html += ""; + var html = ''; + html += "'; html += [30, 60, 90].map(function(val) { - return ""; - }).join(""); + return "'; + }).join(''); select.innerHTML = html; } function renderMetadataReaders(page, plugins) { - var html = ""; - var elem = page.querySelector(".metadataReaders"); + var html = ''; + var elem = page.querySelector('.metadataReaders'); - if (plugins.length < 1) return elem.innerHTML = "", elem.classList.add("hide"), !1; - html += '

' + globalize.translate("LabelMetadataReaders") + "

"; + if (plugins.length < 1) return elem.innerHTML = '', elem.classList.add('hide'), !1; + html += '

' + globalize.translate('LabelMetadataReaders') + '

'; html += '
'; for (var i = 0; i < plugins.length; i++) { var plugin = plugins[i]; html += '
'; - html += ''; + html += ''; html += '
'; html += '

'; html += plugin.Name; - html += "

"; - html += "
"; + html += ''; + html += '
'; if (i > 0) { - html += ''; + html += ''; } else if (plugins.length > 1) { - html += ''; + html += ''; } - html += "
"; + html += '
'; } - html += "
"; - html += '
' + globalize.translate("LabelMetadataReadersHelp") + "
"; + html += '
'; + html += '
' + globalize.translate('LabelMetadataReadersHelp') + '
'; if (plugins.length < 2) { - elem.classList.add("hide"); + elem.classList.add('hide'); } else { - elem.classList.remove("hide"); + elem.classList.remove('hide'); } elem.innerHTML = html; return true; } function renderMetadataSavers(page, metadataSavers) { - var html = ""; - var elem = page.querySelector(".metadataSavers"); - if (!metadataSavers.length) return elem.innerHTML = "", elem.classList.add("hide"), false; - html += '

' + globalize.translate("LabelMetadataSavers") + "

"; + var html = ''; + var elem = page.querySelector('.metadataSavers'); + if (!metadataSavers.length) return elem.innerHTML = '', elem.classList.add('hide'), false; + html += '

' + globalize.translate('LabelMetadataSavers') + '

'; html += '
'; for (var i = 0; i < metadataSavers.length; i++) { var plugin = metadataSavers[i]; - html += '"; + html += ''; } - html += "
"; - html += '
' + globalize.translate("LabelMetadataSaversHelp") + "
"; + html += '
'; + html += '
' + globalize.translate('LabelMetadataSaversHelp') + '
'; elem.innerHTML = html; - elem.classList.remove("hide"); + elem.classList.remove('hide'); return true; } function getMetadataFetchersForTypeHtml(availableTypeOptions, libraryOptionsForType) { - var html = ""; + var html = ''; var plugins = availableTypeOptions.MetadataFetchers; plugins = getOrderedPlugins(plugins, libraryOptionsForType.MetadataFetcherOrder || []); if (!plugins.length) return html; html += '
'; - html += '

' + globalize.translate("LabelTypeMetadataDownloaders", globalize.translate(availableTypeOptions.Type)) + "

"; + html += '

' + globalize.translate('LabelTypeMetadataDownloaders', globalize.translate(availableTypeOptions.Type)) + '

'; html += '
'; - for (var i = 0; i < plugins.length; i++) { - var plugin = plugins[i]; + + plugins.forEach((plugin, index) => { html += '
'; var isChecked = libraryOptionsForType.MetadataFetchers ? -1 !== libraryOptionsForType.MetadataFetchers.indexOf(plugin.Name) : plugin.DefaultEnabled; - var checkedHtml = isChecked ? ' checked="checked"' : ""; - html += '"; + var checkedHtml = isChecked ? ' checked="checked"' : ''; + html += ''; html += '
'; html += '

'; html += plugin.Name; - html += "

"; - html += "
"; - i > 0 ? html += '' : plugins.length > 1 && (html += ''), html += "
"; - } - html += "
"; - html += '
' + globalize.translate("LabelMetadataDownloadersHelp") + "
"; - html += "
"; + html += ''; + html += '
'; + if (index > 0) { + html += ''; + } else if (plugins.length > 1) { + html += ''; + } + html += '
'; + }); + + html += '
'; + html += '
' + globalize.translate('LabelMetadataDownloadersHelp') + '
'; + html += '
'; return html; } @@ -138,62 +144,62 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct } function renderMetadataFetchers(page, availableOptions, libraryOptions) { - var html = ""; - var elem = page.querySelector(".metadataFetchers"); + var html = ''; + var elem = page.querySelector('.metadataFetchers'); for (var i = 0; i < availableOptions.TypeOptions.length; i++) { var availableTypeOptions = availableOptions.TypeOptions[i]; html += getMetadataFetchersForTypeHtml(availableTypeOptions, getTypeOptions(libraryOptions, availableTypeOptions.Type) || {}); } elem.innerHTML = html; if (html) { - elem.classList.remove("hide"); - page.querySelector(".fldAutoRefreshInterval").classList.remove("hide"); - page.querySelector(".fldMetadataLanguage").classList.remove("hide"); - page.querySelector(".fldMetadataCountry").classList.remove("hide"); + elem.classList.remove('hide'); + page.querySelector('.fldAutoRefreshInterval').classList.remove('hide'); + page.querySelector('.fldMetadataLanguage').classList.remove('hide'); + page.querySelector('.fldMetadataCountry').classList.remove('hide'); } else { - elem.classList.add("hide"); - page.querySelector(".fldAutoRefreshInterval").classList.add("hide"); - page.querySelector(".fldMetadataLanguage").classList.add("hide"); - page.querySelector(".fldMetadataCountry").classList.add("hide"); + elem.classList.add('hide'); + page.querySelector('.fldAutoRefreshInterval').classList.add('hide'); + page.querySelector('.fldMetadataLanguage').classList.add('hide'); + page.querySelector('.fldMetadataCountry').classList.add('hide'); } return true; } function renderSubtitleFetchers(page, availableOptions, libraryOptions) { - var html = ""; - var elem = page.querySelector(".subtitleFetchers"); + var html = ''; + var elem = page.querySelector('.subtitleFetchers'); var plugins = availableOptions.SubtitleFetchers; plugins = getOrderedPlugins(plugins, libraryOptions.SubtitleFetcherOrder || []); if (!plugins.length) return html; - html += '

' + globalize.translate("LabelSubtitleDownloaders") + "

"; + html += '

' + globalize.translate('LabelSubtitleDownloaders') + '

'; html += '
'; for (var i = 0; i < plugins.length; i++) { var plugin = plugins[i]; html += '
'; var isChecked = libraryOptions.DisabledSubtitleFetchers ? -1 === libraryOptions.DisabledSubtitleFetchers.indexOf(plugin.Name) : plugin.DefaultEnabled; - var checkedHtml = isChecked ? ' checked="checked"' : ""; - html += '"; + var checkedHtml = isChecked ? ' checked="checked"' : ''; + html += ''; html += '
'; html += '

'; html += plugin.Name; - html += "

"; - html += "
"; + html += ''; + html += '
'; if (i > 0) { - html += ''; + html += ''; } else if (plugins.length > 1) { - html += ''; + html += ''; } - html += "
"; + html += '
'; } - html += "
"; - html += '
' + globalize.translate("SubtitleDownloadersHelp") + "
"; + html += ''; + html += '
' + globalize.translate('SubtitleDownloadersHelp') + '
'; elem.innerHTML = html; } function getImageFetchersForTypeHtml(availableTypeOptions, libraryOptionsForType) { - var html = ""; + var html = ''; var plugins = availableTypeOptions.ImageFetchers; plugins = getOrderedPlugins(plugins, libraryOptionsForType.ImageFetcherOrder || []); @@ -201,60 +207,60 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct html += '
'; html += '
'; - html += '

' + globalize.translate("HeaderTypeImageFetchers", availableTypeOptions.Type) + "

"; + html += '

' + globalize.translate('HeaderTypeImageFetchers', availableTypeOptions.Type) + '

'; var supportedImageTypes = availableTypeOptions.SupportedImageTypes || []; - if (supportedImageTypes.length > 1 || 1 === supportedImageTypes.length && "Primary" !== supportedImageTypes[0]) { - html += '"; + if (supportedImageTypes.length > 1 || 1 === supportedImageTypes.length && 'Primary' !== supportedImageTypes[0]) { + html += ''; } - html += "
"; + html += '
'; html += '
'; for (var i = 0; i < plugins.length; i++) { var plugin = plugins[i]; html += '
'; var isChecked = libraryOptionsForType.ImageFetchers ? -1 !== libraryOptionsForType.ImageFetchers.indexOf(plugin.Name) : plugin.DefaultEnabled; - var checkedHtml = isChecked ? ' checked="checked"' : ""; - html += '"; + var checkedHtml = isChecked ? ' checked="checked"' : ''; + html += ''; html += '
'; html += '

'; html += plugin.Name; - html += "

"; - html += "
"; + html += ''; + html += '
'; if (i > 0) { - html += ''; + html += ''; } else if (plugins.length > 1) { - html += ''; + html += ''; } - html += "
"; + html += ''; } - html += ""; - html += '
' + globalize.translate("LabelImageFetchersHelp") + "
"; - html += ""; + html += ''; + html += '
' + globalize.translate('LabelImageFetchersHelp') + '
'; + html += ''; return html; } function renderImageFetchers(page, availableOptions, libraryOptions) { - var html = ""; - var elem = page.querySelector(".imageFetchers"); + var html = ''; + var elem = page.querySelector('.imageFetchers'); for (var i = 0; i < availableOptions.TypeOptions.length; i++) { var availableTypeOptions = availableOptions.TypeOptions[i]; html += getImageFetchersForTypeHtml(availableTypeOptions, getTypeOptions(libraryOptions, availableTypeOptions.Type) || {}); } elem.innerHTML = html; if (html) { - elem.classList.remove("hide"); - page.querySelector(".chkDownloadImagesInAdvanceContainer").classList.remove("hide"); - page.querySelector(".chkSaveLocalContainer").classList.remove("hide"); + elem.classList.remove('hide'); + page.querySelector('.chkDownloadImagesInAdvanceContainer').classList.remove('hide'); + page.querySelector('.chkSaveLocalContainer').classList.remove('hide'); } else { - elem.classList.add("hide"); - page.querySelector(".chkDownloadImagesInAdvanceContainer").classList.add("hide"); - page.querySelector(".chkSaveLocalContainer").classList.add("hide"); + elem.classList.add('hide'); + page.querySelector('.chkDownloadImagesInAdvanceContainer').classList.add('hide'); + page.querySelector('.chkSaveLocalContainer').classList.add('hide'); } return true; } function populateMetadataSettings(parent, contentType, isNewLibrary) { - var isNewLibrary = parent.classList.contains("newlibrary"); - return ApiClient.getJSON(ApiClient.getUrl("Libraries/AvailableOptions", { + var isNewLibrary = parent.classList.contains('newlibrary'); + return ApiClient.getJSON(ApiClient.getUrl('Libraries/AvailableOptions', { LibraryContentType: contentType, IsNewLibrary: isNewLibrary })).then(function(availableOptions) { @@ -265,70 +271,80 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct renderMetadataFetchers(parent, availableOptions, {}); renderSubtitleFetchers(parent, availableOptions, {}); renderImageFetchers(parent, availableOptions, {}); - availableOptions.SubtitleFetchers.length ? parent.querySelector(".subtitleDownloadSettings").classList.remove("hide") : parent.querySelector(".subtitleDownloadSettings").classList.add("hide"); + availableOptions.SubtitleFetchers.length ? parent.querySelector('.subtitleDownloadSettings').classList.remove('hide') : parent.querySelector('.subtitleDownloadSettings').classList.add('hide'); }).catch(function() { return Promise.resolve(); }); } function adjustSortableListElement(elem) { - var btnSortable = elem.querySelector(".btnSortable"); - var inner = btnSortable.querySelector("i"); + var btnSortable = elem.querySelector('.btnSortable'); + var inner = btnSortable.querySelector('.material-icons'); if (elem.previousSibling) { - btnSortable.title = globalize.translate("ButtonUp"); - btnSortable.classList.add("btnSortableMoveUp"); - btnSortable.classList.remove("btnSortableMoveDown"); - inner.classList.remove("keyboard_arrow_down"); - inner.classList.add("keyboard_arrow_up"); + btnSortable.title = globalize.translate('ButtonUp'); + btnSortable.classList.add('btnSortableMoveUp'); + btnSortable.classList.remove('btnSortableMoveDown'); + inner.classList.remove('keyboard_arrow_down'); + inner.classList.add('keyboard_arrow_up'); } else { - btnSortable.title = globalize.translate("ButtonDown"); - btnSortable.classList.remove("btnSortableMoveUp"); - btnSortable.classList.add("btnSortableMoveDown"); - inner.classList.remove("keyboard_arrow_up"); - inner.classList.add("keyboard_arrow_down"); + btnSortable.title = globalize.translate('ButtonDown'); + btnSortable.classList.remove('btnSortableMoveUp'); + btnSortable.classList.add('btnSortableMoveDown'); + inner.classList.remove('keyboard_arrow_up'); + inner.classList.add('keyboard_arrow_down'); } } function showImageOptionsForType(type) { - require(["imageoptionseditor"], function(ImageOptionsEditor) { + require(['imageoptionseditor'], function(ImageOptionsEditor) { var typeOptions = getTypeOptions(currentLibraryOptions, type); - typeOptions || (typeOptions = { - Type: type - }, currentLibraryOptions.TypeOptions.push(typeOptions)); + if (!typeOptions) { + typeOptions = { + Type: type + }; + currentLibraryOptions.TypeOptions.push(typeOptions); + } var availableOptions = getTypeOptions(currentAvailableOptions || {}, type); - (new ImageOptionsEditor).show(type, typeOptions, availableOptions); + var imageOptionsEditor = new ImageOptionsEditor(); + imageOptionsEditor.show(type, typeOptions, availableOptions); }); } function onImageFetchersContainerClick(e) { - var btnImageOptionsForType = dom.parentWithClass(e.target, "btnImageOptionsForType"); + var btnImageOptionsForType = dom.parentWithClass(e.target, 'btnImageOptionsForType'); if (btnImageOptionsForType) { - return void showImageOptionsForType(dom.parentWithClass(btnImageOptionsForType, "imageFetcher").getAttribute("data-type")); + return void showImageOptionsForType(dom.parentWithClass(btnImageOptionsForType, 'imageFetcher').getAttribute('data-type')); } onSortableContainerClick.call(this, e); } function onSortableContainerClick(e) { - var btnSortable = dom.parentWithClass(e.target, "btnSortable"); + var btnSortable = dom.parentWithClass(e.target, 'btnSortable'); if (btnSortable) { - var li = dom.parentWithClass(btnSortable, "sortableOption"); - var list = dom.parentWithClass(li, "paperList"); - if (btnSortable.classList.contains("btnSortableMoveDown")) { + var li = dom.parentWithClass(btnSortable, 'sortableOption'); + var list = dom.parentWithClass(li, 'paperList'); + if (btnSortable.classList.contains('btnSortableMoveDown')) { var next = li.nextSibling; - next && (li.parentNode.removeChild(li), next.parentNode.insertBefore(li, next.nextSibling)); + if (next) { + li.parentNode.removeChild(li); + next.parentNode.insertBefore(li, next.nextSibling); + } } else { var prev = li.previousSibling; - prev && (li.parentNode.removeChild(li), prev.parentNode.insertBefore(li, prev)); + if (prev) { + li.parentNode.removeChild(li); + prev.parentNode.insertBefore(li, prev); + } } - Array.prototype.forEach.call(list.querySelectorAll(".sortableOption"), adjustSortableListElement); + Array.prototype.forEach.call(list.querySelectorAll('.sortableOption'), adjustSortableListElement); } } function bindEvents(parent) { - parent.querySelector(".metadataReaders").addEventListener("click", onSortableContainerClick); - parent.querySelector(".subtitleFetchers").addEventListener("click", onSortableContainerClick); - parent.querySelector(".metadataFetchers").addEventListener("click", onSortableContainerClick); - parent.querySelector(".imageFetchers").addEventListener("click", onImageFetchersContainerClick); + parent.querySelector('.metadataReaders').addEventListener('click', onSortableContainerClick); + parent.querySelector('.subtitleFetchers').addEventListener('click', onSortableContainerClick); + parent.querySelector('.metadataFetchers').addEventListener('click', onSortableContainerClick); + parent.querySelector('.imageFetchers').addEventListener('click', onImageFetchersContainerClick); } function embed(parent, contentType, libraryOptions) { @@ -337,15 +353,15 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct }; currentAvailableOptions = null; var isNewLibrary = null === libraryOptions; - isNewLibrary && parent.classList.add("newlibrary"); + isNewLibrary && parent.classList.add('newlibrary'); return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest; - xhr.open("GET", "components/libraryoptionseditor/libraryoptionseditor.template.html", true); + xhr.open('GET', 'components/libraryoptionseditor/libraryoptionseditor.template.html', true); xhr.onload = function(e) { var template = this.response; parent.innerHTML = globalize.translateDocument(template); - populateRefreshInterval(parent.querySelector("#selectAutoRefreshInterval")); - var promises = [populateLanguages(parent), populateCountries(parent.querySelector("#selectCountry"))]; + populateRefreshInterval(parent.querySelector('#selectAutoRefreshInterval')); + var promises = [populateLanguages(parent), populateCountries(parent.querySelector('#selectCountry'))]; Promise.all(promises).then(function() { return setContentType(parent, contentType).then(function() { libraryOptions && setLibraryOptions(parent, libraryOptions); @@ -359,69 +375,69 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct } function setAdvancedVisible(parent, visible) { - var elems = parent.querySelectorAll(".advanced"); + var elems = parent.querySelectorAll('.advanced'); for (var i = 0; i < elems.length; i++) { - visible ? elems[i].classList.remove("advancedHide") : elems[i].classList.add("advancedHide"); + visible ? elems[i].classList.remove('advancedHide') : elems[i].classList.add('advancedHide'); } } function setContentType(parent, contentType) { - if (contentType === "homevideos" || contentType === "photos") { - parent.querySelector(".chkEnablePhotosContainer").classList.remove("hide"); + if (contentType === 'homevideos' || contentType === 'photos') { + parent.querySelector('.chkEnablePhotosContainer').classList.remove('hide'); } else { - parent.querySelector(".chkEnablePhotosContainer").classList.add("hide"); + parent.querySelector('.chkEnablePhotosContainer').classList.add('hide'); } - if (contentType !== "tvshows" && contentType !== "movies" && contentType !== "homevideos" && contentType !== "musicvideos" && contentType !== "mixed") { - parent.querySelector(".chapterSettingsSection").classList.add("hide"); + if (contentType !== 'tvshows' && contentType !== 'movies' && contentType !== 'homevideos' && contentType !== 'musicvideos' && contentType !== 'mixed') { + parent.querySelector('.chapterSettingsSection').classList.add('hide'); } else { - parent.querySelector(".chapterSettingsSection").classList.remove("hide"); + parent.querySelector('.chapterSettingsSection').classList.remove('hide'); } - if (contentType === "tvshows") { - parent.querySelector(".chkImportMissingEpisodesContainer").classList.remove("hide"); - parent.querySelector(".chkAutomaticallyGroupSeriesContainer").classList.remove("hide"); - parent.querySelector(".fldSeasonZeroDisplayName").classList.remove("hide"); - parent.querySelector("#txtSeasonZeroName").setAttribute("required", "required"); + if (contentType === 'tvshows') { + parent.querySelector('.chkImportMissingEpisodesContainer').classList.remove('hide'); + parent.querySelector('.chkAutomaticallyGroupSeriesContainer').classList.remove('hide'); + parent.querySelector('.fldSeasonZeroDisplayName').classList.remove('hide'); + parent.querySelector('#txtSeasonZeroName').setAttribute('required', 'required'); } else { - parent.querySelector(".chkImportMissingEpisodesContainer").classList.add("hide"); - parent.querySelector(".chkAutomaticallyGroupSeriesContainer").classList.add("hide"); - parent.querySelector(".fldSeasonZeroDisplayName").classList.add("hide"); - parent.querySelector("#txtSeasonZeroName").removeAttribute("required"); + parent.querySelector('.chkImportMissingEpisodesContainer').classList.add('hide'); + parent.querySelector('.chkAutomaticallyGroupSeriesContainer').classList.add('hide'); + parent.querySelector('.fldSeasonZeroDisplayName').classList.add('hide'); + parent.querySelector('#txtSeasonZeroName').removeAttribute('required'); } - if (contentType === "books" || contentType === "boxsets" || contentType === "playlists" || contentType === "music") { - parent.querySelector(".chkEnableEmbeddedTitlesContainer").classList.add("hide"); + if (contentType === 'books' || contentType === 'boxsets' || contentType === 'playlists' || contentType === 'music') { + parent.querySelector('.chkEnableEmbeddedTitlesContainer').classList.add('hide'); } else { - parent.querySelector(".chkEnableEmbeddedTitlesContainer").classList.remove("hide"); + parent.querySelector('.chkEnableEmbeddedTitlesContainer').classList.remove('hide'); } - if (contentType === "tvshows") { - parent.querySelector(".chkEnableEmbeddedEpisodeInfosContainer").classList.remove("hide"); + if (contentType === 'tvshows') { + parent.querySelector('.chkEnableEmbeddedEpisodeInfosContainer').classList.remove('hide'); } else { - parent.querySelector(".chkEnableEmbeddedEpisodeInfosContainer").classList.add("hide"); + parent.querySelector('.chkEnableEmbeddedEpisodeInfosContainer').classList.add('hide'); } return populateMetadataSettings(parent, contentType); } function setSubtitleFetchersIntoOptions(parent, options) { - options.DisabledSubtitleFetchers = Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll(".chkSubtitleFetcher"), function(elem) { + options.DisabledSubtitleFetchers = Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll('.chkSubtitleFetcher'), function(elem) { return !elem.checked; }), function(elem) { - return elem.getAttribute("data-pluginname"); + return elem.getAttribute('data-pluginname'); }); - options.SubtitleFetcherOrder = Array.prototype.map.call(parent.querySelectorAll(".subtitleFetcherItem"), function(elem) { - return elem.getAttribute("data-pluginname"); + options.SubtitleFetcherOrder = Array.prototype.map.call(parent.querySelectorAll('.subtitleFetcherItem'), function(elem) { + return elem.getAttribute('data-pluginname'); }); } function setMetadataFetchersIntoOptions(parent, options) { - var sections = parent.querySelectorAll(".metadataFetcher"); + var sections = parent.querySelectorAll('.metadataFetcher'); for (var i = 0; i < sections.length; i++) { var section = sections[i]; - var type = section.getAttribute("data-type"); + var type = section.getAttribute('data-type'); var typeOptions = getTypeOptions(options, type); if (!typeOptions) { typeOptions = { @@ -429,23 +445,23 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct }; options.TypeOptions.push(typeOptions); } - typeOptions.MetadataFetchers = Array.prototype.map.call(Array.prototype.filter.call(section.querySelectorAll(".chkMetadataFetcher"), function(elem) { + typeOptions.MetadataFetchers = Array.prototype.map.call(Array.prototype.filter.call(section.querySelectorAll('.chkMetadataFetcher'), function(elem) { return elem.checked; }), function(elem) { - return elem.getAttribute("data-pluginname"); + return elem.getAttribute('data-pluginname'); }); - typeOptions.MetadataFetcherOrder = Array.prototype.map.call(section.querySelectorAll(".metadataFetcherItem"), function(elem) { - return elem.getAttribute("data-pluginname"); + typeOptions.MetadataFetcherOrder = Array.prototype.map.call(section.querySelectorAll('.metadataFetcherItem'), function(elem) { + return elem.getAttribute('data-pluginname'); }); } } function setImageFetchersIntoOptions(parent, options) { - var sections = parent.querySelectorAll(".imageFetcher"); + var sections = parent.querySelectorAll('.imageFetcher'); for (var i = 0; i < sections.length; i++) { var section = sections[i]; - var type = section.getAttribute("data-type"); + var type = section.getAttribute('data-type'); var typeOptions = getTypeOptions(options, type); if (!typeOptions) { typeOptions = { @@ -454,14 +470,14 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct options.TypeOptions.push(typeOptions); } - typeOptions.ImageFetchers = Array.prototype.map.call(Array.prototype.filter.call(section.querySelectorAll(".chkImageFetcher"), function(elem) { + typeOptions.ImageFetchers = Array.prototype.map.call(Array.prototype.filter.call(section.querySelectorAll('.chkImageFetcher'), function(elem) { return elem.checked; }), function(elem) { - return elem.getAttribute("data-pluginname"); + return elem.getAttribute('data-pluginname'); }); - typeOptions.ImageFetcherOrder = Array.prototype.map.call(section.querySelectorAll(".imageFetcherItem"), function(elem) { - return elem.getAttribute("data-pluginname"); + typeOptions.ImageFetcherOrder = Array.prototype.map.call(section.querySelectorAll('.imageFetcherItem'), function(elem) { + return elem.getAttribute('data-pluginname'); }); } } @@ -485,40 +501,40 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct function getLibraryOptions(parent) { var options = { EnableArchiveMediaFiles: false, - EnablePhotos: parent.querySelector(".chkEnablePhotos").checked, - EnableRealtimeMonitor: parent.querySelector(".chkEnableRealtimeMonitor").checked, - ExtractChapterImagesDuringLibraryScan: parent.querySelector(".chkExtractChaptersDuringLibraryScan").checked, - EnableChapterImageExtraction: parent.querySelector(".chkExtractChapterImages").checked, - DownloadImagesInAdvance: parent.querySelector("#chkDownloadImagesInAdvance").checked, + EnablePhotos: parent.querySelector('.chkEnablePhotos').checked, + EnableRealtimeMonitor: parent.querySelector('.chkEnableRealtimeMonitor').checked, + ExtractChapterImagesDuringLibraryScan: parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked, + EnableChapterImageExtraction: parent.querySelector('.chkExtractChapterImages').checked, + DownloadImagesInAdvance: parent.querySelector('#chkDownloadImagesInAdvance').checked, EnableInternetProviders: true, - ImportMissingEpisodes: parent.querySelector("#chkImportMissingEpisodes").checked, - SaveLocalMetadata: parent.querySelector("#chkSaveLocal").checked, - EnableAutomaticSeriesGrouping: parent.querySelector(".chkAutomaticallyGroupSeries").checked, - PreferredMetadataLanguage: parent.querySelector("#selectLanguage").value, - MetadataCountryCode: parent.querySelector("#selectCountry").value, - SeasonZeroDisplayName: parent.querySelector("#txtSeasonZeroName").value, - AutomaticRefreshIntervalDays: parseInt(parent.querySelector("#selectAutoRefreshInterval").value), - EnableEmbeddedTitles: parent.querySelector("#chkEnableEmbeddedTitles").checked, - EnableEmbeddedEpisodeInfos: parent.querySelector("#chkEnableEmbeddedEpisodeInfos").checked, - SkipSubtitlesIfEmbeddedSubtitlesPresent: parent.querySelector("#chkSkipIfGraphicalSubsPresent").checked, - SkipSubtitlesIfAudioTrackMatches: parent.querySelector("#chkSkipIfAudioTrackPresent").checked, - SaveSubtitlesWithMedia: parent.querySelector("#chkSaveSubtitlesLocally").checked, - RequirePerfectSubtitleMatch: parent.querySelector("#chkRequirePerfectMatch").checked, - MetadataSavers: Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll(".chkMetadataSaver"), function(elem) { + ImportMissingEpisodes: parent.querySelector('#chkImportMissingEpisodes').checked, + SaveLocalMetadata: parent.querySelector('#chkSaveLocal').checked, + EnableAutomaticSeriesGrouping: parent.querySelector('.chkAutomaticallyGroupSeries').checked, + PreferredMetadataLanguage: parent.querySelector('#selectLanguage').value, + MetadataCountryCode: parent.querySelector('#selectCountry').value, + SeasonZeroDisplayName: parent.querySelector('#txtSeasonZeroName').value, + AutomaticRefreshIntervalDays: parseInt(parent.querySelector('#selectAutoRefreshInterval').value), + EnableEmbeddedTitles: parent.querySelector('#chkEnableEmbeddedTitles').checked, + EnableEmbeddedEpisodeInfos: parent.querySelector('#chkEnableEmbeddedEpisodeInfos').checked, + SkipSubtitlesIfEmbeddedSubtitlesPresent: parent.querySelector('#chkSkipIfGraphicalSubsPresent').checked, + SkipSubtitlesIfAudioTrackMatches: parent.querySelector('#chkSkipIfAudioTrackPresent').checked, + SaveSubtitlesWithMedia: parent.querySelector('#chkSaveSubtitlesLocally').checked, + RequirePerfectSubtitleMatch: parent.querySelector('#chkRequirePerfectMatch').checked, + MetadataSavers: Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll('.chkMetadataSaver'), function(elem) { return elem.checked; }), function(elem) { - return elem.getAttribute("data-pluginname"); + return elem.getAttribute('data-pluginname'); }), TypeOptions: [] }; - options.LocalMetadataReaderOrder = Array.prototype.map.call(parent.querySelectorAll(".localReaderOption"), function(elem) { - return elem.getAttribute("data-pluginname"); + options.LocalMetadataReaderOrder = Array.prototype.map.call(parent.querySelectorAll('.localReaderOption'), function(elem) { + return elem.getAttribute('data-pluginname'); }); - options.SubtitleDownloadLanguages = Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll(".chkSubtitleLanguage"), function(elem) { + options.SubtitleDownloadLanguages = Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll('.chkSubtitleLanguage'), function(elem) { return elem.checked; }), function(elem) { - return elem.getAttribute("data-lang"); + return elem.getAttribute('data-lang'); }); setSubtitleFetchersIntoOptions(parent, options); setMetadataFetchersIntoOptions(parent, options); @@ -539,29 +555,29 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct function setLibraryOptions(parent, options) { currentLibraryOptions = options; currentAvailableOptions = parent.availableOptions; - parent.querySelector("#selectLanguage").value = options.PreferredMetadataLanguage || ""; - parent.querySelector("#selectCountry").value = options.MetadataCountryCode || ""; - parent.querySelector("#selectAutoRefreshInterval").value = options.AutomaticRefreshIntervalDays || "0"; - parent.querySelector("#txtSeasonZeroName").value = options.SeasonZeroDisplayName || "Specials"; - parent.querySelector(".chkEnablePhotos").checked = options.EnablePhotos; - parent.querySelector(".chkEnableRealtimeMonitor").checked = options.EnableRealtimeMonitor; - parent.querySelector(".chkExtractChaptersDuringLibraryScan").checked = options.ExtractChapterImagesDuringLibraryScan; - parent.querySelector(".chkExtractChapterImages").checked = options.EnableChapterImageExtraction; - parent.querySelector("#chkDownloadImagesInAdvance").checked = options.DownloadImagesInAdvance; - parent.querySelector("#chkSaveLocal").checked = options.SaveLocalMetadata; - parent.querySelector("#chkImportMissingEpisodes").checked = options.ImportMissingEpisodes; - parent.querySelector(".chkAutomaticallyGroupSeries").checked = options.EnableAutomaticSeriesGrouping; - parent.querySelector("#chkEnableEmbeddedTitles").checked = options.EnableEmbeddedTitles; - parent.querySelector("#chkEnableEmbeddedEpisodeInfos").checked = options.EnableEmbeddedEpisodeInfos; - parent.querySelector("#chkSkipIfGraphicalSubsPresent").checked = options.SkipSubtitlesIfEmbeddedSubtitlesPresent; - parent.querySelector("#chkSaveSubtitlesLocally").checked = options.SaveSubtitlesWithMedia; - parent.querySelector("#chkSkipIfAudioTrackPresent").checked = options.SkipSubtitlesIfAudioTrackMatches; - parent.querySelector("#chkRequirePerfectMatch").checked = options.RequirePerfectSubtitleMatch; - Array.prototype.forEach.call(parent.querySelectorAll(".chkMetadataSaver"), function(elem) { - elem.checked = options.MetadataSavers ? -1 !== options.MetadataSavers.indexOf(elem.getAttribute("data-pluginname")) : "true" === elem.getAttribute("data-defaultenabled"); + parent.querySelector('#selectLanguage').value = options.PreferredMetadataLanguage || ''; + parent.querySelector('#selectCountry').value = options.MetadataCountryCode || ''; + parent.querySelector('#selectAutoRefreshInterval').value = options.AutomaticRefreshIntervalDays || '0'; + parent.querySelector('#txtSeasonZeroName').value = options.SeasonZeroDisplayName || 'Specials'; + parent.querySelector('.chkEnablePhotos').checked = options.EnablePhotos; + parent.querySelector('.chkEnableRealtimeMonitor').checked = options.EnableRealtimeMonitor; + parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked = options.ExtractChapterImagesDuringLibraryScan; + parent.querySelector('.chkExtractChapterImages').checked = options.EnableChapterImageExtraction; + parent.querySelector('#chkDownloadImagesInAdvance').checked = options.DownloadImagesInAdvance; + parent.querySelector('#chkSaveLocal').checked = options.SaveLocalMetadata; + parent.querySelector('#chkImportMissingEpisodes').checked = options.ImportMissingEpisodes; + parent.querySelector('.chkAutomaticallyGroupSeries').checked = options.EnableAutomaticSeriesGrouping; + parent.querySelector('#chkEnableEmbeddedTitles').checked = options.EnableEmbeddedTitles; + parent.querySelector('#chkEnableEmbeddedEpisodeInfos').checked = options.EnableEmbeddedEpisodeInfos; + parent.querySelector('#chkSkipIfGraphicalSubsPresent').checked = options.SkipSubtitlesIfEmbeddedSubtitlesPresent; + parent.querySelector('#chkSaveSubtitlesLocally').checked = options.SaveSubtitlesWithMedia; + parent.querySelector('#chkSkipIfAudioTrackPresent').checked = options.SkipSubtitlesIfAudioTrackMatches; + parent.querySelector('#chkRequirePerfectMatch').checked = options.RequirePerfectSubtitleMatch; + Array.prototype.forEach.call(parent.querySelectorAll('.chkMetadataSaver'), function(elem) { + elem.checked = options.MetadataSavers ? -1 !== options.MetadataSavers.indexOf(elem.getAttribute('data-pluginname')) : 'true' === elem.getAttribute('data-defaultenabled'); }); - Array.prototype.forEach.call(parent.querySelectorAll(".chkSubtitleLanguage"), function(elem) { - elem.checked = !!options.SubtitleDownloadLanguages && -1 !== options.SubtitleDownloadLanguages.indexOf(elem.getAttribute("data-lang")); + Array.prototype.forEach.call(parent.querySelectorAll('.chkSubtitleLanguage'), function(elem) { + elem.checked = !!options.SubtitleDownloadLanguages && -1 !== options.SubtitleDownloadLanguages.indexOf(elem.getAttribute('data-lang')); }); renderMetadataReaders(parent, getOrderedPlugins(parent.availableOptions.MetadataReaders, options.LocalMetadataReaderOrder || [])); renderMetadataFetchers(parent, parent.availableOptions, options); diff --git a/src/components/libraryoptionseditor/libraryoptionseditor.template.html b/src/components/libraryoptionseditor/libraryoptionseditor.template.html index caa177108d..92cebc9da4 100644 --- a/src/components/libraryoptionseditor/libraryoptionseditor.template.html +++ b/src/components/libraryoptionseditor/libraryoptionseditor.template.html @@ -79,7 +79,7 @@
${OptionAutomaticallyGroupSeriesHelp}
diff --git a/src/components/listview/listview.js b/src/components/listview/listview.js index 532ba094a8..7bafc925b2 100644 --- a/src/components/listview/listview.js +++ b/src/components/listview/listview.js @@ -70,52 +70,53 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan function getImageUrl(item, width) { var apiClient = connectionManager.getApiClient(item.ServerId); + let itemId; var options = { maxWidth: width * 2, - type: "Primary" + type: 'Primary' }; if (item.ImageTags && item.ImageTags.Primary) { - options.tag = item.ImageTags.Primary; - return apiClient.getScaledImageUrl(item.Id, options); + itemId = item.Id; } if (item.AlbumId && item.AlbumPrimaryImageTag) { - options.tag = item.AlbumPrimaryImageTag; - return apiClient.getScaledImageUrl(item.AlbumId, options); + itemId = item.AlbumId; } else if (item.SeriesId && item.SeriesPrimaryImageTag) { - options.tag = item.SeriesPrimaryImageTag; - return apiClient.getScaledImageUrl(item.SeriesId, options); - + itemId = item.SeriesId; } else if (item.ParentPrimaryImageTag) { - options.tag = item.ParentPrimaryImageTag; - return apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, options); + itemId = item.ParentPrimaryImageItemId; } + let blurHashes = item.ImageBlurHashes || {}; + let blurhashstr = (blurHashes[options.type] || {})[options.tag]; - return null; + if (itemId) { + return { url: apiClient.getScaledImageUrl(itemId, options), blurhash: blurhashstr }; + } } function getChannelImageUrl(item, width) { var apiClient = connectionManager.getApiClient(item.ServerId); - var options = { maxWidth: width * 2, - type: "Primary" + type: 'Primary' }; if (item.ChannelId && item.ChannelPrimaryImageTag) { - options.tag = item.ChannelPrimaryImageTag; - return apiClient.getScaledImageUrl(item.ChannelId, options); } + let blurHashes = item.ImageBlurHashes || {}; + let blurhashstr = (blurHashes[options.type])[options.tag]; - return null; + if (item.ChannelId) { + return { url: apiClient.getScaledImageUrl(item.ChannelId, options), blurhash: blurhashstr }; + } } function getTextLinesHtml(textlines, isLargeStyle) { @@ -160,7 +161,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan var button = options.rightButtons[i]; - html += ''; + html += ''; } return html; @@ -219,7 +220,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan } } - var cssClass = "listItem"; + var cssClass = 'listItem'; if (options.border || (options.highlight !== false && !layoutManager.tv)) { cssClass += ' listItem-border'; @@ -236,7 +237,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan var downloadWidth = 80; if (isLargeStyle) { - cssClass += " listItem-largeImage"; + cssClass += ' listItem-largeImage'; downloadWidth = 500; } @@ -262,14 +263,16 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan } if (!clickEntireItem && options.dragHandle) { - //html += ''; + //html += ''; // Firefox and Edge are not allowing the button to be draggable - html += ''; + html += ''; } if (options.image !== false) { - var imgUrl = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth); - var imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage'; + let imgData = options.imageSource === 'channel' ? getChannelImageUrl(item, downloadWidth) : getImageUrl(item, downloadWidth); + let imgUrl = imgData.url; + let blurhash = imgData.blurhash; + let imageClass = isLargeStyle ? 'listItemImage listItemImage-large' : 'listItemImage'; if (isLargeStyle && layoutManager.tv) { imageClass += ' listItemImage-large-tv'; @@ -283,8 +286,13 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan var imageAction = playOnImageClick ? 'resume' : action; + let blurhashAttrib = ''; + if (blurhash && blurhash.length > 0) { + blurhashAttrib = 'data-blurhash="' + blurhash + '"'; + } + if (imgUrl) { - html += '
'; + html += '
'; } else { html += '
'; } @@ -297,7 +305,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan } if (playOnImageClick) { - html += ''; + html += ''; } var progressHtml = indicators.getProgressBarHtml(item, { @@ -355,7 +363,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan }); if (options.showIndexNumber && item.IndexNumber != null) { - displayName = item.IndexNumber + ". " + displayName; + displayName = item.IndexNumber + '. ' + displayName; } if (options.showParentTitle && options.parentTitleWithTitle) { @@ -426,7 +434,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan html += '
'; - var moreIcon = ''; + const moreIcon = 'more_vert'; html += getTextLinesHtml(textlines, isLargeStyle); @@ -475,15 +483,15 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan if (!clickEntireItem) { if (options.addToListButton) { - html += ''; + html += ''; } if (options.moreButton !== false) { - html += ''; + html += ''; } if (options.infoButton) { - html += ''; + html += ''; } if (options.rightButtons) { @@ -496,11 +504,11 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan var likes = userData.Likes == null ? '' : userData.Likes; if (itemHelper.canMarkPlayed(item)) { - html += ''; + html += ''; } if (itemHelper.canRate(item)) { - html += ''; + html += ''; } } } diff --git a/src/components/loading/loading.js b/src/components/loading/loading.js index d6c00347c7..9a9aa3ca7d 100644 --- a/src/components/loading/loading.js +++ b/src/components/loading/loading.js @@ -19,7 +19,7 @@ define(['components/loading/loadingLegacy', 'browser', 'css!./loading'], functio if (!elem) { - elem = document.createElement("div"); + elem = document.createElement('div'); loadingElem = elem; elem.classList.add('docspinner'); diff --git a/src/components/loading/loadingLegacy.js b/src/components/loading/loadingLegacy.js index 757cea0160..d766a4aca4 100644 --- a/src/components/loading/loadingLegacy.js +++ b/src/components/loading/loadingLegacy.js @@ -7,7 +7,7 @@ define(['require', 'css!./loadingLegacy'], function (require) { show: function () { var elem = loadingElem; if (!elem) { - elem = document.createElement("img"); + elem = document.createElement('img'); elem.src = require.toUrl('.').split('?')[0] + '/loader.gif'; loadingElem = elem; diff --git a/src/components/loadingdialog/loadingdialog.js b/src/components/loadingDialog/loadingDialog.js similarity index 100% rename from src/components/loadingdialog/loadingdialog.js rename to src/components/loadingDialog/loadingDialog.js diff --git a/src/components/logoscreensaver/plugin.js b/src/components/logoscreensaver/plugin.js deleted file mode 100644 index 2becfad0c3..0000000000 --- a/src/components/logoscreensaver/plugin.js +++ /dev/null @@ -1,192 +0,0 @@ -define(["pluginManager"], function (pluginManager) { - - return function () { - - var self = this; - - self.name = "Logo ScreenSaver"; - self.type = "screensaver"; - self.id = "logoscreensaver"; - self.supportsAnonymous = true; - - var interval; - - function animate() { - - var animations = [ - - bounceInLeft, - bounceInRight, - swing, - tada, - wobble, - rotateIn, - rotateOut - ]; - - var elem = document.querySelector(".logoScreenSaverImage"); - - if (elem && elem.animate) { - var random = getRandomInt(0, animations.length - 1); - - animations[random](elem, 1); - } - } - - function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; - } - - function bounceInLeft(elem, iterations) { - var keyframes = [ - { transform: "translate3d(-3000px, 0, 0)", opacity: "0", offset: 0 }, - { transform: "translate3d(25px, 0, 0)", opacity: "1", offset: 0.6 }, - { transform: "translate3d(-100px, 0, 0)", offset: 0.75 }, - { transform: "translate3d(5px, 0, 0)", offset: 0.9 }, - { transform: "none", opacity: "1", offset: 1 }]; - var timing = { duration: 900, iterations: iterations, easing: "cubic-bezier(0.215, 0.610, 0.355, 1.000)" }; - return elem.animate(keyframes, timing); - } - - function bounceInRight(elem, iterations) { - var keyframes = [ - { transform: "translate3d(3000px, 0, 0)", opacity: "0", offset: 0 }, - { transform: "translate3d(-25px, 0, 0)", opacity: "1", offset: 0.6 }, - { transform: "translate3d(100px, 0, 0)", offset: 0.75 }, - { transform: "translate3d(-5px, 0, 0)", offset: 0.9 }, - { transform: "none", opacity: "1", offset: 1 }]; - var timing = { duration: 900, iterations: iterations, easing: "cubic-bezier(0.215, 0.610, 0.355, 1.000)" }; - return elem.animate(keyframes, timing); - } - - function shake(elem, iterations) { - var keyframes = [ - { transform: "translate3d(0, 0, 0)", offset: 0 }, - { transform: "translate3d(-10px, 0, 0)", offset: 0.1 }, - { transform: "translate3d(10px, 0, 0)", offset: 0.2 }, - { transform: "translate3d(-10px, 0, 0)", offset: 0.3 }, - { transform: "translate3d(10px, 0, 0)", offset: 0.4 }, - { transform: "translate3d(-10px, 0, 0)", offset: 0.5 }, - { transform: "translate3d(10px, 0, 0)", offset: 0.6 }, - { transform: "translate3d(-10px, 0, 0)", offset: 0.7 }, - { transform: "translate3d(10px, 0, 0)", offset: 0.8 }, - { transform: "translate3d(-10px, 0, 0)", offset: 0.9 }, - { transform: "translate3d(0, 0, 0)", offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function swing(elem, iterations) { - var keyframes = [ - { transform: "translate(0%)", offset: 0 }, - { transform: "rotate3d(0, 0, 1, 15deg)", offset: 0.2 }, - { transform: "rotate3d(0, 0, 1, -10deg)", offset: 0.4 }, - { transform: "rotate3d(0, 0, 1, 5deg)", offset: 0.6 }, - { transform: "rotate3d(0, 0, 1, -5deg)", offset: 0.8 }, - { transform: "rotate3d(0, 0, 1, 0deg)", offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function tada(elem, iterations) { - var keyframes = [ - { transform: "scale3d(1, 1, 1)", offset: 0 }, - { transform: "scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)", offset: 0.1 }, - { transform: "scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)", offset: 0.2 }, - { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)", offset: 0.3 }, - { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)", offset: 0.4 }, - { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)", offset: 0.5 }, - { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)", offset: 0.6 }, - { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)", offset: 0.7 }, - { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)", offset: 0.8 }, - { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)", offset: 0.9 }, - { transform: "scale3d(1, 1, 1)", offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function wobble(elem, iterations) { - var keyframes = [ - { transform: "translate(0%)", offset: 0 }, - { transform: "translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)", offset: 0.15 }, - { transform: "translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)", offset: 0.45 }, - { transform: "translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)", offset: 0.6 }, - { transform: "translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)", offset: 0.75 }, - { transform: "translateX(0%)", offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function rotateIn(elem, iterations) { - var transformOrigin = elem.style["transform-origin"]; - var keyframes = [{ transform: "rotate3d(0, 0, 1, -200deg)", opacity: "0", transformOrigin: "center", offset: 0 }, - { transform: "none", opacity: "1", transformOrigin: "center", offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function rotateOut(elem, iterations) { - var transformOrigin = elem.style["transform-origin"]; - var keyframes = [{ transform: "none", opacity: "1", transformOrigin: "center", offset: 0 }, - { transform: "rotate3d(0, 0, 1, 200deg)", opacity: "0", transformOrigin: "center", offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - - } - - function fadeOut(elem, iterations) { - var keyframes = [ - { opacity: "1", offset: 0 }, - { opacity: "0", offset: 1 }]; - var timing = { duration: 400, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function stopInterval() { - if (interval) { - clearInterval(interval); - interval = null; - } - } - - self.show = function () { - - require(["css!" + pluginManager.mapPath(self, "style.css")], function () { - - var elem = document.querySelector(".logoScreenSaver"); - - if (!elem) { - elem = document.createElement("div"); - elem.classList.add("logoScreenSaver"); - document.body.appendChild(elem); - - elem.innerHTML = ''; - } - - stopInterval(); - interval = setInterval(animate, 3000); - }); - }; - - self.hide = function () { - - stopInterval(); - - var elem = document.querySelector(".logoScreenSaver"); - - if (elem) { - - var onAnimationFinish = function () { - elem.parentNode.removeChild(elem); - }; - - if (elem.animate) { - var animation = fadeOut(elem, 1); - animation.onfinish = onAnimationFinish; - } else { - onAnimationFinish(); - } - } - }; - }; -}); diff --git a/src/components/maintabsmanager.js b/src/components/maintabsmanager.js index 2571470015..e1c5434363 100644 --- a/src/components/maintabsmanager.js +++ b/src/components/maintabsmanager.js @@ -166,6 +166,7 @@ define(['dom', 'browser', 'events', 'emby-tabs', 'emby-button'], function (dom, }).join('') + '
'; tabsContainerElem.innerHTML = tabsHtml; + window.CustomElements.upgradeSubtree(tabsContainerElem); document.body.classList.add('withSectionTabs'); tabOwnerView = view; diff --git a/src/components/medialibrarycreator/medialibrarycreator.js b/src/components/mediaLibraryCreator/mediaLibraryCreator.js similarity index 56% rename from src/components/medialibrarycreator/medialibrarycreator.js rename to src/components/mediaLibraryCreator/mediaLibraryCreator.js index 0a8741387f..bbef6e1f07 100644 --- a/src/components/medialibrarycreator/medialibrarycreator.js +++ b/src/components/mediaLibraryCreator/mediaLibraryCreator.js @@ -1,5 +1,5 @@ -define(["loading", "dialogHelper", "dom", "jQuery", "components/libraryoptionseditor/libraryoptionseditor", "emby-toggle", "emby-input", "emby-select", "paper-icon-button-light", "listViewStyle", "formDialogStyle", "emby-button", "flexStyles"], function (loading, dialogHelper, dom, $, libraryoptionseditor) { - "use strict"; +define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionseditor/libraryoptionseditor', 'globalize', 'emby-toggle', 'emby-input', 'emby-select', 'paper-icon-button-light', 'listViewStyle', 'formDialogStyle', 'emby-button', 'flexStyles'], function (loading, dialogHelper, dom, $, libraryoptionseditor, globalize) { + 'use strict'; function onAddLibrary() { if (isCreating) { @@ -7,10 +7,10 @@ define(["loading", "dialogHelper", "dom", "jQuery", "components/libraryoptionsed } if (pathInfos.length == 0) { - require(["alert"], function (alert) { + require(['alert'], function (alert) { alert({ - text: Globalize.translate("PleaseAddAtLeastOneFolder"), - type: "error" + text: globalize.translate('PleaseAddAtLeastOneFolder'), + type: 'error' }); }); @@ -19,15 +19,15 @@ define(["loading", "dialogHelper", "dom", "jQuery", "components/libraryoptionsed isCreating = true; loading.show(); - var dlg = dom.parentWithClass(this, "dlg-librarycreator"); - var name = $("#txtValue", dlg).val(); - var type = $("#selectCollectionType", dlg).val(); + var dlg = dom.parentWithClass(this, 'dlg-librarycreator'); + var name = $('#txtValue', dlg).val(); + var type = $('#selectCollectionType', dlg).val(); - if (type == "mixed") { + if (type == 'mixed') { type = null; } - var libraryOptions = libraryoptionseditor.getLibraryOptions(dlg.querySelector(".libraryOptions")); + var libraryOptions = libraryoptionseditor.getLibraryOptions(dlg.querySelector('.libraryOptions')); libraryOptions.PathInfos = pathInfos; ApiClient.addVirtualFolder(name, type, currentOptions.refresh, libraryOptions).then(function () { hasChanges = true; @@ -35,8 +35,8 @@ define(["loading", "dialogHelper", "dom", "jQuery", "components/libraryoptionsed loading.hide(); dialogHelper.close(dlg); }, function () { - require(["toast"], function (toast) { - toast(Globalize.translate("ErrorAddingMediaPathToVirtualFolder")); + require(['toast'], function (toast) { + toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder')); }); isCreating = false; @@ -47,50 +47,50 @@ define(["loading", "dialogHelper", "dom", "jQuery", "components/libraryoptionsed function getCollectionTypeOptionsHtml(collectionTypeOptions) { return collectionTypeOptions.map(function (i) { - return '"; - }).join(""); + return ''; + }).join(''); } function initEditor(page, collectionTypeOptions) { - $("#selectCollectionType", page).html(getCollectionTypeOptionsHtml(collectionTypeOptions)).val("").on("change", function () { + $('#selectCollectionType', page).html(getCollectionTypeOptionsHtml(collectionTypeOptions)).val('').on('change', function () { var value = this.value; - var dlg = $(this).parents(".dialog")[0]; - libraryoptionseditor.setContentType(dlg.querySelector(".libraryOptions"), value == "mixed" ? "" : value); + var dlg = $(this).parents('.dialog')[0]; + libraryoptionseditor.setContentType(dlg.querySelector('.libraryOptions'), value == 'mixed' ? '' : value); if (value) { - dlg.querySelector(".libraryOptions").classList.remove("hide"); + dlg.querySelector('.libraryOptions').classList.remove('hide'); } else { - dlg.querySelector(".libraryOptions").classList.add("hide"); + dlg.querySelector('.libraryOptions').classList.add('hide'); } - if (value != "mixed") { + if (value != 'mixed') { var index = this.selectedIndex; if (index != -1) { - var name = this.options[index].innerHTML.replace("*", "").replace("&", "&"); - $("#txtValue", dlg).val(name); + var name = this.options[index].innerHTML.replace('*', '').replace('&', '&'); + $('#txtValue', dlg).val(name); var folderOption = collectionTypeOptions.filter(function (i) { return i.value == value; })[0]; - $(".collectionTypeFieldDescription", dlg).html(folderOption.message || ""); + $('.collectionTypeFieldDescription', dlg).html(folderOption.message || ''); } } }); - page.querySelector(".btnAddFolder").addEventListener("click", onAddButtonClick); - page.querySelector(".btnSubmit").addEventListener("click", onAddLibrary); - page.querySelector(".folderList").addEventListener("click", onRemoveClick); - page.querySelector(".chkAdvanced").addEventListener("change", onToggleAdvancedChange); + page.querySelector('.btnAddFolder').addEventListener('click', onAddButtonClick); + page.querySelector('.btnSubmit').addEventListener('click', onAddLibrary); + page.querySelector('.folderList').addEventListener('click', onRemoveClick); + page.querySelector('.chkAdvanced').addEventListener('change', onToggleAdvancedChange); } function onToggleAdvancedChange() { - var dlg = dom.parentWithClass(this, "dlg-librarycreator"); - libraryoptionseditor.setAdvancedVisible(dlg.querySelector(".libraryOptions"), this.checked); + var dlg = dom.parentWithClass(this, 'dlg-librarycreator'); + libraryoptionseditor.setAdvancedVisible(dlg.querySelector('.libraryOptions'), this.checked); } function onAddButtonClick() { - var page = dom.parentWithClass(this, "dlg-librarycreator"); + var page = dom.parentWithClass(this, 'dlg-librarycreator'); - require(["directorybrowser"], function (directoryBrowser) { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ enableNetworkSharePath: true, @@ -106,30 +106,30 @@ define(["loading", "dialogHelper", "dom", "jQuery", "components/libraryoptionsed } function getFolderHtml(pathInfo, index) { - var html = ""; + var html = ''; html += '
'; - html += '
'; - html += '
' + pathInfo.Path + "
"; + html += '
'; + html += '
' + pathInfo.Path + '
'; if (pathInfo.NetworkPath) { - html += '
' + pathInfo.NetworkPath + "
"; + html += '
' + pathInfo.NetworkPath + '
'; } - html += "
"; - html += ''; - html += "
"; + html += '
'; + html += ''; + html += '
'; return html; } function renderPaths(page) { - var foldersHtml = pathInfos.map(getFolderHtml).join(""); - var folderList = page.querySelector(".folderList"); + var foldersHtml = pathInfos.map(getFolderHtml).join(''); + var folderList = page.querySelector('.folderList'); folderList.innerHTML = foldersHtml; if (foldersHtml) { - folderList.classList.remove("hide"); + folderList.classList.remove('hide'); } else { - folderList.classList.add("hide"); + folderList.classList.add('hide'); } } @@ -154,14 +154,14 @@ define(["loading", "dialogHelper", "dom", "jQuery", "components/libraryoptionsed } function onRemoveClick(e) { - var button = dom.parentWithClass(e.target, "btnRemovePath"); - var index = parseInt(button.getAttribute("data-index")); + var button = dom.parentWithClass(e.target, 'btnRemovePath'); + var index = parseInt(button.getAttribute('data-index')); var location = pathInfos[index].Path; var locationLower = location.toLowerCase(); pathInfos = pathInfos.filter(function (p) { return p.Path.toLowerCase() != locationLower; }); - renderPaths(dom.parentWithClass(button, "dlg-librarycreator")); + renderPaths(dom.parentWithClass(button, 'dlg-librarycreator')); } function onDialogClosed() { @@ -169,9 +169,9 @@ define(["loading", "dialogHelper", "dom", "jQuery", "components/libraryoptionsed } function initLibraryOptions(dlg) { - libraryoptionseditor.embed(dlg.querySelector(".libraryOptions")).then(function () { - $("#selectCollectionType", dlg).trigger("change"); - onToggleAdvancedChange.call(dlg.querySelector(".chkAdvanced")); + libraryoptionseditor.embed(dlg.querySelector('.libraryOptions')).then(function () { + $('#selectCollectionType', dlg).trigger('change'); + onToggleAdvancedChange.call(dlg.querySelector('.chkAdvanced')); }); } @@ -182,25 +182,25 @@ define(["loading", "dialogHelper", "dom", "jQuery", "components/libraryoptionsed currentResolve = resolve; hasChanges = false; var xhr = new XMLHttpRequest(); - xhr.open("GET", "components/medialibrarycreator/medialibrarycreator.template.html", true); + xhr.open('GET', 'components/mediaLibraryCreator/mediaLibraryCreator.template.html', true); xhr.onload = function (e) { var template = this.response; var dlg = dialogHelper.createDialog({ - size: "medium-tall", + size: 'small', modal: false, removeOnClose: true, scrollY: false }); - dlg.classList.add("ui-body-a"); - dlg.classList.add("background-theme-a"); - dlg.classList.add("dlg-librarycreator"); - dlg.classList.add("formDialog"); - dlg.innerHTML = Globalize.translateDocument(template); + dlg.classList.add('ui-body-a'); + dlg.classList.add('background-theme-a'); + dlg.classList.add('dlg-librarycreator'); + dlg.classList.add('formDialog'); + dlg.innerHTML = globalize.translateDocument(template); initEditor(dlg, options.collectionTypeOptions); - dlg.addEventListener("close", onDialogClosed); + dlg.addEventListener('close', onDialogClosed); dialogHelper.open(dlg); - dlg.querySelector(".btnCancel").addEventListener("click", function () { + dlg.querySelector('.btnCancel').addEventListener('click', function () { dialogHelper.close(dlg); }); pathInfos = []; diff --git a/src/components/medialibrarycreator/medialibrarycreator.template.html b/src/components/mediaLibraryCreator/mediaLibraryCreator.template.html similarity index 92% rename from src/components/medialibrarycreator/medialibrarycreator.template.html rename to src/components/mediaLibraryCreator/mediaLibraryCreator.template.html index 32c6b254b7..f92a63e403 100644 --- a/src/components/medialibrarycreator/medialibrarycreator.template.html +++ b/src/components/mediaLibraryCreator/mediaLibraryCreator.template.html @@ -1,5 +1,5 @@
- +

${ButtonAddMediaLibrary}

@@ -26,7 +26,7 @@

${HeadersFolders}

diff --git a/src/components/medialibraryeditor/medialibraryeditor.js b/src/components/mediaLibraryEditor/mediaLibraryEditor.js similarity index 64% rename from src/components/medialibraryeditor/medialibraryeditor.js rename to src/components/mediaLibraryEditor/mediaLibraryEditor.js index 7c5c8d4080..554cf4cc0f 100644 --- a/src/components/medialibraryeditor/medialibraryeditor.js +++ b/src/components/mediaLibraryEditor/mediaLibraryEditor.js @@ -1,5 +1,5 @@ -define(["jQuery", "loading", "dialogHelper", "dom", "components/libraryoptionseditor/libraryoptionseditor", "emby-button", "listViewStyle", "paper-icon-button-light", "formDialogStyle", "emby-toggle", "flexStyles"], function (jQuery, loading, dialogHelper, dom, libraryoptionseditor) { - "use strict"; +define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionseditor/libraryoptionseditor', 'globalize', 'emby-button', 'listViewStyle', 'paper-icon-button-light', 'formDialogStyle', 'emby-toggle', 'flexStyles'], function (jQuery, loading, dialogHelper, dom, libraryoptionseditor, globalize) { + 'use strict'; function onEditLibrary() { if (isCreating) { @@ -8,8 +8,8 @@ define(["jQuery", "loading", "dialogHelper", "dom", "components/libraryoptionsed isCreating = true; loading.show(); - var dlg = dom.parentWithClass(this, "dlg-libraryeditor"); - var libraryOptions = libraryoptionseditor.getLibraryOptions(dlg.querySelector(".libraryOptions")); + var dlg = dom.parentWithClass(this, 'dlg-libraryeditor'); + var libraryOptions = libraryoptionseditor.getLibraryOptions(dlg.querySelector('.libraryOptions')); libraryOptions = Object.assign(currentOptions.library.LibraryOptions || {}, libraryOptions); ApiClient.updateVirtualFolderOptions(currentOptions.library.ItemId, libraryOptions).then(function () { hasChanges = true; @@ -30,8 +30,8 @@ define(["jQuery", "loading", "dialogHelper", "dom", "components/libraryoptionsed hasChanges = true; refreshLibraryFromServer(page); }, function () { - require(["toast"], function (toast) { - toast(Globalize.translate("ErrorAddingMediaPathToVirtualFolder")); + require(['toast'], function (toast) { + toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder')); }); }); } @@ -45,8 +45,8 @@ define(["jQuery", "loading", "dialogHelper", "dom", "components/libraryoptionsed hasChanges = true; refreshLibraryFromServer(page); }, function () { - require(["toast"], function (toast) { - toast(Globalize.translate("ErrorAddingMediaPathToVirtualFolder")); + require(['toast'], function (toast) { + toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder')); }); }); } @@ -55,20 +55,20 @@ define(["jQuery", "loading", "dialogHelper", "dom", "components/libraryoptionsed var button = btnRemovePath; var virtualFolder = currentOptions.library; - require(["confirm"], function (confirm) { + require(['confirm'], function (confirm) { confirm({ - title: Globalize.translate("HeaderRemoveMediaLocation"), - text: Globalize.translate("MessageConfirmRemoveMediaLocation"), - confirmText: Globalize.translate("ButtonDelete"), - primary: "delete" + title: globalize.translate('HeaderRemoveMediaLocation'), + text: globalize.translate('MessageConfirmRemoveMediaLocation'), + confirmText: globalize.translate('ButtonDelete'), + primary: 'delete' }).then(function () { var refreshAfterChange = currentOptions.refresh; ApiClient.removeMediaPath(virtualFolder.Name, location, refreshAfterChange).then(function () { hasChanges = true; - refreshLibraryFromServer(dom.parentWithClass(button, "dlg-libraryeditor")); + refreshLibraryFromServer(dom.parentWithClass(button, 'dlg-libraryeditor')); }, function () { - require(["toast"], function (toast) { - toast(Globalize.translate("DefaultErrorMessage")); + require(['toast'], function (toast) { + toast(globalize.translate('DefaultErrorMessage')); }); }); }); @@ -76,39 +76,39 @@ define(["jQuery", "loading", "dialogHelper", "dom", "components/libraryoptionsed } function onListItemClick(e) { - var listItem = dom.parentWithClass(e.target, "listItem"); + var listItem = dom.parentWithClass(e.target, 'listItem'); if (listItem) { - var index = parseInt(listItem.getAttribute("data-index")); + var index = parseInt(listItem.getAttribute('data-index')); var pathInfos = (currentOptions.library.LibraryOptions || {}).PathInfos || []; var pathInfo = null == index ? {} : pathInfos[index] || {}; var originalPath = pathInfo.Path || (null == index ? null : currentOptions.library.Locations[index]); - var btnRemovePath = dom.parentWithClass(e.target, "btnRemovePath"); + var btnRemovePath = dom.parentWithClass(e.target, 'btnRemovePath'); if (btnRemovePath) { onRemoveClick(btnRemovePath, originalPath); return; } - showDirectoryBrowser(dom.parentWithClass(listItem, "dlg-libraryeditor"), originalPath, pathInfo.NetworkPath); + showDirectoryBrowser(dom.parentWithClass(listItem, 'dlg-libraryeditor'), originalPath, pathInfo.NetworkPath); } } function getFolderHtml(pathInfo, index) { - var html = ""; + var html = ''; html += '
'; - html += '
'; + html += '
'; html += '

'; html += pathInfo.Path; - html += "

"; + html += ''; if (pathInfo.NetworkPath) { - html += '
' + pathInfo.NetworkPath + "
"; + html += '
' + pathInfo.NetworkPath + '
'; } - html += "
"; - html += ''; - html += "
"; + html += '
'; + html += ''; + html += '
'; return html; } @@ -137,20 +137,20 @@ define(["jQuery", "loading", "dialogHelper", "dom", "components/libraryoptionsed } if (options.library.CollectionType === 'boxsets') { - page.querySelector(".folders").classList.add("hide"); + page.querySelector('.folders').classList.add('hide'); } else { - page.querySelector(".folders").classList.remove("hide"); + page.querySelector('.folders').classList.remove('hide'); } - page.querySelector(".folderList").innerHTML = pathInfos.map(getFolderHtml).join(""); + page.querySelector('.folderList').innerHTML = pathInfos.map(getFolderHtml).join(''); } function onAddButtonClick() { - showDirectoryBrowser(dom.parentWithClass(this, "dlg-libraryeditor")); + showDirectoryBrowser(dom.parentWithClass(this, 'dlg-libraryeditor')); } function showDirectoryBrowser(context, originalPath, networkPath) { - require(["directorybrowser"], function (directoryBrowser) { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ enableNetworkSharePath: true, @@ -173,18 +173,18 @@ define(["jQuery", "loading", "dialogHelper", "dom", "components/libraryoptionsed } function onToggleAdvancedChange() { - var dlg = dom.parentWithClass(this, "dlg-libraryeditor"); - libraryoptionseditor.setAdvancedVisible(dlg.querySelector(".libraryOptions"), this.checked); + var dlg = dom.parentWithClass(this, 'dlg-libraryeditor'); + libraryoptionseditor.setAdvancedVisible(dlg.querySelector('.libraryOptions'), this.checked); } function initEditor(dlg, options) { renderLibrary(dlg, options); - dlg.querySelector(".btnAddFolder").addEventListener("click", onAddButtonClick); - dlg.querySelector(".folderList").addEventListener("click", onListItemClick); - dlg.querySelector(".chkAdvanced").addEventListener("change", onToggleAdvancedChange); - dlg.querySelector(".btnSubmit").addEventListener("click", onEditLibrary); - libraryoptionseditor.embed(dlg.querySelector(".libraryOptions"), options.library.CollectionType, options.library.LibraryOptions).then(function () { - onToggleAdvancedChange.call(dlg.querySelector(".chkAdvanced")); + dlg.querySelector('.btnAddFolder').addEventListener('click', onAddButtonClick); + dlg.querySelector('.folderList').addEventListener('click', onListItemClick); + dlg.querySelector('.chkAdvanced').addEventListener('change', onToggleAdvancedChange); + dlg.querySelector('.btnSubmit').addEventListener('click', onEditLibrary); + libraryoptionseditor.embed(dlg.querySelector('.libraryOptions'), options.library.CollectionType, options.library.LibraryOptions).then(function () { + onToggleAdvancedChange.call(dlg.querySelector('.chkAdvanced')); }); } @@ -199,26 +199,26 @@ define(["jQuery", "loading", "dialogHelper", "dom", "components/libraryoptionsed currentDeferred = deferred; hasChanges = false; var xhr = new XMLHttpRequest(); - xhr.open("GET", "components/medialibraryeditor/medialibraryeditor.template.html", true); + xhr.open('GET', 'components/mediaLibraryEditor/mediaLibraryEditor.template.html', true); xhr.onload = function (e) { var template = this.response; var dlg = dialogHelper.createDialog({ - size: "medium-tall", + size: 'small', modal: false, removeOnClose: true, scrollY: false }); - dlg.classList.add("dlg-libraryeditor"); - dlg.classList.add("ui-body-a"); - dlg.classList.add("background-theme-a"); - dlg.classList.add("formDialog"); - dlg.innerHTML = Globalize.translateDocument(template); - dlg.querySelector(".formDialogHeaderTitle").innerHTML = options.library.Name; + dlg.classList.add('dlg-libraryeditor'); + dlg.classList.add('ui-body-a'); + dlg.classList.add('background-theme-a'); + dlg.classList.add('formDialog'); + dlg.innerHTML = globalize.translateDocument(template); + dlg.querySelector('.formDialogHeaderTitle').innerHTML = options.library.Name; initEditor(dlg, options); - dlg.addEventListener("close", onDialogClosed); + dlg.addEventListener('close', onDialogClosed); dialogHelper.open(dlg); - dlg.querySelector(".btnCancel").addEventListener("click", function () { + dlg.querySelector('.btnCancel').addEventListener('click', function () { dialogHelper.close(dlg); }); refreshLibraryFromServer(dlg); diff --git a/src/components/medialibraryeditor/medialibraryeditor.template.html b/src/components/mediaLibraryEditor/mediaLibraryEditor.template.html similarity index 90% rename from src/components/medialibraryeditor/medialibraryeditor.template.html rename to src/components/mediaLibraryEditor/mediaLibraryEditor.template.html index 172c535fa1..6c814cf2dd 100644 --- a/src/components/medialibraryeditor/medialibraryeditor.template.html +++ b/src/components/mediaLibraryEditor/mediaLibraryEditor.template.html @@ -1,5 +1,5 @@
- +

@@ -20,7 +20,7 @@

${HeadersFolders}

diff --git a/src/components/mediainfo/mediainfo.js b/src/components/mediainfo/mediainfo.js index 188ea9a07c..7de11c42f7 100644 --- a/src/components/mediainfo/mediainfo.js +++ b/src/components/mediainfo/mediainfo.js @@ -6,7 +6,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater var status; if (item.Type === 'SeriesTimer') { - return ''; + return ''; } else if (item.TimerId || item.SeriesTimerId) { status = item.Status || 'Cancelled'; @@ -20,13 +20,13 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater if (item.SeriesTimerId) { if (status !== 'Cancelled') { - return ''; + return ''; } - return ''; + return ''; } - return ''; + return ''; } function getProgramInfoHtml(item, options) { @@ -57,7 +57,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater miscInfo.push(text); } catch (e) { - console.error("error parsing date: " + item.StartDate); + console.error('error parsing date: ' + item.StartDate); } } @@ -109,7 +109,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater var minutes; var count; - var showFolderRuntime = item.Type === "MusicAlbum" || item.MediaType === 'MusicArtist' || item.MediaType === 'Playlist' || item.MediaType === 'MusicGenre'; + var showFolderRuntime = item.Type === 'MusicAlbum' || item.MediaType === 'MusicArtist' || item.MediaType === 'Playlist' || item.MediaType === 'MusicGenre'; if (showFolderRuntime) { @@ -123,7 +123,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater if (item.RunTimeTicks) { miscInfo.push(datetime.getDisplayRunningTime(item.RunTimeTicks)); } - } else if (item.Type === "PhotoAlbum" || item.Type === "BoxSet") { + } else if (item.Type === 'PhotoAlbum' || item.Type === 'BoxSet') { count = item.ChildCount; @@ -133,7 +133,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater } } - if ((item.Type === "Episode" || item.MediaType === 'Photo') && options.originalAirDate !== false) { + if ((item.Type === 'Episode' || item.MediaType === 'Photo') && options.originalAirDate !== false) { if (item.PremiereDate) { @@ -143,7 +143,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater text = datetime.toLocaleDateString(date); miscInfo.push(text); } catch (e) { - console.error("error parsing date: " + item.PremiereDate); + console.error('error parsing date: ' + item.PremiereDate); } } } @@ -171,18 +171,18 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater text = datetime.toLocaleDateString(date); miscInfo.push(text); - if (item.Type !== "Recording") { + if (item.Type !== 'Recording') { text = datetime.getDisplayTime(date); miscInfo.push(text); } } catch (e) { - console.error("error parsing date: " + item.StartDate); + console.error('error parsing date: ' + item.StartDate); } } - if (options.year !== false && item.ProductionYear && item.Type === "Series") { + if (options.year !== false && item.ProductionYear && item.Type === 'Series') { - if (item.Status === "Continuing") { + if (item.Status === 'Continuing') { miscInfo.push(globalize.translate('SeriesYearToPresent', item.ProductionYear)); } else if (item.ProductionYear) { @@ -196,11 +196,11 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater var endYear = datetime.parseISO8601Date(item.EndDate).getFullYear(); if (endYear !== item.ProductionYear) { - text += "-" + datetime.parseISO8601Date(item.EndDate).getFullYear(); + text += '-' + datetime.parseISO8601Date(item.EndDate).getFullYear(); } } catch (e) { - console.error("error parsing date: " + item.EndDate); + console.error('error parsing date: ' + item.EndDate); } } @@ -248,7 +248,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater text = globalize.translate('OriginalAirDateValue', datetime.toLocaleDateString(date)); miscInfo.push(text); } catch (e) { - console.error("error parsing date: " + item.PremiereDate); + console.error('error parsing date: ' + item.PremiereDate); } } else if (item.ProductionYear) { miscInfo.push(item.ProductionYear); @@ -256,7 +256,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater } if (options.year !== false) { - if (item.Type !== "Series" && item.Type !== "Episode" && item.Type !== "Person" && item.MediaType !== 'Photo' && item.Type !== 'Program' && item.Type !== 'Season') { + if (item.Type !== 'Series' && item.Type !== 'Episode' && item.Type !== 'Person' && item.MediaType !== 'Photo' && item.Type !== 'Program' && item.Type !== 'Season') { if (item.ProductionYear) { @@ -267,15 +267,15 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater text = datetime.parseISO8601Date(item.PremiereDate).getFullYear(); miscInfo.push(text); } catch (e) { - console.error("error parsing date: " + item.PremiereDate); + console.error('error parsing date: ' + item.PremiereDate); } } } } - if (item.RunTimeTicks && item.Type !== "Series" && item.Type !== 'Program' && !showFolderRuntime && options.runtime !== false) { + if (item.RunTimeTicks && item.Type !== 'Series' && item.Type !== 'Program' && item.Type !== 'Book' && !showFolderRuntime && options.runtime !== false) { - if (item.Type === "Audio") { + if (item.Type === 'Audio') { miscInfo.push(datetime.getDisplayRunningTime(item.RunTimeTicks)); @@ -284,11 +284,11 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater minutes = minutes || 1; - miscInfo.push(Math.round(minutes) + " mins"); + miscInfo.push(Math.round(minutes) + ' mins'); } } - if (item.OfficialRating && item.Type !== "Season" && item.Type !== "Episode") { + if (item.OfficialRating && item.Type !== 'Season' && item.Type !== 'Episode') { miscInfo.push({ text: item.OfficialRating, cssClass: 'mediaInfoOfficialRating' @@ -296,11 +296,11 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater } if (item.Video3DFormat) { - miscInfo.push("3D"); + miscInfo.push('3D'); } if (item.MediaType === 'Photo' && item.Width && item.Height) { - miscInfo.push(item.Width + "x" + item.Height); + miscInfo.push(item.Width + 'x' + item.Height); } if (options.container !== false && item.Type === 'Audio' && item.Container) { @@ -390,7 +390,7 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater if (item.CommunityRating) { html += '
'; - html += 'star'; + html += ''; html += item.CommunityRating.toFixed(1); html += '
'; } @@ -490,26 +490,26 @@ define(['datetime', 'globalize', 'appRouter', 'itemHelper', 'indicators', 'mater if (i.IsInterlaced) { return '1440i'; } - return '1440P'; + return '1440p'; } if (width >= 1800 || height >= 1000) { if (i.IsInterlaced) { return '1080i'; } - return '1080P'; + return '1080p'; } if (width >= 1200 || height >= 700) { if (i.IsInterlaced) { return '720i'; } - return '720P'; + return '720p'; } if (width >= 700 || height >= 400) { if (i.IsInterlaced) { return '480i'; } - return '480P'; + return '480p'; } } diff --git a/src/components/metadataeditor/metadataeditor.js b/src/components/metadataEditor/metadataEditor.js similarity index 84% rename from src/components/metadataeditor/metadataeditor.js rename to src/components/metadataEditor/metadataEditor.js index 8a64cac7ef..52f030cd23 100644 --- a/src/components/metadataeditor/metadataeditor.js +++ b/src/components/metadataEditor/metadataEditor.js @@ -142,9 +142,9 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi Status: form.querySelector('#selectStatus').value, AirDays: getSelectedAirDays(form), AirTime: form.querySelector('#txtAirTime').value, - Genres: getListValues(form.querySelector("#listGenres")), - Tags: getListValues(form.querySelector("#listTags")), - Studios: getListValues(form.querySelector("#listStudios")).map(function (element) { + Genres: getListValues(form.querySelector('#listGenres')), + Tags: getListValues(form.querySelector('#listTags')), + Studios: getListValues(form.querySelector('#listStudios')).map(function (element) { return { Name: element }; }), @@ -158,7 +158,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi OfficialRating: form.querySelector('#selectOfficialRating').value, CustomRating: form.querySelector('#selectCustomRating').value, People: currentItem.People, - LockData: form.querySelector("#chkLockData").checked, + LockData: form.querySelector('#chkLockData').checked, LockedFields: Array.prototype.filter.call(form.querySelectorAll('.selectLockedField'), function (c) { return !c.checked; }).map(function (c) { @@ -177,14 +177,14 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi item.PreferredMetadataLanguage = form.querySelector('#selectLanguage').value; item.PreferredMetadataCountryCode = form.querySelector('#selectCountry').value; - if (currentItem.Type === "Person") { + if (currentItem.Type === 'Person') { var placeOfBirth = form.querySelector('#txtPlaceOfBirth').value; item.ProductionLocations = placeOfBirth ? [placeOfBirth] : []; } - if (currentItem.Type === "Series") { + if (currentItem.Type === 'Series') { // 600000000 var seriesRuntime = form.querySelector('#txtSeriesRuntime').value; @@ -245,50 +245,6 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi }); } - function showMoreMenu(context, button, user) { - - require(['itemContextMenu'], function (itemContextMenu) { - - var item = currentItem; - - itemContextMenu.show({ - - item: item, - positionTo: button, - edit: false, - editImages: true, - editSubtitles: true, - sync: false, - share: false, - play: false, - queue: false, - user: user - - }).then(function (result) { - - if (result.deleted) { - afterDeleted(context, item); - - } else if (result.updated) { - reload(context, item.Id, item.ServerId); - } - }); - }); - } - - function afterDeleted(context, item) { - - var parentId = item.ParentId || item.SeasonId || item.SeriesId; - - if (parentId) { - reload(context, parentId, item.ServerId); - } else { - require(['appRouter'], function (appRouter) { - appRouter.goHome(); - }); - } - } - function onEditorClick(e) { var btnRemoveFromEditorList = dom.parentWithClass(e.target, 'btnRemoveFromEditorList'); @@ -307,6 +263,12 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi return connectionManager.getApiClient(currentItem.ServerId); } + function bindAll(elems, eventName, fn) { + for (var i = 0, length = elems.length; i < length; i++) { + elems[i].addEventListener(eventName, fn); + } + } + function init(context, apiClient) { context.querySelector('.externalIds').addEventListener('click', function (e) { @@ -322,19 +284,16 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi } }); - context.querySelector('.btnCancel').addEventListener('click', function () { + if (!layoutManager.desktop) { + context.querySelector('.btnBack').classList.remove('hide'); + context.querySelector('.btnClose').classList.add('hide'); + } + bindAll(context.querySelectorAll('.btnCancel'), 'click', function (event) { + event.preventDefault(); closeDialog(false); }); - context.querySelector('.btnMore').addEventListener('click', function (e) { - - getApiClient().getCurrentUser().then(function (user) { - showMoreMenu(context, e.target, user); - }); - - }); - context.querySelector('.btnHeaderSave').addEventListener('click', function (e) { context.querySelector('.btnSave').click(); @@ -349,14 +308,14 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi } }); - context.removeEventListener('click', onEditorClick); - context.addEventListener('click', onEditorClick); + context.removeEventListener('submit', onEditorClick); + context.addEventListener('submit', onEditorClick); var form = context.querySelector('form'); form.removeEventListener('submit', onSubmit); form.addEventListener('submit', onSubmit); - context.querySelector("#btnAddPerson").addEventListener('click', function (event, data) { + context.querySelector('#btnAddPerson').addEventListener('click', function (event, data) { editPerson(context, {}, -1); }); @@ -403,7 +362,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi function populateCountries(select, allCountries) { - var html = ""; + var html = ''; html += ""; @@ -411,7 +370,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi var culture = allCountries[i]; - html += ""; + html += "'; } select.innerHTML = html; @@ -419,7 +378,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi function populateLanguages(select, languages) { - var html = ""; + var html = ''; html += ""; @@ -427,7 +386,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi var culture = languages[i]; - html += ""; + html += "'; } select.innerHTML = html; @@ -462,12 +421,12 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi var idInfo = externalIds[i]; - var id = "txt1" + idInfo.Key; + var id = 'txt1' + idInfo.Key; var formatString = idInfo.UrlFormatString || ''; var fullName = idInfo.Name; if (idInfo.Type) { - fullName = idInfo.Name + " " + globalize.translate(idInfo.Type); + fullName = idInfo.Name + ' ' + globalize.translate(idInfo.Type); } var labelText = globalize.translate('LabelDynamicExternalId', fullName); @@ -482,7 +441,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi html += '
'; if (formatString) { - html += ''; + html += ''; } html += ''; @@ -544,37 +503,37 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi hideElement('#fldPath', context); } - if (item.Type === "Series" || item.Type === "Movie" || item.Type === "Trailer") { + if (item.Type === 'Series' || item.Type === 'Movie' || item.Type === 'Trailer') { showElement('#fldOriginalName', context); } else { hideElement('#fldOriginalName', context); } - if (item.Type === "Series") { + if (item.Type === 'Series') { showElement('#fldSeriesRuntime', context); } else { hideElement('#fldSeriesRuntime', context); } - if (item.Type === "Series" || item.Type === "Person") { + if (item.Type === 'Series' || item.Type === 'Person') { showElement('#fldEndDate', context); } else { hideElement('#fldEndDate', context); } - if (item.Type === "MusicAlbum") { + if (item.Type === 'MusicAlbum') { showElement('#albumAssociationMessage', context); } else { hideElement('#albumAssociationMessage', context); } - if (item.Type === "Movie" || item.Type === "Trailer") { + if (item.Type === 'Movie' || item.Type === 'Trailer') { showElement('#fldCriticRating', context); } else { hideElement('#fldCriticRating', context); } - if (item.Type === "Series") { + if (item.Type === 'Series') { showElement('#fldStatus', context); showElement('#fldAirDays', context); showElement('#fldAirTime', context); @@ -584,19 +543,19 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi hideElement('#fldAirTime', context); } - if (item.MediaType === "Video" && item.Type !== "TvChannel") { + if (item.MediaType === 'Video' && item.Type !== 'TvChannel') { showElement('#fld3dFormat', context); } else { hideElement('#fld3dFormat', context); } - if (item.Type === "Audio") { + if (item.Type === 'Audio') { showElement('#fldAlbumArtist', context); } else { hideElement('#fldAlbumArtist', context); } - if (item.Type === "Audio" || item.Type === "MusicVideo") { + if (item.Type === 'Audio' || item.Type === 'MusicVideo') { showElement('#fldArtist', context); showElement('#fldAlbum', context); } else { @@ -604,29 +563,29 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi hideElement('#fldAlbum', context); } - if (item.Type === "Episode" && item.ParentIndexNumber === 0) { + if (item.Type === 'Episode' && item.ParentIndexNumber === 0) { showElement('#collapsibleSpecialEpisodeInfo', context); } else { hideElement('#collapsibleSpecialEpisodeInfo', context); } - if (item.Type === "Person" || - item.Type === "Genre" || - item.Type === "Studio" || - item.Type === "MusicGenre" || - item.Type === "TvChannel" || - item.Type === "Book") { + if (item.Type === 'Person' || + item.Type === 'Genre' || + item.Type === 'Studio' || + item.Type === 'MusicGenre' || + item.Type === 'TvChannel' || + item.Type === 'Book') { hideElement('#peopleCollapsible', context); } else { showElement('#peopleCollapsible', context); } - if (item.Type === "Person" || item.Type === "Genre" || item.Type === "Studio" || item.Type === "MusicGenre" || item.Type === "TvChannel") { + if (item.Type === 'Person' || item.Type === 'Genre' || item.Type === 'Studio' || item.Type === 'MusicGenre' || item.Type === 'TvChannel') { hideElement('#fldCommunityRating', context); hideElement('#genresCollapsible', context); hideElement('#studiosCollapsible', context); - if (item.Type === "TvChannel") { + if (item.Type === 'TvChannel') { showElement('#fldOfficialRating', context); } else { hideElement('#fldOfficialRating', context); @@ -642,7 +601,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi showElement('#tagsCollapsible', context); - if (item.Type === "TvChannel") { + if (item.Type === 'TvChannel') { hideElement('#metadataSettingsCollapsible', context); hideElement('#fldPremiereDate', context); hideElement('#fldDateAdded', context); @@ -654,39 +613,39 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi showElement('#fldYear', context); } - if (item.Type === "TvChannel") { + if (item.Type === 'TvChannel') { hideElement('.overviewContainer', context); } else { showElement('.overviewContainer', context); } - if (item.Type === "Person") { + if (item.Type === 'Person') { //todo context.querySelector('#txtProductionYear').label(globalize.translate('LabelBirthYear')); - context.querySelector("#txtPremiereDate").label(globalize.translate('LabelBirthDate')); - context.querySelector("#txtEndDate").label(globalize.translate('LabelDeathDate')); + context.querySelector('#txtPremiereDate').label(globalize.translate('LabelBirthDate')); + context.querySelector('#txtEndDate').label(globalize.translate('LabelDeathDate')); showElement('#fldPlaceOfBirth'); } else { context.querySelector('#txtProductionYear').label(globalize.translate('LabelYear')); - context.querySelector("#txtPremiereDate").label(globalize.translate('LabelReleaseDate')); - context.querySelector("#txtEndDate").label(globalize.translate('LabelEndDate')); + context.querySelector('#txtPremiereDate').label(globalize.translate('LabelReleaseDate')); + context.querySelector('#txtEndDate').label(globalize.translate('LabelEndDate')); hideElement('#fldPlaceOfBirth'); } - if (item.MediaType === "Video" && item.Type !== "TvChannel") { + if (item.MediaType === 'Video' && item.Type !== 'TvChannel') { showElement('#fldOriginalAspectRatio'); } else { hideElement('#fldOriginalAspectRatio'); } - if (item.Type === "Audio" || item.Type === "Episode" || item.Type === "Season") { + if (item.Type === 'Audio' || item.Type === 'Episode' || item.Type === 'Season') { showElement('#fldIndexNumber'); - if (item.Type === "Episode") { + if (item.Type === 'Episode') { context.querySelector('#txtIndexNumber').label(globalize.translate('LabelEpisodeNumber')); - } else if (item.Type === "Season") { + } else if (item.Type === 'Season') { context.querySelector('#txtIndexNumber').label(globalize.translate('LabelSeasonNumber')); - } else if (item.Type === "Audio") { + } else if (item.Type === 'Audio') { context.querySelector('#txtIndexNumber').label(globalize.translate('LabelTrackNumber')); } else { context.querySelector('#txtIndexNumber').label(globalize.translate('LabelNumber')); @@ -695,12 +654,12 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi hideElement('#fldIndexNumber'); } - if (item.Type === "Audio" || item.Type === "Episode") { + if (item.Type === 'Audio' || item.Type === 'Episode') { showElement('#fldParentIndexNumber'); - if (item.Type === "Episode") { + if (item.Type === 'Episode') { context.querySelector('#txtParentIndexNumber').label(globalize.translate('LabelSeasonNumber')); - } else if (item.Type === "Audio") { + } else if (item.Type === 'Audio') { context.querySelector('#txtParentIndexNumber').label(globalize.translate('LabelDiscNumber')); } else { context.querySelector('#txtParentIndexNumber').label(globalize.translate('LabelParentNumber')); @@ -709,12 +668,12 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi hideElement('#fldParentIndexNumber', context); } - if (item.Type === "BoxSet") { + if (item.Type === 'BoxSet') { showElement('#fldDisplayOrder', context); hideElement('.seriesDisplayOrderDescription', context); context.querySelector('#selectDisplayOrder').innerHTML = ''; - } else if (item.Type === "Series") { + } else if (item.Type === 'Series') { showElement('#fldDisplayOrder', context); showElement('.seriesDisplayOrderDescription', context); @@ -731,19 +690,19 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi populateRatings(parentalRatingOptions, select, item.OfficialRating); - select.value = item.OfficialRating || ""; + select.value = item.OfficialRating || ''; select = context.querySelector('#selectCustomRating'); populateRatings(parentalRatingOptions, select, item.CustomRating); - select.value = item.CustomRating || ""; + select.value = item.CustomRating || ''; var selectStatus = context.querySelector('#selectStatus'); populateStatus(selectStatus); - selectStatus.value = item.Status || ""; + selectStatus.value = item.Status || ''; - context.querySelector('#select3dFormat', context).value = item.Video3DFormat || ""; + context.querySelector('#select3dFormat', context).value = item.Video3DFormat || ''; Array.prototype.forEach.call(context.querySelectorAll('.chkAirDay', context), function (el) { el.checked = (item.AirDays || []).indexOf(el.getAttribute('data-day')) !== -1; @@ -759,7 +718,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi populateListView(context.querySelector('#listTags'), item.Tags); var lockData = (item.LockData || false); - var chkLockData = context.querySelector("#chkLockData"); + var chkLockData = context.querySelector('#chkLockData'); chkLockData.checked = lockData; if (chkLockData.checked) { hideElement('.providerSettingsContainer', context); @@ -769,33 +728,29 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi fillMetadataSettings(context, item, item.LockedFields); context.querySelector('#txtPath').value = item.Path || ''; - context.querySelector('#txtName').value = item.Name || ""; - context.querySelector('#txtOriginalName').value = item.OriginalTitle || ""; + context.querySelector('#txtName').value = item.Name || ''; + context.querySelector('#txtOriginalName').value = item.OriginalTitle || ''; context.querySelector('#txtOverview').value = item.Overview || ''; context.querySelector('#txtTagline').value = (item.Taglines && item.Taglines.length ? item.Taglines[0] : ''); - context.querySelector('#txtSortName').value = item.ForcedSortName || ""; - context.querySelector('#txtCommunityRating').value = item.CommunityRating || ""; + context.querySelector('#txtSortName').value = item.ForcedSortName || ''; + context.querySelector('#txtCommunityRating').value = item.CommunityRating || ''; - context.querySelector('#txtCriticRating').value = item.CriticRating || ""; + context.querySelector('#txtCriticRating').value = item.CriticRating || ''; context.querySelector('#txtIndexNumber').value = item.IndexNumber == null ? '' : item.IndexNumber; context.querySelector('#txtParentIndexNumber').value = item.ParentIndexNumber == null ? '' : item.ParentIndexNumber; - context.querySelector('#txtAirsBeforeSeason').value = ('AirsBeforeSeasonNumber' in item) ? item.AirsBeforeSeasonNumber : ""; - context.querySelector('#txtAirsAfterSeason').value = ('AirsAfterSeasonNumber' in item) ? item.AirsAfterSeasonNumber : ""; - context.querySelector('#txtAirsBeforeEpisode').value = ('AirsBeforeEpisodeNumber' in item) ? item.AirsBeforeEpisodeNumber : ""; + context.querySelector('#txtAirsBeforeSeason').value = ('AirsBeforeSeasonNumber' in item) ? item.AirsBeforeSeasonNumber : ''; + context.querySelector('#txtAirsAfterSeason').value = ('AirsAfterSeasonNumber' in item) ? item.AirsAfterSeasonNumber : ''; + context.querySelector('#txtAirsBeforeEpisode').value = ('AirsBeforeEpisodeNumber' in item) ? item.AirsBeforeEpisodeNumber : ''; - context.querySelector('#txtAlbum').value = item.Album || ""; + context.querySelector('#txtAlbum').value = item.Album || ''; context.querySelector('#txtAlbumArtist').value = (item.AlbumArtists || []).map(function (a) { return a.Name; }).join(';'); - if (item.Type === 'Series') { - context.querySelector('#selectDisplayOrder').value = item.DisplayOrder || ''; - } else { - context.querySelector('#selectDisplayOrder').value = item.DisplayOrder || ''; - } + context.querySelector('#selectDisplayOrder').value = item.DisplayOrder || ''; context.querySelector('#txtArtist').value = (item.ArtistItems || []).map(function (a) { return a.Name; @@ -839,17 +794,17 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi context.querySelector('#txtEndDate').value = ''; } - context.querySelector('#txtProductionYear').value = item.ProductionYear || ""; + context.querySelector('#txtProductionYear').value = item.ProductionYear || ''; context.querySelector('#txtAirTime').value = item.AirTime || ''; var placeofBirth = item.ProductionLocations && item.ProductionLocations.length ? item.ProductionLocations[0] : ''; context.querySelector('#txtPlaceOfBirth').value = placeofBirth; - context.querySelector('#txtOriginalAspectRatio').value = item.AspectRatio || ""; + context.querySelector('#txtOriginalAspectRatio').value = item.AspectRatio || ''; - context.querySelector('#selectLanguage').value = item.PreferredMetadataLanguage || ""; - context.querySelector('#selectCountry').value = item.PreferredMetadataCountryCode || ""; + context.querySelector('#selectLanguage').value = item.PreferredMetadataLanguage || ''; + context.querySelector('#selectCountry').value = item.PreferredMetadataCountryCode || ''; if (item.RunTimeTicks) { @@ -857,13 +812,13 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi context.querySelector('#txtSeriesRuntime').value = Math.round(minutes); } else { - context.querySelector('#txtSeriesRuntime', context).value = ""; + context.querySelector('#txtSeriesRuntime', context).value = ''; } } function populateRatings(allParentalRatings, select, currentValue) { - var html = ""; + var html = ''; html += ""; @@ -893,18 +848,18 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi rating = ratings[i]; - html += ""; + html += "'; } select.innerHTML = html; } function populateStatus(select) { - var html = ""; + var html = ''; html += ""; - html += ""; - html += ""; + html += "'; + html += "'; select.innerHTML = html; } @@ -922,7 +877,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi for (var i = 0; i < items.length; i++) { html += '
'; - html += ''; + html += ''; html += '
'; @@ -932,7 +887,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi html += '
'; - html += ''; + html += ''; html += '
'; } @@ -953,7 +908,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi html += '
'; - html += 'person'; + html += ''; html += '
'; html += ''; html += '
'; - html += ''; + html += ''; html += '
'; } @@ -999,30 +954,30 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi lockedFields = lockedFields || []; var lockedFieldsList = [ - { name: globalize.translate('Name'), value: "Name" }, - { name: globalize.translate('Overview'), value: "Overview" }, - { name: globalize.translate('Genres'), value: "Genres" }, - { name: globalize.translate('ParentalRating'), value: "OfficialRating" }, - { name: globalize.translate('People'), value: "Cast" } + { name: globalize.translate('Name'), value: 'Name' }, + { name: globalize.translate('Overview'), value: 'Overview' }, + { name: globalize.translate('Genres'), value: 'Genres' }, + { name: globalize.translate('ParentalRating'), value: 'OfficialRating' }, + { name: globalize.translate('People'), value: 'Cast' } ]; - if (item.Type === "Person") { - lockedFieldsList.push({ name: globalize.translate('BirthLocation'), value: "ProductionLocations" }); + if (item.Type === 'Person') { + lockedFieldsList.push({ name: globalize.translate('BirthLocation'), value: 'ProductionLocations' }); } else { - lockedFieldsList.push({ name: globalize.translate('ProductionLocations'), value: "ProductionLocations" }); + lockedFieldsList.push({ name: globalize.translate('ProductionLocations'), value: 'ProductionLocations' }); } - if (item.Type === "Series") { - lockedFieldsList.push({ name: globalize.translate('Runtime'), value: "Runtime" }); + if (item.Type === 'Series') { + lockedFieldsList.push({ name: globalize.translate('Runtime'), value: 'Runtime' }); } - lockedFieldsList.push({ name: globalize.translate('Studios'), value: "Studios" }); - lockedFieldsList.push({ name: globalize.translate('Tags'), value: "Tags" }); + lockedFieldsList.push({ name: globalize.translate('Studios'), value: 'Studios' }); + lockedFieldsList.push({ name: globalize.translate('Tags'), value: 'Tags' }); var html = ''; - html += "

" + globalize.translate('HeaderEnabledFields') + "

"; - html += "

" + globalize.translate('HeaderEnabledFieldsHelp') + "

"; + html += '

' + globalize.translate('HeaderEnabledFields') + '

'; + html += '

' + globalize.translate('HeaderEnabledFieldsHelp') + '

'; html += getLockedFieldsHtml(lockedFieldsList, lockedFields); container.innerHTML = html; } @@ -1051,7 +1006,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi setFieldVisibilities(context, item); fillItemInfo(context, item, metadataEditorInfo.ParentalRatingOptions); - if (item.MediaType === "Video" && item.Type !== "Episode" && item.Type !== "TvChannel") { + if (item.MediaType === 'Video' && item.Type !== 'Episode' && item.Type !== 'TvChannel') { showElement('#fldTagline', context); } else { hideElement('#fldTagline', context); @@ -1071,7 +1026,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi function show(itemId, serverId, resolve, reject) { loading.show(); - require(['text!./metadataeditor.template.html'], function (template) { + require(['text!./metadataEditor.template.html'], function (template) { var dialogOptions = { removeOnClose: true, @@ -1081,7 +1036,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi if (layoutManager.tv) { dialogOptions.size = 'fullscreen'; } else { - dialogOptions.size = 'medium-tall'; + dialogOptions.size = 'small'; } var dlg = dialogHelper.createDialog(dialogOptions); @@ -1128,7 +1083,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi loading.show(); - require(['text!./metadataeditor.template.html'], function (template) { + require(['text!./metadataEditor.template.html'], function (template) { elem.innerHTML = globalize.translateDocument(template, 'core'); diff --git a/src/components/metadataeditor/metadataeditor.template.html b/src/components/metadataEditor/metadataEditor.template.html similarity index 76% rename from src/components/metadataeditor/metadataeditor.template.html rename to src/components/metadataEditor/metadataEditor.template.html index 8e36a824b7..4b4e2cf965 100644 --- a/src/components/metadataeditor/metadataeditor.template.html +++ b/src/components/metadataEditor/metadataEditor.template.html @@ -1,15 +1,15 @@
- +

${Edit}

-
@@ -57,11 +57,13 @@
-
- -
-
- +
+
+ +
+
+ +
@@ -129,24 +131,28 @@
-
- +
+
+ +
+
+ +
-
- -
-
- -
-
- +
+
+ +
+
+ +
@@ -160,14 +166,16 @@

${HeaderSpecialEpisodeInfo}

-
- -
-
- -
-
- +
+
+ +
+
+ +
+
+ +
@@ -184,7 +192,7 @@ ${Genres}
@@ -193,7 +201,7 @@ ${People}
@@ -203,7 +211,7 @@ ${Studios}
@@ -212,7 +220,7 @@ ${Tags}
@@ -240,8 +248,11 @@

+
diff --git a/src/components/metadataeditor/personeditor.js b/src/components/metadataEditor/personEditor.js similarity index 96% rename from src/components/metadataeditor/personeditor.js rename to src/components/metadataEditor/personEditor.js index 9fb6fdec6f..fc6a0595f2 100644 --- a/src/components/metadataeditor/personeditor.js +++ b/src/components/metadataEditor/personEditor.js @@ -11,7 +11,7 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'require', 'paper-icon-but function show(person) { return new Promise(function (resolve, reject) { - require(['text!./personeditor.template.html'], function (template) { + require(['text!./personEditor.template.html'], function (template) { var dialogOptions = { removeOnClose: true, @@ -21,7 +21,7 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'require', 'paper-icon-but if (layoutManager.tv) { dialogOptions.size = 'fullscreen'; } else { - dialogOptions.size = 'medium-tall'; + dialogOptions.size = 'small'; } var dlg = dialogHelper.createDialog(dialogOptions); diff --git a/src/components/metadataeditor/personeditor.template.html b/src/components/metadataEditor/personEditor.template.html similarity index 95% rename from src/components/metadataeditor/personeditor.template.html rename to src/components/metadataEditor/personEditor.template.html index ec76f58c70..40b29767fa 100644 --- a/src/components/metadataeditor/personeditor.template.html +++ b/src/components/metadataEditor/personEditor.template.html @@ -1,5 +1,5 @@
- +

${Edit}

diff --git a/src/components/multiselect/multiselect.css b/src/components/multiSelect/multiSelect.css similarity index 100% rename from src/components/multiselect/multiselect.css rename to src/components/multiSelect/multiSelect.css diff --git a/src/components/multiselect/multiselect.js b/src/components/multiSelect/multiSelect.js similarity index 70% rename from src/components/multiselect/multiselect.js rename to src/components/multiSelect/multiSelect.js index b4fa87c8a0..83982413ae 100644 --- a/src/components/multiselect/multiselect.js +++ b/src/components/multiSelect/multiSelect.js @@ -1,5 +1,5 @@ -define(["browser", "appStorage", "apphost", "loading", "connectionManager", "globalize", "appRouter", "dom", "css!./multiselect"], function (browser, appStorage, appHost, loading, connectionManager, globalize, appRouter, dom) { - "use strict"; +define(['browser', 'appStorage', 'apphost', 'loading', 'connectionManager', 'globalize', 'appRouter', 'dom', 'css!./multiSelect'], function (browser, appStorage, appHost, loading, connectionManager, globalize, appRouter, dom) { + 'use strict'; var selectedItems = []; var selectedElements = []; @@ -15,12 +15,12 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo selectedItems = []; selectedElements = []; - var elems = document.querySelectorAll(".itemSelectionPanel"); + var elems = document.querySelectorAll('.itemSelectionPanel'); for (var i = 0, length = elems.length; i < length; i++) { var parent = elems[i].parentNode; parent.removeChild(elems[i]); - parent.classList.remove("withMultiSelect"); + parent.classList.remove('withMultiSelect'); } } } @@ -28,13 +28,13 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo function onItemSelectionPanelClick(e, itemSelectionPanel) { // toggle the checkbox, if it wasn't clicked on - if (!dom.parentWithClass(e.target, "chkItemSelect")) { - var chkItemSelect = itemSelectionPanel.querySelector(".chkItemSelect"); + if (!dom.parentWithClass(e.target, 'chkItemSelect')) { + var chkItemSelect = itemSelectionPanel.querySelector('.chkItemSelect'); if (chkItemSelect) { - if (chkItemSelect.classList.contains("checkedInitial")) { - chkItemSelect.classList.remove("checkedInitial"); + if (chkItemSelect.classList.contains('checkedInitial')) { + chkItemSelect.classList.remove('checkedInitial'); } else { var newValue = !chkItemSelect.checked; chkItemSelect.checked = newValue; @@ -50,7 +50,7 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo function updateItemSelection(chkItemSelect, selected) { - var id = dom.parentWithAttribute(chkItemSelect, "data-id").getAttribute("data-id"); + var id = dom.parentWithAttribute(chkItemSelect, 'data-id').getAttribute('data-id'); if (selected) { @@ -73,7 +73,7 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo } if (selectedItems.length) { - var itemSelectionCount = document.querySelector(".itemSelectionCount"); + var itemSelectionCount = document.querySelector('.itemSelectionCount'); if (itemSelectionCount) { itemSelectionCount.innerHTML = selectedItems.length; } @@ -88,27 +88,27 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo function showSelection(item, isChecked) { - var itemSelectionPanel = item.querySelector(".itemSelectionPanel"); + var itemSelectionPanel = item.querySelector('.itemSelectionPanel'); if (!itemSelectionPanel) { - itemSelectionPanel = document.createElement("div"); - itemSelectionPanel.classList.add("itemSelectionPanel"); + itemSelectionPanel = document.createElement('div'); + itemSelectionPanel.classList.add('itemSelectionPanel'); - var parent = item.querySelector(".cardBox") || item.querySelector(".cardContent"); - parent.classList.add("withMultiSelect"); + var parent = item.querySelector('.cardBox') || item.querySelector('.cardContent'); + parent.classList.add('withMultiSelect'); parent.appendChild(itemSelectionPanel); - var cssClass = "chkItemSelect"; + var cssClass = 'chkItemSelect'; if (isChecked && !browser.firefox) { // In firefox, the initial tap hold doesnt' get treated as a click // In other browsers it does, so we need to make sure that initial click is ignored - cssClass += " checkedInitial"; + cssClass += ' checkedInitial'; } - var checkedAttribute = isChecked ? " checked" : ""; + var checkedAttribute = isChecked ? ' checked' : ''; itemSelectionPanel.innerHTML = ''; - var chkItemSelect = itemSelectionPanel.querySelector(".chkItemSelect"); - chkItemSelect.addEventListener("change", onSelectionChange); + var chkItemSelect = itemSelectionPanel.querySelector('.chkItemSelect'); + chkItemSelect.addEventListener('change', onSelectionChange); } } @@ -118,27 +118,27 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo if (!selectionCommandsPanel) { - selectionCommandsPanel = document.createElement("div"); - selectionCommandsPanel.classList.add("selectionCommandsPanel"); + selectionCommandsPanel = document.createElement('div'); + selectionCommandsPanel.classList.add('selectionCommandsPanel'); document.body.appendChild(selectionCommandsPanel); currentSelectionCommandsPanel = selectionCommandsPanel; - var html = ""; + var html = ''; - html += ''; + html += ''; html += '

'; - var moreIcon = ""; - html += ''; + const moreIcon = 'more_vert'; + html += ''; selectionCommandsPanel.innerHTML = html; - selectionCommandsPanel.querySelector(".btnCloseSelectionPanel").addEventListener("click", hideSelections); + selectionCommandsPanel.querySelector('.btnCloseSelectionPanel').addEventListener('click', hideSelections); - var btnSelectionPanelOptions = selectionCommandsPanel.querySelector(".btnSelectionPanelOptions"); + var btnSelectionPanelOptions = selectionCommandsPanel.querySelector('.btnSelectionPanelOptions'); - dom.addEventListener(btnSelectionPanelOptions, "click", showMenuForSelectedItems, { passive: true }); + dom.addEventListener(btnSelectionPanelOptions, 'click', showMenuForSelectedItems, { passive: true }); } } @@ -146,7 +146,7 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo return new Promise(function (resolve, reject) { - require(["alert"], function (alert) { + require(['alert'], function (alert) { alert(options).then(resolve, resolve); }); }); @@ -156,15 +156,15 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo return new Promise(function (resolve, reject) { - var msg = globalize.translate("ConfirmDeleteItem"); - var title = globalize.translate("HeaderDeleteItem"); + var msg = globalize.translate('ConfirmDeleteItem'); + var title = globalize.translate('HeaderDeleteItem'); if (itemIds.length > 1) { - msg = globalize.translate("ConfirmDeleteItems"); - title = globalize.translate("HeaderDeleteItems"); + msg = globalize.translate('ConfirmDeleteItems'); + title = globalize.translate('HeaderDeleteItems'); } - require(["confirm"], function (confirm) { + require(['confirm'], function (confirm) { confirm(msg, title).then(function () { var promises = itemIds.map(function (itemId) { @@ -173,7 +173,7 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo Promise.all(promises).then(resolve, function () { - alertText(globalize.translate("ErrorDeletingItem")).then(reject, reject); + alertText(globalize.translate('ErrorDeletingItem')).then(reject, reject); }); }, reject); @@ -190,58 +190,58 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo var menuItems = []; menuItems.push({ - name: globalize.translate("AddToCollection"), - id: "addtocollection", - icon: "add" + name: globalize.translate('AddToCollection'), + id: 'addtocollection', + icon: 'add' }); menuItems.push({ - name: globalize.translate("AddToPlaylist"), - id: "playlist", - icon: "playlist_add" + name: globalize.translate('AddToPlaylist'), + id: 'playlist', + icon: 'playlist_add' }); // TODO: Be more dynamic based on what is selected if (user.Policy.EnableContentDeletion) { menuItems.push({ - name: globalize.translate("Delete"), - id: "delete", - icon: "delete" + name: globalize.translate('Delete'), + id: 'delete', + icon: 'delete' }); } - if (user.Policy.EnableContentDownloading && appHost.supports("filedownload")) { + if (user.Policy.EnableContentDownloading && appHost.supports('filedownload')) { menuItems.push({ - name: Globalize.translate("ButtonDownload"), - id: "download", - icon: "file_download" + name: globalize.translate('ButtonDownload'), + id: 'download', + icon: 'file_download' }); } if (user.Policy.IsAdministrator) { menuItems.push({ - name: globalize.translate("GroupVersions"), - id: "groupvideos", - icon: "call_merge" + name: globalize.translate('GroupVersions'), + id: 'groupvideos', + icon: 'call_merge' }); } menuItems.push({ - name: globalize.translate("MarkPlayed"), - id: "markplayed", - icon: "check_box" + name: globalize.translate('MarkPlayed'), + id: 'markplayed', + icon: 'check_box' }); menuItems.push({ - name: globalize.translate("MarkUnplayed"), - id: "markunplayed", - icon: "check_box_outline_blank" + name: globalize.translate('MarkUnplayed'), + id: 'markunplayed', + icon: 'check_box_outline_blank' }); menuItems.push({ - name: globalize.translate("RefreshMetadata"), - id: "refresh", - icon: "refresh" + name: globalize.translate('RefreshMetadata'), + id: 'refresh', + icon: 'refresh' }); require(['actionsheet'], function (actionsheet) { @@ -253,8 +253,8 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo var serverId = apiClient.serverInfo().Id; switch (id) { - case "addtocollection": - require(["collectionEditor"], function (collectionEditor) { + case 'addtocollection': + require(['collectionEditor'], function (collectionEditor) { new collectionEditor().show({ items: items, serverId: serverId @@ -263,8 +263,8 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo hideSelections(); dispatchNeedsRefresh(); break; - case "playlist": - require(["playlistEditor"], function (playlistEditor) { + case 'playlist': + require(['playlistEditor'], function (playlistEditor) { new playlistEditor().show({ items: items, serverId: serverId @@ -273,30 +273,30 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo hideSelections(); dispatchNeedsRefresh(); break; - case "delete": + case 'delete': deleteItems(apiClient, items).then(dispatchNeedsRefresh); hideSelections(); dispatchNeedsRefresh(); break; - case "groupvideos": + case 'groupvideos': combineVersions(apiClient, items); break; - case "markplayed": + case 'markplayed': items.forEach(function (itemId) { apiClient.markPlayed(apiClient.getCurrentUserId(), itemId); }); hideSelections(); dispatchNeedsRefresh(); break; - case "markunplayed": + case 'markunplayed': items.forEach(function (itemId) { apiClient.markUnplayed(apiClient.getCurrentUserId(), itemId); }); hideSelections(); dispatchNeedsRefresh(); break; - case "refresh": - require(["refreshDialog"], function (refreshDialog) { + case 'refresh': + require(['refreshDialog'], function (refreshDialog) { new refreshDialog({ itemIds: items, serverId: serverId @@ -321,7 +321,7 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo [].forEach.call(selectedElements, function (i) { - var container = dom.parentWithAttribute(i, "is", "emby-itemscontainer"); + var container = dom.parentWithAttribute(i, 'is', 'emby-itemscontainer'); if (container && elems.indexOf(container) === -1) { elems.push(container); @@ -337,9 +337,9 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo if (selection.length < 2) { - require(["alert"], function (alert) { + require(['alert'], function (alert) { alert({ - text: globalize.translate("PleaseSelectTwoItems") + text: globalize.translate('PleaseSelectTwoItems') }); }); return; @@ -349,8 +349,8 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo apiClient.ajax({ - type: "POST", - url: apiClient.getUrl("Videos/MergeVersions", { Ids: selection.join(",") }) + type: 'POST', + url: apiClient.getUrl('Videos/MergeVersions', { Ids: selection.join(',') }) }).then(function () { @@ -362,8 +362,8 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo function showSelections(initialCard) { - require(["emby-checkbox"], function () { - var cards = document.querySelectorAll(".card"); + require(['emby-checkbox'], function () { + var cards = document.querySelectorAll('.card'); for (var i = 0, length = cards.length; i < length; i++) { showSelection(cards[i], initialCard === cards[i]); } @@ -379,9 +379,9 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo if (selectedItems.length) { - var card = dom.parentWithClass(target, "card"); + var card = dom.parentWithClass(target, 'card'); if (card) { - var itemSelectionPanel = card.querySelector(".itemSelectionPanel"); + var itemSelectionPanel = card.querySelector('.itemSelectionPanel'); if (itemSelectionPanel) { return onItemSelectionPanelClick(e, itemSelectionPanel); } @@ -393,7 +393,7 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo } } - document.addEventListener("viewbeforehide", hideSelections); + document.addEventListener('viewbeforehide', hideSelections); return function (options) { @@ -403,7 +403,7 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo function onTapHold(e) { - var card = dom.parentWithClass(e.target, "card"); + var card = dom.parentWithClass(e.target, 'card'); if (card) { @@ -440,7 +440,7 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo var element = touch.target; if (element) { - var card = dom.parentWithClass(element, "card"); + var card = dom.parentWithClass(element, 'card'); if (card) { @@ -509,7 +509,7 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo return; } - var card = dom.parentWithClass(touchTarget, "card"); + var card = dom.parentWithClass(touchTarget, 'card'); touchTarget = null; if (card) { @@ -522,27 +522,27 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo // mobile safari doesn't allow contextmenu override if (browser.touch && !browser.safari) { - element.addEventListener("contextmenu", onTapHold); + element.addEventListener('contextmenu', onTapHold); } else { - dom.addEventListener(element, "touchstart", onTouchStart, { + dom.addEventListener(element, 'touchstart', onTouchStart, { passive: true }); - dom.addEventListener(element, "touchmove", onTouchMove, { + dom.addEventListener(element, 'touchmove', onTouchMove, { passive: true }); - dom.addEventListener(element, "touchend", onTouchEnd, { + dom.addEventListener(element, 'touchend', onTouchEnd, { passive: true }); - dom.addEventListener(element, "touchcancel", onTouchEnd, { + dom.addEventListener(element, 'touchcancel', onTouchEnd, { passive: true }); - dom.addEventListener(element, "mousedown", onMouseDown, { + dom.addEventListener(element, 'mousedown', onMouseDown, { passive: true }); - dom.addEventListener(element, "mouseleave", onMouseOut, { + dom.addEventListener(element, 'mouseleave', onMouseOut, { passive: true }); - dom.addEventListener(element, "mouseup", onMouseOut, { + dom.addEventListener(element, 'mouseup', onMouseOut, { passive: true }); } @@ -551,38 +551,38 @@ define(["browser", "appStorage", "apphost", "loading", "connectionManager", "glo initTapHold(container); if (options.bindOnClick !== false) { - container.addEventListener("click", onContainerClick); + container.addEventListener('click', onContainerClick); } self.onContainerClick = onContainerClick; self.destroy = function () { - container.removeEventListener("click", onContainerClick); - container.removeEventListener("contextmenu", onTapHold); + container.removeEventListener('click', onContainerClick); + container.removeEventListener('contextmenu', onTapHold); var element = container; - dom.removeEventListener(element, "touchstart", onTouchStart, { + dom.removeEventListener(element, 'touchstart', onTouchStart, { passive: true }); - dom.removeEventListener(element, "touchmove", onTouchMove, { + dom.removeEventListener(element, 'touchmove', onTouchMove, { passive: true }); - dom.removeEventListener(element, "touchend", onTouchEnd, { + dom.removeEventListener(element, 'touchend', onTouchEnd, { passive: true }); // this fires in safari due to magnifying class //dom.removeEventListener(element, "touchcancel", onTouchEnd, { // passive: true //}); - dom.removeEventListener(element, "mousedown", onMouseDown, { + dom.removeEventListener(element, 'mousedown', onMouseDown, { passive: true }); - dom.removeEventListener(element, "mouseleave", onMouseOut, { + dom.removeEventListener(element, 'mouseleave', onMouseOut, { passive: true }); - dom.removeEventListener(element, "mouseup", onMouseOut, { + dom.removeEventListener(element, 'mouseup', onMouseOut, { passive: true }); }; diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index c8a79a3627..c8480e4f15 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -5,7 +5,9 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir document.removeEventListener('click', onOneDocumentClick); document.removeEventListener('keydown', onOneDocumentClick); - if (window.Notification) { + // don't request notification permissions if they're already granted or denied + if (window.Notification && window.Notification.permission === 'default') { + /* eslint-disable-next-line compat/compat */ Notification.requestPermission(); } } @@ -26,6 +28,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir } function resetRegistration() { + /* eslint-disable-next-line compat/compat */ var serviceWorker = navigator.serviceWorker; if (serviceWorker) { serviceWorker.ready.then(function (registration) { @@ -43,7 +46,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir function showNonPersistentNotification(title, options, timeoutMs) { try { - var notif = new Notification(title, options); + var notif = new Notification(title, options); /* eslint-disable-line compat/compat */ if (notif.show) { notif.show(); @@ -94,10 +97,10 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir } var notification = { - title: "New " + item.Type, + title: 'New ' + item.Type, body: body, vibrate: true, - tag: "newItem" + item.Id, + tag: 'newItem' + item.Id, data: { //options: { // url: LibraryBrowser.getHref(item) @@ -112,7 +115,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir notification.icon = apiClient.getScaledImageUrl(item.Id, { width: 80, tag: imageTags.Primary, - type: "Primary" + type: 'Primary' }); } @@ -136,11 +139,11 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir Recursive: true, Limit: 3, - Filters: "IsNotFolder", - SortBy: "DateCreated", - SortOrder: "Descending", + Filters: 'IsNotFolder', + SortBy: 'DateCreated', + SortOrder: 'Descending', Ids: newItems.join(','), - MediaTypes: "Audio,Video", + MediaTypes: 'Audio,Video', EnableTotalRecordCount: false }).then(function (result) { @@ -168,7 +171,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir } var notification = { - tag: "install" + installation.Id, + tag: 'install' + installation.Id, data: {} }; @@ -213,25 +216,25 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir }); events.on(serverNotifications, 'PackageInstallationCompleted', function (e, apiClient, data) { - showPackageInstallNotification(apiClient, data, "completed"); + showPackageInstallNotification(apiClient, data, 'completed'); }); events.on(serverNotifications, 'PackageInstallationFailed', function (e, apiClient, data) { - showPackageInstallNotification(apiClient, data, "failed"); + showPackageInstallNotification(apiClient, data, 'failed'); }); events.on(serverNotifications, 'PackageInstallationCancelled', function (e, apiClient, data) { - showPackageInstallNotification(apiClient, data, "cancelled"); + showPackageInstallNotification(apiClient, data, 'cancelled'); }); events.on(serverNotifications, 'PackageInstalling', function (e, apiClient, data) { - showPackageInstallNotification(apiClient, data, "progress"); + showPackageInstallNotification(apiClient, data, 'progress'); }); events.on(serverNotifications, 'ServerShuttingDown', function (e, apiClient, data) { var serverId = apiClient.serverInfo().Id; var notification = { - tag: "restart" + serverId, + tag: 'restart' + serverId, title: globalize.translate('ServerNameIsShuttingDown', apiClient.serverInfo().Name) }; showNotification(notification, 0, apiClient); @@ -240,7 +243,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir events.on(serverNotifications, 'ServerRestarting', function (e, apiClient, data) { var serverId = apiClient.serverInfo().Id; var notification = { - tag: "restart" + serverId, + tag: 'restart' + serverId, title: globalize.translate('ServerNameIsRestarting', apiClient.serverInfo().Name) }; showNotification(notification, 0, apiClient); @@ -250,7 +253,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir var serverId = apiClient.serverInfo().Id; var notification = { - tag: "restart" + serverId, + tag: 'restart' + serverId, title: globalize.translate('PleaseRestartServerName', apiClient.serverInfo().Name) }; diff --git a/src/components/nowplayingbar/nowplayingbar.css b/src/components/nowPlayingBar/nowPlayingBar.css similarity index 100% rename from src/components/nowplayingbar/nowplayingbar.css rename to src/components/nowPlayingBar/nowPlayingBar.css diff --git a/src/components/nowplayingbar/nowplayingbar.js b/src/components/nowPlayingBar/nowPlayingBar.js similarity index 85% rename from src/components/nowplayingbar/nowplayingbar.js rename to src/components/nowPlayingBar/nowPlayingBar.js index 8da9b9c053..bc9c3c1a88 100644 --- a/src/components/nowplayingbar/nowplayingbar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -1,4 +1,4 @@ -define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', 'layoutManager', 'playbackManager', 'nowPlayingHelper', 'apphost', 'dom', 'connectionManager', 'paper-icon-button-light', 'emby-ratingbutton'], function (require, datetime, itemHelper, events, browser, imageLoader, layoutManager, playbackManager, nowPlayingHelper, appHost, dom, connectionManager) { +define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', 'layoutManager', 'playbackManager', 'nowPlayingHelper', 'apphost', 'dom', 'connectionManager', 'itemContextMenu', 'paper-icon-button-light', 'emby-ratingbutton'], function (require, datetime, itemHelper, events, browser, imageLoader, layoutManager, playbackManager, nowPlayingHelper, appHost, dom, connectionManager, itemContextMenu) { 'use strict'; var currentPlayer; @@ -42,31 +42,31 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', // The onclicks are needed due to the return false above html += '
'; - html += ''; + html += ''; - html += ''; + html += ''; - html += ''; - html += ''; + html += ''; + html += ''; html += '
'; html += '
'; html += '
'; - html += ''; + html += ''; html += '
'; html += ''; html += '
'; - html += ''; + html += ''; html += '
'; html += '
'; - html += ''; - html += ''; + html += ''; + html += ''; html += '
'; html += '
'; @@ -134,12 +134,10 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } }); - var i; - var length; playPauseButtons = elem.querySelectorAll('.playPauseButton'); - for (i = 0, length = playPauseButtons.length; i < length; i++) { - playPauseButtons[i].addEventListener('click', onPlayPauseClick); - } + playPauseButtons.forEach((button) => { + button.addEventListener('click', onPlayPauseClick); + }); elem.querySelector('.nextTrackButton').addEventListener('click', function () { @@ -155,8 +153,6 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } }); - elem.querySelector('.remoteControlButton').addEventListener('click', showRemoteControl); - toggleRepeatButton = elem.querySelector('.toggleRepeatButton'); toggleRepeatButton.addEventListener('click', function () { @@ -176,7 +172,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } }); - toggleRepeatButtonIcon = toggleRepeatButton.querySelector('i'); + toggleRepeatButtonIcon = toggleRepeatButton.querySelector('.material-icons'); volumeSlider = elem.querySelector('.nowPlayingBarVolumeSlider'); volumeSliderContainer = elem.querySelector('.nowPlayingBarVolumeSliderContainer'); @@ -187,29 +183,15 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', volumeSliderContainer.classList.remove('hide'); } - var volumeSliderTimer; - function setVolume() { - clearTimeout(volumeSliderTimer); - volumeSliderTimer = null; - if (currentPlayer) { currentPlayer.setVolume(this.value); } } - function setVolumeDelayed() { - if (!volumeSliderTimer) { - var that = this; - volumeSliderTimer = setTimeout(function () { - setVolume.call(that); - }, 700); - } - } - volumeSlider.addEventListener('change', setVolume); - volumeSlider.addEventListener('mousemove', setVolumeDelayed); - volumeSlider.addEventListener('touchmove', setVolumeDelayed); + volumeSlider.addEventListener('mousemove', setVolume); + volumeSlider.addEventListener('touchmove', setVolume); positionSlider = elem.querySelector('.nowPlayingBarPositionSlider'); positionSlider.addEventListener('change', function () { @@ -240,8 +222,8 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', elem.addEventListener('click', function (e) { - if (!dom.parentWithTag(e.target, ['BUTTON', 'INPUT', 'A'])) { - showRemoteControl(0); + if (!dom.parentWithTag(e.target, ['BUTTON', 'INPUT'])) { + showRemoteControl(); } }); } @@ -262,7 +244,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', return new Promise(function (resolve, reject) { - require(['appFooter-shared', 'itemShortcuts', 'css!./nowplayingbar.css', 'emby-slider'], function (appfooter, itemShortcuts) { + require(['appFooter-shared', 'itemShortcuts', 'css!./nowPlayingBar.css', 'emby-slider'], function (appfooter, itemShortcuts) { var parentContainer = appfooter.element; nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); @@ -297,22 +279,12 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } function updatePlayPauseState(isPaused) { - var i; - var length; - if (playPauseButtons) { - if (isPaused) { - - for (i = 0, length = playPauseButtons.length; i < length; i++) { - playPauseButtons[i].querySelector('i').innerHTML = ''; - } - - } else { - - for (i = 0, length = playPauseButtons.length; i < length; i++) { - playPauseButtons[i].querySelector('i').innerHTML = 'pause'; - } - } + playPauseButtons.forEach((button) => { + const icon = button.querySelector('.material-icons'); + icon.classList.remove('play_arrow', 'pause'); + icon.classList.add(isPaused ? 'play_arrow' : 'pause'); + }); } } @@ -356,15 +328,16 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } function updateRepeatModeDisplay(repeatMode) { + toggleRepeatButtonIcon.classList.remove('repeat', 'repeat_one'); if (repeatMode === 'RepeatAll') { - toggleRepeatButtonIcon.innerHTML = "repeat"; + toggleRepeatButtonIcon.classList.add('repeat'); toggleRepeatButton.classList.add('repeatButton-active'); } else if (repeatMode === 'RepeatOne') { - toggleRepeatButtonIcon.innerHTML = "repeat_one"; + toggleRepeatButtonIcon.classList.add('repeat_one'); toggleRepeatButton.classList.add('repeatButton-active'); } else { - toggleRepeatButtonIcon.innerHTML = "repeat"; + toggleRepeatButtonIcon.classList.add('repeat'); toggleRepeatButton.classList.remove('repeatButton-active'); } } @@ -395,7 +368,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', var timeText = positionTicks == null ? '--:--' : datetime.getDisplayRunningTime(positionTicks); if (runtimeTicks) { - timeText += " / " + datetime.getDisplayRunningTime(runtimeTicks); + timeText += ' / ' + datetime.getDisplayRunningTime(runtimeTicks); } currentTimeElement.innerHTML = timeText; @@ -413,11 +386,9 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', showMuteButton = false; } - if (isMuted) { - muteButton.querySelector('i').innerHTML = ''; - } else { - muteButton.querySelector('i').innerHTML = ''; - } + const muteButtonIcon = muteButton.querySelector('.material-icons'); + muteButtonIcon.classList.remove('volume_off', 'volume_up'); + muteButtonIcon.classList.add(isMuted ? 'volume_off' : 'volume_up'); if (supportedCommands.indexOf('SetVolume') === -1) { showVolumeSlider = false; @@ -449,17 +420,13 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } } - function getTextActionButton(item, text, serverId) { + function getTextActionButton(item, text) { if (!text) { text = itemHelper.getDisplayName(item); } - var html = ''; - - return html; + return `${text}`; } function seriesImageUrl(item, options) { @@ -473,7 +440,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } options = options || {}; - options.type = options.type || "Primary"; + options.type = options.type || 'Primary'; if (options.type === 'Primary') { @@ -511,7 +478,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', } options = options || {}; - options.type = options.type || "Primary"; + options.type = options.type || 'Primary'; if (item.ImageTags && item.ImageTags[options.type]) { @@ -537,16 +504,16 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', if (textLines.length > 1) { textLines[1].secondary = true; } - var serverId = nowPlayingItem ? nowPlayingItem.ServerId : null; nowPlayingTextElement.innerHTML = textLines.map(function (nowPlayingName) { var cssClass = nowPlayingName.secondary ? ' class="nowPlayingBarSecondaryText"' : ''; if (nowPlayingName.item) { - return '' + getTextActionButton(nowPlayingName.item, nowPlayingName.text, serverId) + '
'; + var nowPlayingText = getTextActionButton(nowPlayingName.item, nowPlayingName.text); + return `
${nowPlayingText}
`; } - return '' + nowPlayingName.text + ''; + return `
${nowPlayingText}
`; }).join(''); @@ -575,15 +542,25 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader', if (isRefreshing) { var apiClient = connectionManager.getApiClient(nowPlayingItem.ServerId); - apiClient.getItem(apiClient.getCurrentUserId(), nowPlayingItem.Id).then(function (item) { - var userData = item.UserData || {}; var likes = userData.Likes == null ? '' : userData.Likes; - - nowPlayingUserData.innerHTML = ''; + var contextButton = document.querySelector('.btnToggleContextMenu'); + var options = { + play: false, + queue: false, + positionTo: contextButton + }; + nowPlayingUserData.innerHTML = ''; + apiClient.getCurrentUser().then(function(user) { + contextButton.addEventListener('click', function () { + itemContextMenu.show(Object.assign({ + item: item, + user: user + }, options )); + }); + }); }); - } } else { nowPlayingUserData.innerHTML = ''; diff --git a/src/components/photoplayer/plugin.js b/src/components/photoplayer/plugin.js deleted file mode 100644 index 4ebdbbd6d2..0000000000 --- a/src/components/photoplayer/plugin.js +++ /dev/null @@ -1,46 +0,0 @@ -define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackManager', 'appRouter', 'appSettings', 'connectionManager'], function (browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager) { - "use strict"; - - function PhotoPlayer() { - - var self = this; - - self.name = 'Photo Player'; - self.type = 'mediaplayer'; - self.id = 'photoplayer'; - - // Let any players created by plugins take priority - self.priority = 1; - } - - PhotoPlayer.prototype.play = function (options) { - - return new Promise(function (resolve, reject) { - - require(['slideshow'], function (slideshow) { - - var index = options.startIndex || 0; - - var newSlideShow = new slideshow({ - showTitle: false, - cover: false, - items: options.items, - startIndex: index, - interval: 11000, - interactive: true - }); - - newSlideShow.show(); - - resolve(); - }); - }); - }; - - PhotoPlayer.prototype.canPlayMediaType = function (mediaType) { - - return (mediaType || '').toLowerCase() === 'photo'; - }; - - return PhotoPlayer; -}); diff --git a/src/components/playback/brightnessosd.js b/src/components/playback/brightnessosd.js index e73fc39689..5f3becd64b 100644 --- a/src/components/playback/brightnessosd.js +++ b/src/components/playback/brightnessosd.js @@ -1,171 +1,171 @@ -define(['events', 'playbackManager', 'dom', 'browser', 'css!./iconosd', 'material-icons'], function (events, playbackManager, dom, browser) { - 'use strict'; +import events from 'events'; +import playbackManager from 'playbackManager'; +import dom from 'dom'; +import browser from 'browser'; +import 'css!./iconosd'; +import 'material-icons'; - var currentPlayer; - var osdElement; - var iconElement; - var progressElement; +var currentPlayer; +var osdElement; +var iconElement; +var progressElement; - var enableAnimation; +var enableAnimation; - function getOsdElementHtml() { - var html = ''; +function getOsdElementHtml() { + var html = ''; - html += ''; + html += ''; - html += '
'; + html += '
'; - return html; + return html; +} + +function ensureOsdElement() { + + var elem = osdElement; + if (!elem) { + + enableAnimation = browser.supportsCssAnimation(); + + elem = document.createElement('div'); + elem.classList.add('hide'); + elem.classList.add('iconOsd'); + elem.classList.add('iconOsd-hidden'); + elem.classList.add('brightnessOsd'); + elem.innerHTML = getOsdElementHtml(); + + iconElement = elem.querySelector('.material-icons'); + progressElement = elem.querySelector('.iconOsdProgressInner'); + + document.body.appendChild(elem); + osdElement = elem; } +} - function ensureOsdElement() { +function onHideComplete() { + this.classList.add('hide'); +} - var elem = osdElement; - if (!elem) { +var hideTimeout; +function showOsd() { - enableAnimation = browser.supportsCssAnimation(); + clearHideTimeout(); - elem = document.createElement('div'); - elem.classList.add('hide'); - elem.classList.add('iconOsd'); - elem.classList.add('iconOsd-hidden'); - elem.classList.add('brightnessOsd'); - elem.innerHTML = getOsdElementHtml(); + var elem = osdElement; - iconElement = elem.querySelector('i'); - progressElement = elem.querySelector('.iconOsdProgressInner'); - - document.body.appendChild(elem); - osdElement = elem; - } - } - - function onHideComplete() { - this.classList.add('hide'); - } - - var hideTimeout; - function showOsd() { - - clearHideTimeout(); - - var elem = osdElement; - - dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: true - }); - - elem.classList.remove('hide'); - - // trigger reflow - void elem.offsetWidth; - - requestAnimationFrame(function () { - elem.classList.remove('iconOsd-hidden'); - - hideTimeout = setTimeout(hideOsd, 3000); - }); - } - - function clearHideTimeout() { - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - } - - function hideOsd() { - - clearHideTimeout(); - - var elem = osdElement; - if (elem) { - - if (enableAnimation) { - // trigger reflow - void elem.offsetWidth; - - requestAnimationFrame(function () { - elem.classList.add('iconOsd-hidden'); - - dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: true - }); - }); - } else { - onHideComplete.call(elem); - } - } - } - - function setIcon(iconElement, icon) { - iconElement.classList.remove('brightness_high'); - iconElement.classList.remove('brightness_medium'); - iconElement.classList.remove('brightness_low'); - iconElement.classList.add(icon); - } - - function updateElementsFromPlayer(brightness) { - - if (iconElement) { - if (brightness >= 80) { - setIcon(iconElement, 'brightness_high'); - } else if (brightness >= 20) { - setIcon(iconElement, 'brightness_medium'); - } else { - setIcon(iconElement, 'brightness_low'); - } - } - if (progressElement) { - progressElement.style.width = (brightness || 0) + '%'; - } - } - - function releaseCurrentPlayer() { - - var player = currentPlayer; - - if (player) { - events.off(player, 'brightnesschange', onBrightnessChanged); - events.off(player, 'playbackstop', hideOsd); - currentPlayer = null; - } - } - - function onBrightnessChanged(e) { - - var player = this; - - ensureOsdElement(); - - updateElementsFromPlayer(playbackManager.getBrightness(player)); - - showOsd(); - } - - function bindToPlayer(player) { - - if (player === currentPlayer) { - return; - } - - releaseCurrentPlayer(); - - currentPlayer = player; - - if (!player) { - return; - } - - hideOsd(); - events.on(player, 'brightnesschange', onBrightnessChanged); - events.on(player, 'playbackstop', hideOsd); - } - - events.on(playbackManager, 'playerchange', function () { - bindToPlayer(playbackManager.getCurrentPlayer()); + dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true }); - bindToPlayer(playbackManager.getCurrentPlayer()); + elem.classList.remove('hide'); + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.remove('iconOsd-hidden'); + + hideTimeout = setTimeout(hideOsd, 3000); + }); +} + +function clearHideTimeout() { + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; + } +} + +function hideOsd() { + + clearHideTimeout(); + + var elem = osdElement; + if (elem) { + + if (enableAnimation) { + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.add('iconOsd-hidden'); + + dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true + }); + }); + } else { + onHideComplete.call(elem); + } + } +} + +function setIcon(iconElement, icon) { + iconElement.classList.remove('brightness_high', 'brightness_medium', 'brightness_low'); + iconElement.classList.add(icon); +} + +function updateElementsFromPlayer(brightness) { + + if (iconElement) { + if (brightness >= 80) { + setIcon(iconElement, 'brightness_high'); + } else if (brightness >= 20) { + setIcon(iconElement, 'brightness_medium'); + } else { + setIcon(iconElement, 'brightness_low'); + } + } + if (progressElement) { + progressElement.style.width = (brightness || 0) + '%'; + } +} + +function releaseCurrentPlayer() { + + var player = currentPlayer; + + if (player) { + events.off(player, 'brightnesschange', onBrightnessChanged); + events.off(player, 'playbackstop', hideOsd); + currentPlayer = null; + } +} + +function onBrightnessChanged(e) { + + var player = this; + + ensureOsdElement(); + + updateElementsFromPlayer(playbackManager.getBrightness(player)); + + showOsd(); +} + +function bindToPlayer(player) { + + if (player === currentPlayer) { + return; + } + + releaseCurrentPlayer(); + + currentPlayer = player; + + if (!player) { + return; + } + + hideOsd(); + events.on(player, 'brightnesschange', onBrightnessChanged); + events.on(player, 'playbackstop', hideOsd); +} + +events.on(playbackManager, 'playerchange', function () { + bindToPlayer(playbackManager.getCurrentPlayer()); }); + +bindToPlayer(playbackManager.getCurrentPlayer()); diff --git a/src/components/playback/mediasession.js b/src/components/playback/mediasession.js index c03420c85a..5eac56b5ce 100644 --- a/src/components/playback/mediasession.js +++ b/src/components/playback/mediasession.js @@ -1,45 +1,28 @@ -define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], function (playbackManager, nowPlayingHelper, events, connectionManager) { - "use strict"; - - // no support for mediaSession - if (!navigator.mediaSession && !window.NativeShell) { - return; - } +import playbackManager from 'playbackManager'; +import nowPlayingHelper from 'nowPlayingHelper'; +import events from 'events'; +import connectionManager from 'connectionManager'; +/* eslint-disable indent */ // Reports media playback to the device for lock screen control - var currentPlayer; - var lastUpdateTime = 0; + let currentPlayer; - function seriesImageUrl(item, options) { + function seriesImageUrl(item, options = {}) { + options.type = options.type || 'Primary'; if (item.Type !== 'Episode') { return null; - } - - options = options || {}; - options.type = options.type || "Primary"; - - if (options.type === 'Primary') { - - if (item.SeriesPrimaryImageTag) { - - options.tag = item.SeriesPrimaryImageTag; - - return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); - } - } - - if (options.type === 'Thumb') { + } else if (options.type === 'Primary' && item.SeriesPrimaryImageTag) { + options.tag = item.SeriesPrimaryImageTag; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + } else if (options.type === 'Thumb') { if (item.SeriesThumbImageTag) { - options.tag = item.SeriesThumbImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); - } - if (item.ParentThumbImageTag) { - + } else if (item.ParentThumbImageTag) { options.tag = item.ParentThumbImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); @@ -49,122 +32,102 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f return null; } - function imageUrl(item, options) { - - options = options || {}; - options.type = options.type || "Primary"; + function imageUrl(item, options = {}) { + options.type = options.type || 'Primary'; if (item.ImageTags && item.ImageTags[options.type]) { - options.tag = item.ImageTags[options.type]; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.Id, options); - } - - if (item.AlbumId && item.AlbumPrimaryImageTag) { - + } else if (item.AlbumId && item.AlbumPrimaryImageTag) { options.tag = item.AlbumPrimaryImageTag; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); } return null; } - function pushImageUrl(item, imageOptions, list) { - var url = seriesImageUrl(item, imageOptions) || imageUrl(item, imageOptions); + function getImageUrl(item, imageOptions = {}) { + const url = seriesImageUrl(item, imageOptions) || imageUrl(item, imageOptions); if (url) { - var height = imageOptions.height || imageOptions.maxHeight; + const height = imageOptions.height || imageOptions.maxHeight; - list.push({ + return { src: url, sizes: height + 'x' + height - }); + }; + } else { + return null; } } - function getImageUrls(item) { + function getImageUrls(item, imageSizes = [96, 128, 192, 256, 384, 512]) { + const list = []; - var list = []; - - pushImageUrl(item, {height: 96}, list); - pushImageUrl(item, {height: 128}, list); - pushImageUrl(item, {height: 192}, list); - pushImageUrl(item, {height: 256}, list); - pushImageUrl(item, {height: 384}, list); - pushImageUrl(item, {height: 512}, list); + imageSizes.forEach((size) => { + const url = getImageUrl(item, {height: size}); + if (url !== null) { + list.push(url); + } + }); return list; } function updatePlayerState(player, state, eventName) { // Don't go crazy reporting position changes - if (eventName == 'timeupdate') { + if (eventName === 'timeupdate') { // Only report if this item hasn't been reported yet, or if there's an actual playback change. // Don't report on simple time updates return; } - var item = state.NowPlayingItem; + const item = state.NowPlayingItem; if (!item) { hideMediaControls(); return; } - if (eventName == 'init') { // transform "init" event into "timeupdate" to restraint update rate + if (eventName === 'init') { // transform "init" event into "timeupdate" to restraint update rate eventName = 'timeupdate'; } - var isVideo = item.MediaType === 'Video'; - var isLocalPlayer = player.isLocalPlayer || false; + const isVideo = item.MediaType === 'Video'; + const isLocalPlayer = player.isLocalPlayer || false; // Local players do their own notifications if (isLocalPlayer && isVideo) { return; } - var playState = state.PlayState || {}; - var parts = nowPlayingHelper.getNowPlayingNames(item); - var artist = parts[parts.length - 1].text; - var title = parts.length === 1 ? '' : parts[0].text; - var albumArtist; + const playState = state.PlayState || {}; + const parts = nowPlayingHelper.getNowPlayingNames(item); + const artist = parts[parts.length - 1].text; + const title = parts.length === 1 ? '' : parts[0].text; - if (item.AlbumArtists && item.AlbumArtists[0]) { - albumArtist = item.AlbumArtists[0].Name; - } - - var album = item.Album || ''; - var itemId = item.Id; + const album = item.Album || ''; + const itemId = item.Id; // Convert to ms - var duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0); - var currentTime = parseInt(playState.PositionTicks ? (playState.PositionTicks / 10000) : 0); + const duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0); + const currentTime = parseInt(playState.PositionTicks ? (playState.PositionTicks / 10000) : 0); - var isPaused = playState.IsPaused || false; - var canSeek = playState.CanSeek || false; + const isPaused = playState.IsPaused || false; + const canSeek = playState.CanSeek || false; - if (navigator.mediaSession) { + if ('mediaSession' in navigator) { + /* eslint-disable-next-line compat/compat */ navigator.mediaSession.metadata = new MediaMetadata({ title: title, artist: artist, album: album, - artwork: getImageUrls(item), - albumArtist: albumArtist, - currentTime: currentTime, - duration: duration, - paused: isPaused, - itemId: itemId, - mediaType: item.MediaType + artwork: getImageUrls(item) }); } else { - var imageUrl = []; - pushImageUrl(item, {maxHeight: 400}, imageUrl); - - if (imageUrl.length) { - imageUrl = imageUrl[0].src; - } else { - imageUrl = null; - } + let itemImageUrl = seriesImageUrl(item, { maxHeight: 3000 }) || imageUrl(item, { maxHeight: 3000 }); window.NativeShell.updateMediaSession({ action: eventName, @@ -175,7 +138,7 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f album: album, duration: duration, position: currentTime, - imageUrl: imageUrl, + imageUrl: itemImageUrl, canSeek: canSeek, isPaused: isPaused }); @@ -183,37 +146,25 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f } function onGeneralEvent(e) { + const state = playbackManager.getPlayerState(this); - var player = this; - var state = playbackManager.getPlayerState(player); - - updatePlayerState(player, state, e.type); + updatePlayerState(this, state, e.type); } function onStateChanged(e, state) { - - var player = this; - updatePlayerState(player, state, 'statechange'); + updatePlayerState(this, state, 'statechange'); } function onPlaybackStart(e, state) { - - var player = this; - - updatePlayerState(player, state, e.type); + updatePlayerState(this, state, e.type); } - function onPlaybackStopped(e, state) { - - var player = this; - + function onPlaybackStopped() { hideMediaControls(); } function releaseCurrentPlayer() { - if (currentPlayer) { - events.off(currentPlayer, 'playbackstart', onPlaybackStart); events.off(currentPlayer, 'playbackstop', onPlaybackStopped); events.off(currentPlayer, 'unpause', onGeneralEvent); @@ -228,9 +179,8 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f } function hideMediaControls() { - lastUpdateTime = 0; - - if (navigator.mediaSession) { + if ('mediaSession' in navigator) { + /* eslint-disable-next-line compat/compat */ navigator.mediaSession.metadata = null; } else { window.NativeShell.hideMediaSession(); @@ -238,7 +188,6 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f } function bindToPlayer(player) { - releaseCurrentPlayer(); if (!player) { @@ -247,7 +196,7 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f currentPlayer = player; - var state = playbackManager.getPlayerState(player); + const state = playbackManager.getPlayerState(player); updatePlayerState(player, state, 'init'); events.on(currentPlayer, 'playbackstart', onPlaybackStart); @@ -261,37 +210,43 @@ define(['playbackManager', 'nowPlayingHelper', 'events', 'connectionManager'], f function execute(name) { playbackManager[name](currentPlayer); } - if (navigator.mediaSession) { + if ('mediaSession' in navigator) { + /* eslint-disable-next-line compat/compat */ navigator.mediaSession.setActionHandler('previoustrack', function () { execute('previousTrack'); }); + /* eslint-disable-next-line compat/compat */ navigator.mediaSession.setActionHandler('nexttrack', function () { execute('nextTrack'); }); + /* eslint-disable-next-line compat/compat */ navigator.mediaSession.setActionHandler('play', function () { execute('unpause'); }); + /* eslint-disable-next-line compat/compat */ navigator.mediaSession.setActionHandler('pause', function () { execute('pause'); }); + /* eslint-disable-next-line compat/compat */ navigator.mediaSession.setActionHandler('seekbackward', function () { execute('rewind'); }); + /* eslint-disable-next-line compat/compat */ navigator.mediaSession.setActionHandler('seekforward', function () { execute('fastForward'); }); } events.on(playbackManager, 'playerchange', function () { - bindToPlayer(playbackManager.getCurrentPlayer()); }); bindToPlayer(playbackManager.getCurrentPlayer()); -}); + +/* eslint-enable indent */ diff --git a/src/components/playback/nowplayinghelper.js b/src/components/playback/nowplayinghelper.js index b1af977ab0..310edc03c3 100644 --- a/src/components/playback/nowplayinghelper.js +++ b/src/components/playback/nowplayinghelper.js @@ -1,86 +1,82 @@ -define([], function () { - 'use strict'; +export function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) { - function getNowPlayingNames(nowPlayingItem, includeNonNameInfo) { + var topItem = nowPlayingItem; + var bottomItem = null; + var topText = nowPlayingItem.Name; - var topItem = nowPlayingItem; - var bottomItem = null; - var topText = nowPlayingItem.Name; - - if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType === 'Audio') { - topItem = { - Id: nowPlayingItem.AlbumId, - Name: nowPlayingItem.Album, - Type: 'MusicAlbum', - IsFolder: true - }; - } - - if (nowPlayingItem.MediaType === 'Video') { - if (nowPlayingItem.IndexNumber != null) { - topText = nowPlayingItem.IndexNumber + " - " + topText; - } - if (nowPlayingItem.ParentIndexNumber != null) { - topText = nowPlayingItem.ParentIndexNumber + "." + topText; - } - } - - var bottomText = ''; - - if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) { - - bottomItem = { - Id: nowPlayingItem.ArtistItems[0].Id, - Name: nowPlayingItem.ArtistItems[0].Name, - Type: 'MusicArtist', - IsFolder: true - }; - - bottomText = nowPlayingItem.ArtistItems.map(function (a) { - return a.Name; - }).join(', '); - - } else if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) { - - bottomText = nowPlayingItem.Artists.join(', '); - } else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) { - bottomText = topText; - topText = nowPlayingItem.SeriesName || nowPlayingItem.Album; - - bottomItem = topItem; - - if (nowPlayingItem.SeriesId) { - topItem = { - Id: nowPlayingItem.SeriesId, - Name: nowPlayingItem.SeriesName, - Type: 'Series', - IsFolder: true - }; - } else { - topItem = null; - } - } else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) { - bottomText = nowPlayingItem.ProductionYear; - } - - var list = []; - - list.push({ - text: topText, - item: topItem - }); - - if (bottomText) { - list.push({ - text: bottomText, - item: bottomItem - }); - } - - return list; + if (nowPlayingItem.AlbumId && nowPlayingItem.MediaType === 'Audio') { + topItem = { + Id: nowPlayingItem.AlbumId, + Name: nowPlayingItem.Album, + Type: 'MusicAlbum', + IsFolder: true + }; } - return { - getNowPlayingNames: getNowPlayingNames - }; -}); + if (nowPlayingItem.MediaType === 'Video') { + if (nowPlayingItem.IndexNumber != null) { + topText = nowPlayingItem.IndexNumber + ' - ' + topText; + } + if (nowPlayingItem.ParentIndexNumber != null) { + topText = nowPlayingItem.ParentIndexNumber + '.' + topText; + } + } + + var bottomText = ''; + + if (nowPlayingItem.ArtistItems && nowPlayingItem.ArtistItems.length) { + + bottomItem = { + Id: nowPlayingItem.ArtistItems[0].Id, + Name: nowPlayingItem.ArtistItems[0].Name, + Type: 'MusicArtist', + IsFolder: true + }; + + bottomText = nowPlayingItem.ArtistItems.map(function (a) { + return a.Name; + }).join(', '); + + } else if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) { + + bottomText = nowPlayingItem.Artists.join(', '); + } else if (nowPlayingItem.SeriesName || nowPlayingItem.Album) { + bottomText = topText; + topText = nowPlayingItem.SeriesName || nowPlayingItem.Album; + + bottomItem = topItem; + + if (nowPlayingItem.SeriesId) { + topItem = { + Id: nowPlayingItem.SeriesId, + Name: nowPlayingItem.SeriesName, + Type: 'Series', + IsFolder: true + }; + } else { + topItem = null; + } + } else if (nowPlayingItem.ProductionYear && includeNonNameInfo !== false) { + bottomText = nowPlayingItem.ProductionYear; + } + + var list = []; + + list.push({ + text: topText, + item: topItem + }); + + if (bottomText) { + list.push({ + text: bottomText, + item: bottomItem + }); + } + + return list; +} + +export default { + getNowPlayingNames: getNowPlayingNames +}; diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index e9f7447691..73f07a05f2 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -1,6 +1,9 @@ -define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'playQueueManager', 'userSettings', 'globalize', 'connectionManager', 'loading', 'apphost', 'fullscreenManager'], function (events, datetime, appSettings, itemHelper, pluginManager, PlayQueueManager, userSettings, globalize, connectionManager, loading, apphost, fullscreenManager) { +define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'playQueueManager', 'userSettings', 'globalize', 'connectionManager', 'loading', 'apphost', 'screenfull'], function (events, datetime, appSettings, itemHelper, pluginManager, PlayQueueManager, userSettings, globalize, connectionManager, loading, apphost, screenfull) { 'use strict'; + /** Delay time in ms for reportPlayback logging */ + const reportPlaybackLogDelay = 1e3; + function enableLocalPlaylistManagement(player) { if (player.getPlaylist) { @@ -17,9 +20,11 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } function bindToFullscreenChange(player) { - events.on(fullscreenManager, 'fullscreenchange', function () { - events.trigger(player, 'fullscreenchange'); - }); + if (screenfull.isEnabled) { + screenfull.on('change', function () { + events.trigger(player, 'fullscreenchange'); + }); + } } function triggerPlayerChange(playbackManagerInstance, newPlayer, newTarget, previousPlayer, previousTargetInfo) { @@ -38,11 +43,18 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla events.trigger(playbackManagerInstance, 'playerchange', [newPlayer, newTarget, previousPlayer]); } + /** Last invoked method */ + let reportPlaybackLastMethod; + + /** Last invoke time of method */ + let reportPlaybackLastTime; + function reportPlayback(playbackManagerInstance, state, player, reportPlaylist, serverId, method, progressEventName) { if (!serverId) { // Not a server item // We can expand on this later and possibly report them + events.trigger(playbackManagerInstance, 'reportplayback', [false]); return; } @@ -57,9 +69,20 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla addPlaylistToPlaybackReport(playbackManagerInstance, info, player, serverId); } - console.debug(method + '-' + JSON.stringify(info)); + const now = (new Date).getTime(); + + if (method !== reportPlaybackLastMethod || now - (reportPlaybackLastTime || 0) >= reportPlaybackLogDelay) { + console.debug(method + '-' + JSON.stringify(info)); + reportPlaybackLastMethod = method; + reportPlaybackLastTime = now; + } + var apiClient = connectionManager.getApiClient(serverId); - apiClient[method](info); + var reportPlaybackPromise = apiClient[method](info); + // Notify that report has been sent + reportPlaybackPromise.then(() => { + events.trigger(playbackManagerInstance, 'reportplayback', [true]); + }); } function getPlaylistSync(playbackManagerInstance, player) { @@ -110,8 +133,8 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } else { query.Limit = query.Limit || 300; - query.Fields = "Chapters"; - query.ExcludeLocationTypes = "Virtual"; + query.Fields = 'Chapters'; + query.ExcludeLocationTypes = 'Virtual'; query.EnableTotalRecordCount = false; query.CollapseBoxSetItems = false; @@ -146,7 +169,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla function backdropImageUrl(apiClient, item, options) { options = options || {}; - options.type = options.type || "Backdrop"; + options.type = options.type || 'Backdrop'; // If not resizing, get the original image if (!options.maxWidth && !options.width && !options.maxHeight && !options.height) { @@ -203,15 +226,15 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } function getParam(name, url) { - name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); - var regexS = "[\\?&]" + name + "=([^&#]*)"; - var regex = new RegExp(regexS, "i"); + name = name.replace(/[\[]/, '\\\[').replace(/[\]]/, '\\\]'); + var regexS = '[\\?&]' + name + '=([^&#]*)'; + var regex = new RegExp(regexS, 'i'); var results = regex.exec(url); if (results == null) { - return ""; + return ''; } else { - return decodeURIComponent(results[1].replace(/\+/g, " ")); + return decodeURIComponent(results[1].replace(/\+/g, ' ')); } } @@ -291,13 +314,11 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla if (codecProfile.Type === 'Audio') { (codecProfile.Conditions || []).map(function (condition) { if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioBitDepth') { - maxAudioBitDepth = condition.Value; - } - if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioSampleRate') { - maxAudioSampleRate = condition.Value; - } - if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioBitrate') { - maxAudioBitrate = condition.Value; + return maxAudioBitDepth = condition.Value; + } else if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioSampleRate') { + return maxAudioSampleRate = condition.Value; + } else if (condition.Condition === 'LessThanEqual' && condition.Property === 'AudioBitrate') { + return maxAudioBitrate = condition.Value; } }); } @@ -598,8 +619,8 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla url: apiClient.getUrl('LiveStreams/Open', query), type: 'POST', data: JSON.stringify(postData), - contentType: "application/json", - dataType: "json" + contentType: 'application/json', + dataType: 'json' }); } @@ -1108,21 +1129,19 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } self.canPlay = function (item) { - var itemType = item.Type; - if (itemType === "PhotoAlbum" || itemType === "MusicGenre" || itemType === "Season" || itemType === "Series" || itemType === "BoxSet" || itemType === "MusicAlbum" || itemType === "MusicArtist" || itemType === "Playlist") { + if (itemType === 'PhotoAlbum' || itemType === 'MusicGenre' || itemType === 'Season' || itemType === 'Series' || itemType === 'BoxSet' || itemType === 'MusicAlbum' || itemType === 'MusicArtist' || itemType === 'Playlist') { return true; } - if (item.LocationType === "Virtual") { - if (itemType !== "Program") { + if (item.LocationType === 'Virtual') { + if (itemType !== 'Program') { return false; } } - if (itemType === "Program") { - + if (itemType === 'Program') { if (!item.EndDate || !item.StartDate) { return false; } @@ -1518,7 +1537,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return player.isFullscreen(); } - return fullscreenManager.isFullScreen(); + return screenfull.isFullscreen; }; self.toggleFullscreen = function (player) { @@ -1528,10 +1547,8 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return player.toggleFulscreen(); } - if (fullscreenManager.isFullScreen()) { - fullscreenManager.exitFullscreen(); - } else { - fullscreenManager.requestFullscreen(); + if (screenfull.isEnabled) { + screenfull.toggle(); } }; @@ -1861,40 +1878,37 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla var queryOptions = options.queryOptions || {}; - if (firstItem.Type === "Program") { + if (firstItem.Type === 'Program') { promise = getItemsForPlayback(serverId, { Ids: firstItem.ChannelId }); - } else if (firstItem.Type === "Playlist") { + } else if (firstItem.Type === 'Playlist') { promise = getItemsForPlayback(serverId, { ParentId: firstItem.Id, SortBy: options.shuffle ? 'Random' : null }); - } else if (firstItem.Type === "MusicArtist") { + } else if (firstItem.Type === 'MusicArtist') { promise = getItemsForPlayback(serverId, { ArtistIds: firstItem.Id, - Filters: "IsNotFolder", + Filters: 'IsNotFolder', Recursive: true, SortBy: options.shuffle ? 'Random' : 'SortName', - MediaTypes: "Audio" + MediaTypes: 'Audio' }); - } else if (firstItem.MediaType === "Photo") { + } else if (firstItem.MediaType === 'Photo') { promise = getItemsForPlayback(serverId, { ParentId: firstItem.ParentId, - Filters: "IsNotFolder", + Filters: 'IsNotFolder', // Setting this to true may cause some incorrect sorting Recursive: false, SortBy: options.shuffle ? 'Random' : 'SortName', - MediaTypes: "Photo,Video", - Limit: 500 - + MediaTypes: 'Photo,Video' }).then(function (result) { - var items = result.Items; var index = items.map(function (i) { @@ -1911,40 +1925,40 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return Promise.resolve(result); }); - } else if (firstItem.Type === "PhotoAlbum") { + } else if (firstItem.Type === 'PhotoAlbum') { promise = getItemsForPlayback(serverId, { ParentId: firstItem.Id, - Filters: "IsNotFolder", + Filters: 'IsNotFolder', // Setting this to true may cause some incorrect sorting Recursive: false, SortBy: options.shuffle ? 'Random' : 'SortName', - MediaTypes: "Photo,Video", + MediaTypes: 'Photo,Video', Limit: 1000 }); - } else if (firstItem.Type === "MusicGenre") { + } else if (firstItem.Type === 'MusicGenre') { promise = getItemsForPlayback(serverId, { GenreIds: firstItem.Id, - Filters: "IsNotFolder", + Filters: 'IsNotFolder', Recursive: true, SortBy: options.shuffle ? 'Random' : 'SortName', - MediaTypes: "Audio" + MediaTypes: 'Audio' }); } else if (firstItem.IsFolder) { promise = getItemsForPlayback(serverId, mergePlaybackQueries({ ParentId: firstItem.Id, - Filters: "IsNotFolder", + Filters: 'IsNotFolder', Recursive: true, // These are pre-sorted SortBy: options.shuffle ? 'Random' : (['BoxSet'].indexOf(firstItem.Type) === -1 ? 'SortName' : null), - MediaTypes: "Audio,Video" + MediaTypes: 'Audio,Video' }, queryOptions)); - } else if (firstItem.Type === "Episode" && items.length === 1 && getPlayer(firstItem, options).supportsProgress !== false) { + } else if (firstItem.Type === 'Episode' && items.length === 1 && getPlayer(firstItem, options).supportsProgress !== false) { promise = new Promise(function (resolve, reject) { var apiClient = connectionManager.getApiClient(firstItem.ServerId); @@ -1960,7 +1974,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla IsVirtualUnaired: false, IsMissing: false, UserId: apiClient.getCurrentUserId(), - Fields: "Chapters" + Fields: 'Chapters' }).then(function (episodesResult) { @@ -2168,7 +2182,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla // Only used internally self.getCurrentTicks = getCurrentTicks; - function playPhotos(items, options, user) { + function playOther(items, options, user) { var playStartIndex = options.startIndex || 0; var player = getPlayer(items[playStartIndex], options); @@ -2197,9 +2211,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return Promise.reject(); } - if (firstItem.MediaType === "Photo") { + if (firstItem.MediaType === 'Photo' || firstItem.MediaType === 'Book') { - return playPhotos(items, options, user); + return playOther(items, options, user); } var apiClient = connectionManager.getApiClient(firstItem.ServerId); @@ -3140,7 +3154,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla AllowVideoStreamCopy: false, AllowAudioStreamCopy: currentlyPreventsAudioStreamCopy || currentlyPreventsVideoStreamCopy ? false : null - }, true); + }); return; } @@ -3378,7 +3392,6 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla pluginManager.ofType('mediaplayer').map(initMediaPlayer); function sendProgressUpdate(player, progressEventName, reportPlaylist) { - if (!player) { throw new Error('player cannot be null'); } @@ -3762,6 +3775,20 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } }; + PlaybackManager.prototype.setPlaybackRate = function (value, player = this._currentPlayer) { + if (player && player.setPlaybackRate) { + player.setPlaybackRate(value); + } + }; + + PlaybackManager.prototype.getPlaybackRate = function (player = this._currentPlayer) { + if (player && player.getPlaybackRate) { + return player.getPlaybackRate(); + } + + return null; + }; + PlaybackManager.prototype.instantMix = function (item, player) { player = player || this._currentPlayer; @@ -3836,23 +3863,23 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla if (player.isLocalPlayer) { var list = [ - "GoHome", - "GoToSettings", - "VolumeUp", - "VolumeDown", - "Mute", - "Unmute", - "ToggleMute", - "SetVolume", - "SetAudioStreamIndex", - "SetSubtitleStreamIndex", - "SetMaxStreamingBitrate", - "DisplayContent", - "GoToSearch", - "DisplayMessage", - "SetRepeatMode", - "PlayMediaSource", - "PlayTrailers" + 'GoHome', + 'GoToSettings', + 'VolumeUp', + 'VolumeDown', + 'Mute', + 'Unmute', + 'ToggleMute', + 'SetVolume', + 'SetAudioStreamIndex', + 'SetSubtitleStreamIndex', + 'SetMaxStreamingBitrate', + 'DisplayContent', + 'GoToSearch', + 'DisplayMessage', + 'SetRepeatMode', + 'PlayMediaSource', + 'PlayTrailers' ]; if (apphost.supports('fullscreenchange')) { @@ -3872,6 +3899,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla if (player.supports('SetAspectRatio')) { list.push('SetAspectRatio'); } + if (player.supports('PlaybackRate')) { + list.push('PlaybackRate'); + } } return list; diff --git a/src/components/playback/playbackorientation.js b/src/components/playback/playbackorientation.js index 5b178dbf08..2078c6f6a8 100644 --- a/src/components/playback/playbackorientation.js +++ b/src/components/playback/playbackorientation.js @@ -1,55 +1,57 @@ -define(['playbackManager', 'layoutManager', 'events'], function (playbackManager, layoutManager, events) { - "use strict"; +import playbackManager from 'playbackManager'; +import layoutManager from 'layoutManager'; +import events from 'events'; - var orientationLocked; +var orientationLocked; - function onOrientationChangeSuccess() { - orientationLocked = true; - } +function onOrientationChangeSuccess() { + orientationLocked = true; +} - function onOrientationChangeError(err) { - orientationLocked = false; - console.error('error locking orientation: ' + err); - } +function onOrientationChangeError(err) { + orientationLocked = false; + console.error('error locking orientation: ' + err); +} - events.on(playbackManager, 'playbackstart', function (e, player, state) { +events.on(playbackManager, 'playbackstart', function (e, player, state) { - var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player); + var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player); - if (isLocalVideo && layoutManager.mobile) { - var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || (screen.orientation && screen.orientation.lock); + if (isLocalVideo && layoutManager.mobile) { + /* eslint-disable-next-line compat/compat */ + var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || (screen.orientation && screen.orientation.lock); - if (lockOrientation) { + if (lockOrientation) { - try { - var promise = lockOrientation('landscape'); - if (promise.then) { - promise.then(onOrientationChangeSuccess, onOrientationChangeError); - } else { - // returns a boolean - orientationLocked = promise; - } - } catch (err) { - onOrientationChangeError(err); + try { + var promise = lockOrientation('landscape'); + if (promise.then) { + promise.then(onOrientationChangeSuccess, onOrientationChangeError); + } else { + // returns a boolean + orientationLocked = promise; } + } catch (err) { + onOrientationChangeError(err); } } - }); - - events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) { - - if (orientationLocked && !playbackStopInfo.nextMediaType) { - - var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || (screen.orientation && screen.orientation.unlock); - - if (unlockOrientation) { - try { - unlockOrientation(); - } catch (err) { - console.error('error unlocking orientation: ' + err); - } - orientationLocked = false; - } - } - }); + } +}); + +events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) { + + if (orientationLocked && !playbackStopInfo.nextMediaType) { + + /* eslint-disable-next-line compat/compat */ + var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || (screen.orientation && screen.orientation.unlock); + + if (unlockOrientation) { + try { + unlockOrientation(); + } catch (err) { + console.error('error unlocking orientation: ' + err); + } + orientationLocked = false; + } + } }); diff --git a/src/components/playback/playerSelectionMenu.js b/src/components/playback/playerSelectionMenu.js index 97e6e46230..91b1ddc20b 100644 --- a/src/components/playback/playerSelectionMenu.js +++ b/src/components/playback/playerSelectionMenu.js @@ -1,320 +1,325 @@ -define(['appSettings', 'events', 'browser', 'loading', 'playbackManager', 'appRouter', 'globalize', 'apphost'], function (appSettings, events, browser, loading, playbackManager, appRouter, globalize, appHost) { - 'use strict'; +import appSettings from 'appSettings'; +import events from 'events'; +import browser from 'browser'; +import loading from 'loading'; +import playbackManager from 'playbackManager'; +import appRouter from 'appRouter'; +import globalize from 'globalize'; +import appHost from 'apphost'; - function mirrorItem(info, player) { +function mirrorItem(info, player) { - var item = info.item; + var item = info.item; - playbackManager.displayContent({ + playbackManager.displayContent({ - ItemName: item.Name, - ItemId: item.Id, - ItemType: item.Type, - Context: info.context - }, player); - } + ItemName: item.Name, + ItemId: item.Id, + ItemType: item.Type, + Context: info.context + }, player); +} - function mirrorIfEnabled(info) { +function mirrorIfEnabled(info) { - if (info && playbackManager.enableDisplayMirroring()) { + if (info && playbackManager.enableDisplayMirroring()) { - var getPlayerInfo = playbackManager.getPlayerInfo(); + var getPlayerInfo = playbackManager.getPlayerInfo(); - if (getPlayerInfo) { - if (!getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { - mirrorItem(info, playbackManager.getCurrentPlayer()); - } + if (getPlayerInfo) { + if (!getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { + mirrorItem(info, playbackManager.getCurrentPlayer()); } } } +} - function emptyCallback() { - // avoid console logs about uncaught promises +function emptyCallback() { + // avoid console logs about uncaught promises +} + +function getTargetSecondaryText(target) { + + if (target.user) { + + return target.user.Name; } - function getTargetSecondaryText(target) { + return null; +} - if (target.user) { +function getIcon(target) { - return target.user.Name; - } + var deviceType = target.deviceType; - return null; - } - - function getIcon(target) { - - var deviceType = target.deviceType; - - if (!deviceType && target.isLocalPlayer) { - if (browser.tv) { - deviceType = 'tv'; - } else if (browser.mobile) { - deviceType = 'smartphone'; - } else { - deviceType = 'desktop'; - } - } - - if (!deviceType) { + if (!deviceType && target.isLocalPlayer) { + if (browser.tv) { deviceType = 'tv'; - } - - switch (deviceType) { - - case 'smartphone': - return 'smartphone'; - case 'tablet': - return 'tablet'; - case 'tv': - return 'tv'; - case 'cast': - return 'cast'; - case 'desktop': - return 'computer'; - default: - return 'tv'; - } - } - - function showPlayerSelection(button) { - - var currentPlayerInfo = playbackManager.getPlayerInfo(); - - if (currentPlayerInfo) { - if (!currentPlayerInfo.isLocalPlayer) { - showActivePlayerMenu(currentPlayerInfo); - return; - } - } - - var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null; - - loading.show(); - - playbackManager.getTargets().then(function (targets) { - - var menuItems = targets.map(function (t) { - - var name = t.name; - - if (t.appName && t.appName !== t.name) { - name += " - " + t.appName; - } - - return { - name: name, - id: t.id, - selected: currentPlayerId === t.id, - secondaryText: getTargetSecondaryText(t), - icon: getIcon(t) - }; - - }); - - require(['actionsheet'], function (actionsheet) { - - loading.hide(); - - var menuOptions = { - title: globalize.translate('HeaderPlayOn'), - items: menuItems, - positionTo: button, - - resolveOnClick: true, - border: true - }; - - // Unfortunately we can't allow the url to change or chromecast will throw a security error - // Might be able to solve this in the future by moving the dialogs to hashbangs - if (!(!browser.chrome || appHost.supports('castmenuhashchange'))) { - menuOptions.enableHistory = false; - } - - actionsheet.show(menuOptions).then(function (id) { - - var target = targets.filter(function (t) { - return t.id === id; - })[0]; - - playbackManager.trySetActivePlayer(target.playerName, target); - - mirrorIfEnabled(); - - }, emptyCallback); - }); - }); - } - - function showActivePlayerMenu(playerInfo) { - - require(['dialogHelper', 'dialog', 'emby-checkbox', 'emby-button'], function (dialogHelper) { - showActivePlayerMenuInternal(dialogHelper, playerInfo); - }); - } - - function disconnectFromPlayer(currentDeviceName) { - - if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) { - - require(['dialog'], function (dialog) { - - var menuItems = []; - - menuItems.push({ - name: globalize.translate('Yes'), - id: 'yes' - }); - menuItems.push({ - name: globalize.translate('No'), - id: 'no' - }); - - dialog({ - buttons: menuItems, - //positionTo: positionTo, - text: globalize.translate('ConfirmEndPlayerSession', currentDeviceName) - - }).then(function (id) { - switch (id) { - - case 'yes': - playbackManager.getCurrentPlayer().endSession(); - playbackManager.setDefaultPlayerActive(); - break; - case 'no': - playbackManager.setDefaultPlayerActive(); - break; - default: - break; - } - }); - - }); - + } else if (browser.mobile) { + deviceType = 'smartphone'; } else { - - playbackManager.setDefaultPlayerActive(); + deviceType = 'desktop'; } } - function showActivePlayerMenuInternal(dialogHelper, playerInfo) { - - var html = ''; - - var dialogOptions = { - removeOnClose: true - }; - - dialogOptions.modal = false; - dialogOptions.entryAnimationDuration = 160; - dialogOptions.exitAnimationDuration = 160; - dialogOptions.autoFocus = false; - - var dlg = dialogHelper.createDialog(dialogOptions); - - dlg.classList.add('promptDialog'); - - var currentDeviceName = (playerInfo.deviceName || playerInfo.name); - - html += '
'; - html += '

'; - html += currentDeviceName; - html += '

'; - - html += '
'; - - if (playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { - - html += ''; - } - - html += '
'; - - html += '
'; - - html += ''; - html += ''; - html += ''; - html += '
'; - - html += '
'; - dlg.innerHTML = html; - - var chkMirror = dlg.querySelector('.chkMirror'); - - if (chkMirror) { - chkMirror.addEventListener('change', onMirrorChange); - } - - var destination = ''; - - var btnRemoteControl = dlg.querySelector('.btnRemoteControl'); - if (btnRemoteControl) { - btnRemoteControl.addEventListener('click', function () { - destination = 'nowplaying'; - dialogHelper.close(dlg); - }); - } - - dlg.querySelector('.btnDisconnect').addEventListener('click', function () { - destination = 'disconnectFromPlayer'; - dialogHelper.close(dlg); - }); - - dlg.querySelector('.btnCancel').addEventListener('click', function () { - dialogHelper.close(dlg); - }); - - dialogHelper.open(dlg).then(function () { - if (destination === 'nowplaying') { - appRouter.showNowPlaying(); - } else if (destination === 'disconnectFromPlayer') { - disconnectFromPlayer(currentDeviceName); - } - }, emptyCallback); + if (!deviceType) { + deviceType = 'tv'; } - function onMirrorChange() { - playbackManager.enableDisplayMirroring(this.checked); + switch (deviceType) { + + case 'smartphone': + return 'smartphone'; + case 'tablet': + return 'tablet'; + case 'tv': + return 'tv'; + case 'cast': + return 'cast'; + case 'desktop': + return 'computer'; + default: + return 'tv'; } +} - document.addEventListener('viewshow', function (e) { +export function show(button) { - var state = e.detail.state || {}; - var item = state.item; + var currentPlayerInfo = playbackManager.getPlayerInfo(); - if (item && item.ServerId) { - mirrorIfEnabled({ - item: item - }); + if (currentPlayerInfo) { + if (!currentPlayerInfo.isLocalPlayer) { + showActivePlayerMenu(currentPlayerInfo); return; } - }); + } - events.on(appSettings, 'change', function (e, name) { - if (name === 'displaymirror') { - mirrorIfEnabled(); - } - }); + var currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null; - events.on(playbackManager, 'pairing', function (e) { - loading.show(); - }); + loading.show(); - events.on(playbackManager, 'paired', function (e) { - loading.hide(); - }); + playbackManager.getTargets().then(function (targets) { - events.on(playbackManager, 'pairerror', function (e) { - loading.hide(); - }); + var menuItems = targets.map(function (t) { - return { - show: showPlayerSelection + var name = t.name; + + if (t.appName && t.appName !== t.name) { + name += ' - ' + t.appName; + } + + return { + name: name, + id: t.id, + selected: currentPlayerId === t.id, + secondaryText: getTargetSecondaryText(t), + icon: getIcon(t) + }; + + }); + + require(['actionsheet'], function (actionsheet) { + + loading.hide(); + + var menuOptions = { + title: globalize.translate('HeaderPlayOn'), + items: menuItems, + positionTo: button, + + resolveOnClick: true, + border: true + }; + + // Unfortunately we can't allow the url to change or chromecast will throw a security error + // Might be able to solve this in the future by moving the dialogs to hashbangs + if (!(!browser.chrome || appHost.supports('castmenuhashchange'))) { + menuOptions.enableHistory = false; + } + + actionsheet.show(menuOptions).then(function (id) { + + var target = targets.filter(function (t) { + return t.id === id; + })[0]; + + playbackManager.trySetActivePlayer(target.playerName, target); + + mirrorIfEnabled(); + + }, emptyCallback); + }); + }); +} + +function showActivePlayerMenu(playerInfo) { + + require(['dialogHelper', 'dialog', 'emby-checkbox', 'emby-button'], function (dialogHelper) { + showActivePlayerMenuInternal(dialogHelper, playerInfo); + }); +} + +function disconnectFromPlayer(currentDeviceName) { + + if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) { + + require(['dialog'], function (dialog) { + + var menuItems = []; + + menuItems.push({ + name: globalize.translate('Yes'), + id: 'yes' + }); + menuItems.push({ + name: globalize.translate('No'), + id: 'no' + }); + + dialog({ + buttons: menuItems, + //positionTo: positionTo, + text: globalize.translate('ConfirmEndPlayerSession', currentDeviceName) + + }).then(function (id) { + switch (id) { + + case 'yes': + playbackManager.getCurrentPlayer().endSession(); + playbackManager.setDefaultPlayerActive(); + break; + case 'no': + playbackManager.setDefaultPlayerActive(); + break; + default: + break; + } + }); + + }); + + } else { + + playbackManager.setDefaultPlayerActive(); + } +} + +function showActivePlayerMenuInternal(dialogHelper, playerInfo) { + + var html = ''; + + var dialogOptions = { + removeOnClose: true }; + + dialogOptions.modal = false; + dialogOptions.entryAnimationDuration = 160; + dialogOptions.exitAnimationDuration = 160; + dialogOptions.autoFocus = false; + + var dlg = dialogHelper.createDialog(dialogOptions); + + dlg.classList.add('promptDialog'); + + var currentDeviceName = (playerInfo.deviceName || playerInfo.name); + + html += '
'; + html += '

'; + html += currentDeviceName; + html += '

'; + + html += '
'; + + if (playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { + + html += ''; + } + + html += '
'; + + html += '
'; + + html += ''; + html += ''; + html += ''; + html += '
'; + + html += '
'; + dlg.innerHTML = html; + + var chkMirror = dlg.querySelector('.chkMirror'); + + if (chkMirror) { + chkMirror.addEventListener('change', onMirrorChange); + } + + var destination = ''; + + var btnRemoteControl = dlg.querySelector('.btnRemoteControl'); + if (btnRemoteControl) { + btnRemoteControl.addEventListener('click', function () { + destination = 'nowplaying'; + dialogHelper.close(dlg); + }); + } + + dlg.querySelector('.btnDisconnect').addEventListener('click', function () { + destination = 'disconnectFromPlayer'; + dialogHelper.close(dlg); + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + dialogHelper.close(dlg); + }); + + dialogHelper.open(dlg).then(function () { + if (destination === 'nowplaying') { + appRouter.showNowPlaying(); + } else if (destination === 'disconnectFromPlayer') { + disconnectFromPlayer(currentDeviceName); + } + }, emptyCallback); +} + +function onMirrorChange() { + playbackManager.enableDisplayMirroring(this.checked); +} + +document.addEventListener('viewshow', function (e) { + + var state = e.detail.state || {}; + var item = state.item; + + if (item && item.ServerId) { + mirrorIfEnabled({ + item: item + }); + return; + } }); + +events.on(appSettings, 'change', function (e, name) { + if (name === 'displaymirror') { + mirrorIfEnabled(); + } +}); + +events.on(playbackManager, 'pairing', function (e) { + loading.show(); +}); + +events.on(playbackManager, 'paired', function (e) { + loading.hide(); +}); + +events.on(playbackManager, 'pairerror', function (e) { + loading.hide(); +}); + +export default { + show: show +}; diff --git a/src/components/playback/playersettingsmenu.js b/src/components/playback/playersettingsmenu.js index 086f98c2d4..33d252c52a 100644 --- a/src/components/playback/playersettingsmenu.js +++ b/src/components/playback/playersettingsmenu.js @@ -1,266 +1,272 @@ -define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'globalize', 'appSettings', 'qualityoptions'], function (connectionManager, actionsheet, datetime, playbackManager, globalize, appSettings, qualityoptions) { - 'use strict'; +import connectionManager from 'connectionManager'; +import actionsheet from 'actionsheet'; +import playbackManager from 'playbackManager'; +import globalize from 'globalize'; +import qualityoptions from 'qualityoptions'; - function showQualityMenu(player, btn) { +function showQualityMenu(player, btn) { - var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { - return stream.Type === "Video"; - })[0]; - var videoWidth = videoStream ? videoStream.Width : null; + var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { + return stream.Type === 'Video'; + })[0]; + var videoWidth = videoStream ? videoStream.Width : null; + var videoHeight = videoStream ? videoStream.Height : null; - var options = qualityoptions.getVideoQualityOptions({ - currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), - isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), - videoWidth: videoWidth, - enableAuto: true - }); + var options = qualityoptions.getVideoQualityOptions({ + currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), + isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), + videoWidth: videoWidth, + videoHeight: videoHeight, + enableAuto: true + }); - var menuItems = options.map(function (o) { - var opt = { - name: o.name, - id: o.bitrate, - asideText: o.secondaryText - }; + var menuItems = options.map(function (o) { + var opt = { + name: o.name, + id: o.bitrate, + asideText: o.secondaryText + }; - if (o.selected) { - opt.selected = true; - } + if (o.selected) { + opt.selected = true; + } - return opt; - }); + return opt; + }); - var selectedId = options.filter(function (o) { - return o.selected; - }); + var selectedId = options.filter(function (o) { + return o.selected; + }); - selectedId = selectedId.length ? selectedId[0].bitrate : null; + selectedId = selectedId.length ? selectedId[0].bitrate : null; - return actionsheet.show({ - items: menuItems, - positionTo: btn - }).then(function (id) { - var bitrate = parseInt(id); - if (bitrate !== selectedId) { - playbackManager.setMaxStreamingBitrate({ - enableAutomaticBitrateDetection: bitrate ? false : true, - maxBitrate: bitrate - }, player); - } - }); + return actionsheet.show({ + items: menuItems, + positionTo: btn + }).then(function (id) { + var bitrate = parseInt(id); + if (bitrate !== selectedId) { + playbackManager.setMaxStreamingBitrate({ + enableAutomaticBitrateDetection: bitrate ? false : true, + maxBitrate: bitrate + }, player); + } + }); +} + +function showRepeatModeMenu(player, btn) { + var menuItems = []; + var currentValue = playbackManager.getRepeatMode(player); + + menuItems.push({ + name: globalize.translate('RepeatAll'), + id: 'RepeatAll', + selected: currentValue === 'RepeatAll' + }); + + menuItems.push({ + name: globalize.translate('RepeatOne'), + id: 'RepeatOne', + selected: currentValue === 'RepeatOne' + }); + + menuItems.push({ + name: globalize.translate('None'), + id: 'RepeatNone', + selected: currentValue === 'RepeatNone' + }); + + return actionsheet.show({ + items: menuItems, + positionTo: btn + }).then(function (mode) { + if (mode) { + playbackManager.setRepeatMode(mode, player); + } + }); +} + +function getQualitySecondaryText(player) { + var state = playbackManager.getPlayerState(player); + var isAutoEnabled = playbackManager.enableAutomaticBitrateDetection(player); + var currentMaxBitrate = playbackManager.getMaxStreamingBitrate(player); + + var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { + return stream.Type === 'Video'; + })[0]; + + var videoWidth = videoStream ? videoStream.Width : null; + var videoHeight = videoStream ? videoStream.Height : null; + + var options = qualityoptions.getVideoQualityOptions({ + currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), + isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), + videoWidth: videoWidth, + videoHeight: videoHeight, + enableAuto: true + }); + + var menuItems = options.map(function (o) { + var opt = { + name: o.name, + id: o.bitrate, + asideText: o.secondaryText + }; + + if (o.selected) { + opt.selected = true; + } + + return opt; + }); + + var selectedOption = options.filter(function (o) { + return o.selected; + }); + + if (!selectedOption.length) { + return null; } - function showRepeatModeMenu(player, btn) { - var menuItems = []; - var currentValue = playbackManager.getRepeatMode(player); + selectedOption = selectedOption[0]; + var text = selectedOption.name; - menuItems.push({ - name: globalize.translate('RepeatAll'), - id: 'RepeatAll', - selected: currentValue === 'RepeatAll' - }); - - menuItems.push({ - name: globalize.translate('RepeatOne'), - id: 'RepeatOne', - selected: currentValue === 'RepeatOne' - }); - - menuItems.push({ - name: globalize.translate('None'), - id: 'RepeatNone', - selected: currentValue === 'RepeatNone' - }); - - return actionsheet.show({ - items: menuItems, - positionTo: btn - }).then(function (mode) { - if (mode) { - playbackManager.setRepeatMode(mode, player); - } - }); + if (selectedOption.autoText) { + if (state.PlayState && state.PlayState.PlayMethod !== 'Transcode') { + text += ' - Direct'; + } else { + text += ' ' + selectedOption.autoText; + } } - function getQualitySecondaryText(player) { - var state = playbackManager.getPlayerState(player); - var isAutoEnabled = playbackManager.enableAutomaticBitrateDetection(player); - var currentMaxBitrate = playbackManager.getMaxStreamingBitrate(player); + return text; +} - var videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { - return stream.Type === "Video"; - })[0]; +function showAspectRatioMenu(player, btn) { + // each has a name and id + var currentId = playbackManager.getAspectRatio(player); + var menuItems = playbackManager.getSupportedAspectRatios(player).map(function (i) { + return { + id: i.id, + name: i.name, + selected: i.id === currentId + }; + }); - var videoWidth = videoStream ? videoStream.Width : null; - - var options = qualityoptions.getVideoQualityOptions({ - currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player), - isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player), - videoWidth: videoWidth, - enableAuto: true - }); - - var menuItems = options.map(function (o) { - var opt = { - name: o.name, - id: o.bitrate, - asideText: o.secondaryText - }; - - if (o.selected) { - opt.selected = true; - } - - return opt; - }); - - var selectedOption = options.filter(function (o) { - return o.selected; - }); - - if (!selectedOption.length) { - return null; - } - - selectedOption = selectedOption[0]; - var text = selectedOption.name; - - if (selectedOption.autoText) { - if (state.PlayState && state.PlayState.PlayMethod !== 'Transcode') { - text += ' - Direct'; - } else { - text += ' ' + selectedOption.autoText; - } - } - - return text; - } - - function showAspectRatioMenu(player, btn) { - // each has a name and id - var currentId = playbackManager.getAspectRatio(player); - var menuItems = playbackManager.getSupportedAspectRatios(player).map(function (i) { - return { - id: i.id, - name: i.name, - selected: i.id === currentId - }; - }); - - return actionsheet.show({ - items: menuItems, - positionTo: btn - }).then(function (id) { - if (id) { - playbackManager.setAspectRatio(id, player); - return Promise.resolve(); - } - - return Promise.reject(); - }); - } - - function showWithUser(options, player, user) { - var supportedCommands = playbackManager.getSupportedCommands(player); - var mediaType = options.mediaType; - - var menuItems = []; - if (supportedCommands.indexOf('SetAspectRatio') !== -1) { - var currentAspectRatioId = playbackManager.getAspectRatio(player); - var currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function (i) { - return i.id === currentAspectRatioId; - })[0]; - - menuItems.push({ - name: globalize.translate('AspectRatio'), - id: 'aspectratio', - asideText: currentAspectRatio ? currentAspectRatio.name : null - }); - } - - if (user && user.Policy.EnableVideoPlaybackTranscoding) { - var secondaryQualityText = getQualitySecondaryText(player); - - menuItems.push({ - name: globalize.translate('Quality'), - id: 'quality', - asideText: secondaryQualityText - }); - } - - var repeatMode = playbackManager.getRepeatMode(player); - - if (supportedCommands.indexOf('SetRepeatMode') !== -1 && playbackManager.currentMediaSource(player).RunTimeTicks) { - menuItems.push({ - name: globalize.translate('RepeatMode'), - id: 'repeatmode', - asideText: repeatMode === 'RepeatNone' ? globalize.translate('None') : globalize.translate('' + repeatMode) - }); - } - - if (options.suboffset) { - menuItems.push({ - name: globalize.translate('SubtitleOffset'), - id: 'suboffset', - asideText: null - }); - } - - if (options.stats) { - menuItems.push({ - name: globalize.translate('PlaybackData'), - id: 'stats', - asideText: null - }); - } - - return actionsheet.show({ - items: menuItems, - positionTo: options.positionTo - }).then(function (id) { - return handleSelectedOption(id, options, player); - }); - } - - function show(options) { - var player = options.player; - var currentItem = playbackManager.currentItem(player); - - if (!currentItem || !currentItem.ServerId) { - return showWithUser(options, player, null); - } - - var apiClient = connectionManager.getApiClient(currentItem.ServerId); - return apiClient.getCurrentUser().then(function (user) { - return showWithUser(options, player, user); - }); - } - - function handleSelectedOption(id, options, player) { - switch (id) { - case 'quality': - return showQualityMenu(player, options.positionTo); - case 'aspectratio': - return showAspectRatioMenu(player, options.positionTo); - case 'repeatmode': - return showRepeatModeMenu(player, options.positionTo); - case 'stats': - if (options.onOption) { - options.onOption('stats'); - } - return Promise.resolve(); - case 'suboffset': - if (options.onOption) { - options.onOption('suboffset'); - } - return Promise.resolve(); - default: - break; + return actionsheet.show({ + items: menuItems, + positionTo: btn + }).then(function (id) { + if (id) { + playbackManager.setAspectRatio(id, player); + return Promise.resolve(); } return Promise.reject(); + }); +} + +function showWithUser(options, player, user) { + var supportedCommands = playbackManager.getSupportedCommands(player); + var mediaType = options.mediaType; + + var menuItems = []; + if (supportedCommands.indexOf('SetAspectRatio') !== -1) { + var currentAspectRatioId = playbackManager.getAspectRatio(player); + var currentAspectRatio = playbackManager.getSupportedAspectRatios(player).filter(function (i) { + return i.id === currentAspectRatioId; + })[0]; + + menuItems.push({ + name: globalize.translate('AspectRatio'), + id: 'aspectratio', + asideText: currentAspectRatio ? currentAspectRatio.name : null + }); } - return { - show: show - }; -}); + if (user && user.Policy.EnableVideoPlaybackTranscoding) { + var secondaryQualityText = getQualitySecondaryText(player); + + menuItems.push({ + name: globalize.translate('Quality'), + id: 'quality', + asideText: secondaryQualityText + }); + } + + var repeatMode = playbackManager.getRepeatMode(player); + + if (supportedCommands.indexOf('SetRepeatMode') !== -1 && playbackManager.currentMediaSource(player).RunTimeTicks) { + menuItems.push({ + name: globalize.translate('RepeatMode'), + id: 'repeatmode', + asideText: repeatMode === 'RepeatNone' ? globalize.translate('None') : globalize.translate('' + repeatMode) + }); + } + + if (options.suboffset) { + menuItems.push({ + name: globalize.translate('SubtitleOffset'), + id: 'suboffset', + asideText: null + }); + } + + if (options.stats) { + menuItems.push({ + name: globalize.translate('PlaybackData'), + id: 'stats', + asideText: null + }); + } + + return actionsheet.show({ + items: menuItems, + positionTo: options.positionTo + }).then(function (id) { + return handleSelectedOption(id, options, player); + }); +} + +export function show(options) { + var player = options.player; + var currentItem = playbackManager.currentItem(player); + + if (!currentItem || !currentItem.ServerId) { + return showWithUser(options, player, null); + } + + var apiClient = connectionManager.getApiClient(currentItem.ServerId); + return apiClient.getCurrentUser().then(function (user) { + return showWithUser(options, player, user); + }); +} + +function handleSelectedOption(id, options, player) { + switch (id) { + case 'quality': + return showQualityMenu(player, options.positionTo); + case 'aspectratio': + return showAspectRatioMenu(player, options.positionTo); + case 'repeatmode': + return showRepeatModeMenu(player, options.positionTo); + case 'stats': + if (options.onOption) { + options.onOption('stats'); + } + return Promise.resolve(); + case 'suboffset': + if (options.onOption) { + options.onOption('suboffset'); + } + return Promise.resolve(); + default: + break; + } + + return Promise.reject(); +} + +export default { + show: show +}; diff --git a/src/components/playback/playmethodhelper.js b/src/components/playback/playmethodhelper.js index 75af04035c..62793b9a33 100644 --- a/src/components/playback/playmethodhelper.js +++ b/src/components/playback/playmethodhelper.js @@ -1,24 +1,20 @@ -define([], function () { - 'use strict'; +export function getDisplayPlayMethod(session) { - function getDisplayPlayMethod(session) { - - if (!session.NowPlayingItem) { - return null; - } - - if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) { - return 'DirectStream'; - } else if (session.PlayState.PlayMethod === 'Transcode') { - return 'Transcode'; - } else if (session.PlayState.PlayMethod === 'DirectStream') { - return 'DirectPlay'; - } else if (session.PlayState.PlayMethod === 'DirectPlay') { - return 'DirectPlay'; - } + if (!session.NowPlayingItem) { + return null; } - return { - getDisplayPlayMethod: getDisplayPlayMethod - }; -}); + if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) { + return 'DirectStream'; + } else if (session.PlayState.PlayMethod === 'Transcode') { + return 'Transcode'; + } else if (session.PlayState.PlayMethod === 'DirectStream') { + return 'DirectPlay'; + } else if (session.PlayState.PlayMethod === 'DirectPlay') { + return 'DirectPlay'; + } +} + +export default { + getDisplayPlayMethod: getDisplayPlayMethod +}; diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index 429c4027f1..565cb6993e 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -6,7 +6,7 @@ define([], function () { if (!item.PlaylistItemId) { - item.PlaylistItemId = "playlistItem" + currentId; + item.PlaylistItemId = 'playlistItem' + currentId; currentId++; } } diff --git a/src/components/playback/remotecontrolautoplay.js b/src/components/playback/remotecontrolautoplay.js index 90a872cc6e..c0adb57a45 100644 --- a/src/components/playback/remotecontrolautoplay.js +++ b/src/components/playback/remotecontrolautoplay.js @@ -1,47 +1,45 @@ -define(['events', 'playbackManager'], function (events, playbackManager) { - 'use strict'; +import events from 'events'; +import playbackManager from 'playbackManager'; - function transferPlayback(oldPlayer, newPlayer) { +function transferPlayback(oldPlayer, newPlayer) { + const state = playbackManager.getPlayerState(oldPlayer); + const item = state.NowPlayingItem; - var state = playbackManager.getPlayerState(oldPlayer); - - var item = state.NowPlayingItem; - - if (!item) { - return; - } - - var playState = state.PlayState || {}; - var resumePositionTicks = playState.PositionTicks || 0; - - playbackManager.stop(oldPlayer).then(function () { - - playbackManager.play({ - ids: [item.Id], - serverId: item.ServerId, - startPositionTicks: resumePositionTicks - - }, newPlayer); - }); + if (!item) { + return; } - events.on(playbackManager, 'playerchange', function (e, newPlayer, newTarget, oldPlayer) { + playbackManager.getPlaylist(oldPlayer).then(playlist => { + const playlistIds = playlist.map(x => x.Id); + const playState = state.PlayState || {}; + const resumePositionTicks = playState.PositionTicks || 0; + const playlistIndex = playlistIds.indexOf(item.Id) || 0; - if (!oldPlayer || !newPlayer) { - return; - } - - if (!oldPlayer.isLocalPlayer) { - console.debug('Skipping remote control autoplay because oldPlayer is not a local player'); - return; - } - - if (newPlayer.isLocalPlayer) { - console.debug('Skipping remote control autoplay because newPlayer is a local player'); - return; - } - - transferPlayback(oldPlayer, newPlayer); + playbackManager.stop(oldPlayer).then(() => { + playbackManager.play({ + ids: playlistIds, + serverId: item.ServerId, + startPositionTicks: resumePositionTicks, + startIndex: playlistIndex + }, newPlayer); + }); }); +} +events.on(playbackManager, 'playerchange', (e, newPlayer, newTarget, oldPlayer) => { + if (!oldPlayer || !newPlayer) { + return; + } + + if (!oldPlayer.isLocalPlayer) { + console.debug('Skipping remote control autoplay because oldPlayer is not a local player'); + return; + } + + if (newPlayer.isLocalPlayer) { + console.debug('Skipping remote control autoplay because newPlayer is a local player'); + return; + } + + transferPlayback(oldPlayer, newPlayer); }); diff --git a/src/components/playback/volumeosd.js b/src/components/playback/volumeosd.js index 5d2c90ddb7..c2f6761f53 100644 --- a/src/components/playback/volumeosd.js +++ b/src/components/playback/volumeosd.js @@ -1,158 +1,161 @@ -define(['events', 'playbackManager', 'dom', 'browser', 'css!./iconosd', 'material-icons'], function (events, playbackManager, dom, browser) { - 'use strict'; +import events from 'events'; +import playbackManager from 'playbackManager'; +import dom from 'dom'; +import browser from 'browser'; +import 'css!./iconosd'; +import 'material-icons'; - var currentPlayer; - var osdElement; - var iconElement; - var progressElement; +var currentPlayer; +var osdElement; +var iconElement; +var progressElement; - var enableAnimation; +var enableAnimation; - function getOsdElementHtml() { - var html = ''; +function getOsdElementHtml() { + var html = ''; - html += ''; + html += ''; - html += '
'; + html += '
'; - return html; + return html; +} + +function ensureOsdElement() { + + var elem = osdElement; + if (!elem) { + + enableAnimation = browser.supportsCssAnimation(); + + elem = document.createElement('div'); + elem.classList.add('hide'); + elem.classList.add('iconOsd'); + elem.classList.add('iconOsd-hidden'); + elem.classList.add('volumeOsd'); + elem.innerHTML = getOsdElementHtml(); + + iconElement = elem.querySelector('.material-icons'); + progressElement = elem.querySelector('.iconOsdProgressInner'); + + document.body.appendChild(elem); + osdElement = elem; } +} - function ensureOsdElement() { +function onHideComplete() { + this.classList.add('hide'); +} - var elem = osdElement; - if (!elem) { +var hideTimeout; +function showOsd() { - enableAnimation = browser.supportsCssAnimation(); + clearHideTimeout(); - elem = document.createElement('div'); - elem.classList.add('hide'); - elem.classList.add('iconOsd'); - elem.classList.add('iconOsd-hidden'); - elem.classList.add('volumeOsd'); - elem.innerHTML = getOsdElementHtml(); + var elem = osdElement; - iconElement = elem.querySelector('i'); - progressElement = elem.querySelector('.iconOsdProgressInner'); - - document.body.appendChild(elem); - osdElement = elem; - } - } - - function onHideComplete() { - this.classList.add('hide'); - } - - var hideTimeout; - function showOsd() { - - clearHideTimeout(); - - var elem = osdElement; - - dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: true - }); - - elem.classList.remove('hide'); - - // trigger reflow - void elem.offsetWidth; - - requestAnimationFrame(function () { - elem.classList.remove('iconOsd-hidden'); - - hideTimeout = setTimeout(hideOsd, 3000); - }); - } - - function clearHideTimeout() { - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - } - - function hideOsd() { - - clearHideTimeout(); - - var elem = osdElement; - if (elem) { - - if (enableAnimation) { - // trigger reflow - void elem.offsetWidth; - - requestAnimationFrame(function () { - elem.classList.add('iconOsd-hidden'); - - dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { - once: true - }); - }); - } else { - onHideComplete.call(elem); - } - } - } - - function updatePlayerVolumeState(isMuted, volume) { - - if (iconElement) { - iconElement.innerHTML = isMuted ? '' : ''; - } - if (progressElement) { - progressElement.style.width = (volume || 0) + '%'; - } - } - - function releaseCurrentPlayer() { - - var player = currentPlayer; - - if (player) { - events.off(player, 'volumechange', onVolumeChanged); - events.off(player, 'playbackstop', hideOsd); - currentPlayer = null; - } - } - - function onVolumeChanged(e) { - - var player = this; - - ensureOsdElement(); - - updatePlayerVolumeState(player.isMuted(), player.getVolume()); - - showOsd(); - } - - function bindToPlayer(player) { - - if (player === currentPlayer) { - return; - } - - releaseCurrentPlayer(); - - currentPlayer = player; - - if (!player) { - return; - } - - hideOsd(); - events.on(player, 'volumechange', onVolumeChanged); - events.on(player, 'playbackstop', hideOsd); - } - - events.on(playbackManager, 'playerchange', function () { - bindToPlayer(playbackManager.getCurrentPlayer()); + dom.removeEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true }); - bindToPlayer(playbackManager.getCurrentPlayer()); + elem.classList.remove('hide'); + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.remove('iconOsd-hidden'); + + hideTimeout = setTimeout(hideOsd, 3000); + }); +} + +function clearHideTimeout() { + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; + } +} + +function hideOsd() { + + clearHideTimeout(); + + var elem = osdElement; + if (elem) { + + if (enableAnimation) { + // trigger reflow + void elem.offsetWidth; + + requestAnimationFrame(function () { + elem.classList.add('iconOsd-hidden'); + + dom.addEventListener(elem, dom.whichTransitionEvent(), onHideComplete, { + once: true + }); + }); + } else { + onHideComplete.call(elem); + } + } +} + +function updatePlayerVolumeState(isMuted, volume) { + + if (iconElement) { + iconElement.classList.remove('volume_off', 'volume_up'); + iconElement.classList.add(isMuted ? 'volume_off' : 'volume_up'); + } + if (progressElement) { + progressElement.style.width = (volume || 0) + '%'; + } +} + +function releaseCurrentPlayer() { + + var player = currentPlayer; + + if (player) { + events.off(player, 'volumechange', onVolumeChanged); + events.off(player, 'playbackstop', hideOsd); + currentPlayer = null; + } +} + +function onVolumeChanged(e) { + + var player = this; + + ensureOsdElement(); + + updatePlayerVolumeState(player.isMuted(), player.getVolume()); + + showOsd(); +} + +function bindToPlayer(player) { + + if (player === currentPlayer) { + return; + } + + releaseCurrentPlayer(); + + currentPlayer = player; + + if (!player) { + return; + } + + hideOsd(); + events.on(player, 'volumechange', onVolumeChanged); + events.on(player, 'playbackstop', hideOsd); +} + +events.on(playbackManager, 'playerchange', function () { + bindToPlayer(playbackManager.getCurrentPlayer()); }); + +bindToPlayer(playbackManager.getCurrentPlayer()); diff --git a/src/components/playbacksettings/playbacksettings.js b/src/components/playbackSettings/playbackSettings.js similarity index 95% rename from src/components/playbacksettings/playbacksettings.js rename to src/components/playbackSettings/playbackSettings.js index d249b5b989..06d2e38d2c 100644 --- a/src/components/playbacksettings/playbacksettings.js +++ b/src/components/playbackSettings/playbackSettings.js @@ -1,5 +1,5 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'qualityoptions', 'globalize', 'loading', 'connectionManager', 'dom', 'events', 'emby-select', 'emby-checkbox'], function (require, browser, appSettings, appHost, focusManager, qualityoptions, globalize, loading, connectionManager, dom, events) { - "use strict"; + 'use strict'; function fillSkipLengths(select) { @@ -17,15 +17,15 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality function populateLanguages(select, languages) { - var html = ""; + var html = ''; - html += ""; + html += "'; for (var i = 0, length = languages.length; i < length; i++) { var culture = languages[i]; - html += ""; + html += "'; } select.innerHTML = html; @@ -154,12 +154,12 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality populateLanguages(context.querySelector('#selectAudioLanguage'), allCultures); - context.querySelector('#selectAudioLanguage', context).value = user.Configuration.AudioLanguagePreference || ""; + context.querySelector('#selectAudioLanguage', context).value = user.Configuration.AudioLanguagePreference || ''; context.querySelector('.chkEpisodeAutoPlay').checked = user.Configuration.EnableNextEpisodeAutoPlay || false; }); // hide cinema mode options if disabled at server level - apiClient.getNamedConfiguration("cinemamode").then(function (cinemaConfig) { + apiClient.getNamedConfiguration('cinemamode').then(function (cinemaConfig) { if (cinemaConfig.EnableIntrosForMovies || cinemaConfig.EnableIntrosForEpisodes) { context.querySelector('.cinemaModeOptions').classList.remove('hide'); @@ -204,6 +204,9 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality fillChromecastQuality(context.querySelector('.selectChromecastVideoQuality')); + var selectChromecastVersion = context.querySelector('.selectChromecastVersion'); + selectChromecastVersion.value = userSettings.chromecastVersion(); + var selectSkipForwardLength = context.querySelector('.selectSkipForwardLength'); fillSkipLengths(selectSkipForwardLength); selectSkipForwardLength.value = userSettings.skipForwardLength(); @@ -234,6 +237,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked); userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked); + userSettingsInstance.chromecastVersion(context.querySelector('.selectChromecastVersion').value); userSettingsInstance.skipForwardLength(context.querySelector('.selectSkipForwardLength').value); userSettingsInstance.skipBackLength(context.querySelector('.selectSkipBackLength').value); @@ -285,7 +289,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality function embed(options, self) { - require(['text!./playbacksettings.template.html'], function (template) { + require(['text!./playbackSettings.template.html'], function (template) { options.element.innerHTML = globalize.translateDocument(template, 'core'); diff --git a/src/components/playbacksettings/playbacksettings.template.html b/src/components/playbackSettings/playbackSettings.template.html similarity index 92% rename from src/components/playbacksettings/playbacksettings.template.html rename to src/components/playbackSettings/playbackSettings.template.html index 0c8b726a40..03af5d6faa 100644 --- a/src/components/playbacksettings/playbacksettings.template.html +++ b/src/components/playbackSettings/playbackSettings.template.html @@ -1,12 +1,13 @@ -

${HeaderAudioSettings}

+
+
+
+ +
+
diff --git a/src/components/playerstats/playerstats.js b/src/components/playerstats/playerstats.js index 89adf22624..a65baf3553 100644 --- a/src/components/playerstats/playerstats.js +++ b/src/components/playerstats/playerstats.js @@ -1,4 +1,4 @@ -define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMethodHelper', 'layoutManager', 'serverNotifications', 'paper-icon-button-light', 'css!./playerstats'], function (events, globalize, playbackManager, connectionManager, playMethodHelper, layoutManager, serverNotifications) { +define(['events', 'globalize', 'playbackManager', 'connectionManager', 'syncPlayManager', 'playMethodHelper', 'layoutManager', 'serverNotifications', 'paper-icon-button-light', 'css!./playerstats'], function (events, globalize, playbackManager, connectionManager, syncPlayManager, playMethodHelper, layoutManager, serverNotifications) { 'use strict'; function init(instance) { @@ -18,7 +18,7 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth if (layoutManager.tv) { button = ''; } else { - button = ''; + button = ''; } var contentClass = layoutManager.tv ? 'playerStats-content playerStats-content-tv' : 'playerStats-content'; @@ -132,7 +132,7 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth if (videoCodec) { sessionStats.push({ - label: globalize.translate("LabelVideoCodec"), + label: globalize.translate('LabelVideoCodec'), value: session.TranscodingInfo.IsVideoDirect ? (videoCodec.toUpperCase() + ' (direct)') : videoCodec.toUpperCase() }); } @@ -140,7 +140,7 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth if (audioCodec) { sessionStats.push({ - label: globalize.translate("LabelAudioCodec"), + label: globalize.translate('LabelAudioCodec'), value: session.TranscodingInfo.IsAudioDirect ? (audioCodec.toUpperCase() + ' (direct)') : audioCodec.toUpperCase() }); } @@ -157,28 +157,28 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth if (totalBitrate) { sessionStats.push({ - label: globalize.translate("LabelBitrate"), + label: globalize.translate('LabelBitrate'), value: getDisplayBitrate(totalBitrate) }); } if (session.TranscodingInfo.CompletionPercentage) { sessionStats.push({ - label: globalize.translate("LabelTranscodingProgress"), + label: globalize.translate('LabelTranscodingProgress'), value: session.TranscodingInfo.CompletionPercentage.toFixed(1) + '%' }); } if (session.TranscodingInfo.Framerate) { sessionStats.push({ - label: globalize.translate("LabelTranscodingFramerate"), + label: globalize.translate('LabelTranscodingFramerate'), value: session.TranscodingInfo.Framerate + ' fps' }); } if (session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length) { sessionStats.push({ - label: globalize.translate("LabelReasonForTranscoding"), + label: globalize.translate('LabelReasonForTranscoding'), value: session.TranscodingInfo.TranscodeReasons.map(translateReason).join('
') }); } @@ -216,14 +216,14 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth if (mediaSource.Container) { sessionStats.push({ - label: globalize.translate("LabelProfileContainer"), + label: globalize.translate('LabelProfileContainer'), value: mediaSource.Container }); } if (mediaFileSize) { sessionStats.push({ - label: globalize.translate("LabelSize"), + label: globalize.translate('LabelSize'), value: getReadableSize(mediaFileSize) }); } @@ -231,7 +231,7 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth if (totalBitrate) { sessionStats.push({ - label: globalize.translate("LabelBitrate"), + label: globalize.translate('LabelBitrate'), value: getDisplayBitrate(totalBitrate) }); } @@ -267,14 +267,14 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth if (videoInfos.length) { sessionStats.push({ - label: globalize.translate("LabelVideoCodec"), + label: globalize.translate('LabelVideoCodec'), value: videoInfos.join(' ') }); } if (videoStream.BitRate) { sessionStats.push({ - label: globalize.translate("LabelVideoBitrate"), + label: globalize.translate('LabelVideoBitrate'), value: getDisplayBitrate(videoStream.BitRate) }); } @@ -291,35 +291,35 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth if (audioInfos.length) { sessionStats.push({ - label: globalize.translate("LabelAudioCodec"), + label: globalize.translate('LabelAudioCodec'), value: audioInfos.join(' ') }); } if (audioStream.BitRate) { sessionStats.push({ - label: globalize.translate("LabelAudioBitrate"), + label: globalize.translate('LabelAudioBitrate'), value: getDisplayBitrate(audioStream.BitRate) }); } if (audioChannels) { sessionStats.push({ - label: globalize.translate("LabelAudioChannels"), + label: globalize.translate('LabelAudioChannels'), value: audioChannels }); } if (audioStream.SampleRate) { sessionStats.push({ - label: globalize.translate("LabelAudioSampleRate"), + label: globalize.translate('LabelAudioSampleRate'), value: audioStream.SampleRate + ' Hz' }); } if (audioStream.BitDepth) { sessionStats.push({ - label: globalize.translate("LabelAudioBitDepth"), + label: globalize.translate('LabelAudioBitDepth'), value: audioStream.BitDepth }); } @@ -327,6 +327,28 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth return sessionStats; } + function getSyncPlayStats() { + var syncStats = []; + var stats = syncPlayManager.getStats(); + + syncStats.push({ + label: globalize.translate('LabelSyncPlayTimeOffset'), + value: stats.TimeOffset + globalize.translate('MillisecondsUnit') + }); + + syncStats.push({ + label: globalize.translate('LabelSyncPlayPlaybackDiff'), + value: stats.PlaybackDiff + globalize.translate('MillisecondsUnit') + }); + + syncStats.push({ + label: globalize.translate('LabelSyncPlaySyncMethod'), + value: stats.SyncMethod + }); + + return syncStats; + } + function getStats(instance, player) { var statsPromise = player.getStats ? player.getStats() : Promise.resolve({}); @@ -346,12 +368,12 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth }; baseCategory.stats.unshift({ - label: globalize.translate("LabelPlayMethod"), + label: globalize.translate('LabelPlayMethod'), value: displayPlayMethod }); baseCategory.stats.unshift({ - label: globalize.translate("LabelPlayer"), + label: globalize.translate('LabelPlayer'), value: player.name }); @@ -383,6 +405,13 @@ define(['events', 'globalize', 'playbackManager', 'connectionManager', 'playMeth name: 'Original Media Info' }); + if (syncPlayManager.isSyncPlayEnabled()) { + categories.push({ + stats: getSyncPlayStats(), + name: 'SyncPlay Info' + }); + } + return Promise.resolve(categories); }); } diff --git a/src/components/playlisteditor/playlisteditor.js b/src/components/playlisteditor/playlisteditor.js index 69356a38c9..56d7142ad3 100644 --- a/src/components/playlisteditor/playlisteditor.js +++ b/src/components/playlisteditor/playlisteditor.js @@ -23,7 +23,7 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan function createPlaylist(apiClient, dlg) { loading.show(); - var url = apiClient.getUrl("Playlists", { + var url = apiClient.getUrl('Playlists', { Name: dlg.querySelector('#txtNewPlaylistName').value, Ids: dlg.querySelector('.fldSelectedItemIds').value || '', userId: apiClient.getCurrentUserId() @@ -31,9 +31,9 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan }); apiClient.ajax({ - type: "POST", + type: 'POST', url: url, - dataType: "json" + dataType: 'json' }).then(function (result) { loading.hide(); @@ -63,13 +63,13 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan loading.show(); - var url = apiClient.getUrl("Playlists/" + id + "/Items", { + var url = apiClient.getUrl('Playlists/' + id + '/Items', { Ids: itemIds, userId: apiClient.getCurrentUserId() }); apiClient.ajax({ - type: "POST", + type: 'POST', url: url }).then(function () { @@ -93,7 +93,7 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan var options = { Recursive: true, - IncludeItemTypes: "Playlist", + IncludeItemTypes: 'Playlist', SortBy: 'SortName', EnableTotalRecordCount: false }; @@ -228,7 +228,7 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackMan var title = globalize.translate('HeaderAddToPlaylist'); html += '
'; - html += ''; + html += ''; html += '

'; html += title; html += '

'; diff --git a/src/components/playmenu.js b/src/components/playmenu.js index a410da6985..50c2a7b31f 100644 --- a/src/components/playmenu.js +++ b/src/components/playmenu.js @@ -1,75 +1,71 @@ -define(['actionsheet', 'datetime', 'playbackManager', 'globalize', 'appSettings'], function (actionsheet, datetime, playbackManager, globalize, appSettings) { - 'use strict'; +import actionsheet from 'actionsheet'; +import datetime from 'datetime'; +import playbackManager from 'playbackManager'; +import globalize from 'globalize'; - function show(options) { +export function show(options) { - var item = options.item; + var item = options.item; - var itemType = item.Type; - var isFolder = item.IsFolder; - var itemId = item.Id; - var channelId = item.ChannelId; - var serverId = item.ServerId; - var resumePositionTicks = item.UserData ? item.UserData.PlaybackPositionTicks : null; + var resumePositionTicks = item.UserData ? item.UserData.PlaybackPositionTicks : null; - var playableItemId = itemType === 'Program' ? channelId : itemId; + var playableItemId = item.Type === 'Program' ? item.ChannelId : item.Id; - if (!resumePositionTicks || isFolder) { - playbackManager.play({ - ids: [playableItemId], - serverId: serverId - }); - return; - } - - var menuItems = []; - - menuItems.push({ - name: globalize.translate('ResumeAt', datetime.getDisplayRunningTime(resumePositionTicks)), - id: 'resume' - }); - - menuItems.push({ - name: globalize.translate('PlayFromBeginning'), - id: 'play' - }); - - actionsheet.show({ - - items: menuItems, - positionTo: options.positionTo - - }).then(function (id) { - switch (id) { - - case 'play': - playbackManager.play({ - ids: [playableItemId], - serverId: serverId - }); - break; - case 'resume': - playbackManager.play({ - ids: [playableItemId], - startPositionTicks: resumePositionTicks, - serverId: serverId - }); - break; - case 'queue': - playbackManager.queue({ - items: [item] - }); - break; - case 'shuffle': - playbackManager.shuffle(item); - break; - default: - break; - } + if (!resumePositionTicks || item.IsFolder) { + playbackManager.play({ + ids: [playableItemId], + serverId: item.ServerId }); + return; } - return { - show: show - }; -}); + var menuItems = []; + + menuItems.push({ + name: globalize.translate('ResumeAt', datetime.getDisplayRunningTime(resumePositionTicks)), + id: 'resume' + }); + + menuItems.push({ + name: globalize.translate('PlayFromBeginning'), + id: 'play' + }); + + actionsheet.show({ + + items: menuItems, + positionTo: options.positionTo + + }).then(function (id) { + switch (id) { + + case 'play': + playbackManager.play({ + ids: [playableItemId], + serverId: item.ServerId + }); + break; + case 'resume': + playbackManager.play({ + ids: [playableItemId], + startPositionTicks: resumePositionTicks, + serverId: item.ServerId + }); + break; + case 'queue': + playbackManager.queue({ + items: [item] + }); + break; + case 'shuffle': + playbackManager.shuffle(item); + break; + default: + break; + } + }); +} + +export default { + show: show +}; diff --git a/src/components/pluginManager.js b/src/components/pluginManager.js index 2126d73b3c..fd35d344bf 100644 --- a/src/components/pluginManager.js +++ b/src/components/pluginManager.js @@ -1,10 +1,10 @@ -define(['events'], function (events) { +define(['events', 'globalize'], function (events, globalize) { 'use strict'; // TODO: replace with each plugin version var cacheParam = new Date().getTime(); - function loadStrings(plugin, globalize) { + function loadStrings(plugin) { var strings = plugin.getTranslations ? plugin.getTranslations() : []; return globalize.loadStrings({ name: plugin.id || plugin.packageName, @@ -25,68 +25,78 @@ define(['events'], function (events) { this.pluginsList = []; } - PluginManager.prototype.loadPlugin = function (url) { + PluginManager.prototype.loadPlugin = function(pluginSpec) { - console.debug('Loading plugin: ' + url); var instance = this; - return new Promise(function (resolve, reject) { + function registerPlugin(plugin) { + instance.register(plugin); - require([url, 'globalize', 'appRouter'], function (pluginFactory, globalize, appRouter) { - - var plugin = new pluginFactory(); - - // See if it's already installed - var existing = instance.pluginsList.filter(function (p) { - return p.id === plugin.id; - })[0]; - - if (existing) { - resolve(url); - return; - } - - plugin.installUrl = url; - - var urlLower = url.toLowerCase(); - if (urlLower.indexOf('http:') === -1 && urlLower.indexOf('https:') === -1 && urlLower.indexOf('file:') === -1) { - if (url.indexOf(appRouter.baseUrl()) !== 0) { - - url = appRouter.baseUrl() + '/' + url; - } - } - - var separatorIndex = Math.max(url.lastIndexOf('/'), url.lastIndexOf('\\')); - plugin.baseUrl = url.substring(0, separatorIndex); - - var paths = {}; - paths[plugin.id] = plugin.baseUrl; - - requirejs.config({ - waitSeconds: 0, - paths: paths + if (plugin.getRoutes) { + plugin.getRoutes().forEach(function (route) { + definePluginRoute(instance, route, plugin); }); + } - instance.register(plugin); + if (plugin.type === 'skin') { - if (plugin.getRoutes) { - plugin.getRoutes().forEach(function (route) { - definePluginRoute(instance, route, plugin); + // translations won't be loaded for skins until needed + return Promise.resolve(plugin); + } else { + return new Promise((resolve, reject) => { + loadStrings(plugin) + .then(function () { + resolve(plugin); + }) + .catch(reject); + }); + } + } + + if (typeof pluginSpec === 'string') { + console.debug('Loading plugin (via deprecated requirejs method): ' + pluginSpec); + + return new Promise(function (resolve, reject) { + require([pluginSpec], (pluginFactory) => { + var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory(); + + // See if it's already installed + var existing = instance.pluginsList.filter(function (p) { + return p.id === plugin.id; + })[0]; + + if (existing) { + resolve(pluginSpec); + } + + plugin.installUrl = pluginSpec; + + var separatorIndex = Math.max(pluginSpec.lastIndexOf('/'), pluginSpec.lastIndexOf('\\')); + plugin.baseUrl = pluginSpec.substring(0, separatorIndex); + + var paths = {}; + paths[plugin.id] = plugin.baseUrl; + + requirejs.config({ + waitSeconds: 0, + paths: paths }); - } - if (plugin.type === 'skin') { - - // translations won't be loaded for skins until needed - resolve(plugin); - } else { - - loadStrings(plugin, globalize).then(function () { - resolve(plugin); - }, reject); - } + registerPlugin(plugin).then(resolve).catch(reject); + }); }); - }); + } else if (pluginSpec.then) { + return pluginSpec.then(pluginBuilder => { + return pluginBuilder(); + }).then(plugin => { + console.debug(`Plugin loaded: ${plugin.id}`); + return registerPlugin(plugin); + }); + } else { + const err = new Error('Plugins have to be a Promise that resolves to a plugin builder function or a requirejs urls (deprecated)'); + console.error(err); + return Promise.reject(err); + } }; // In lieu of automatic discovery, plugins will register dynamic objects diff --git a/src/components/polyfills/objectassign.js b/src/components/polyfills/objectassign.js deleted file mode 100644 index 85f55aa144..0000000000 --- a/src/components/polyfills/objectassign.js +++ /dev/null @@ -1,24 +0,0 @@ -if (typeof Object.assign != 'function') { - (function () { - Object.assign = function (target) { - 'use strict'; - if (target === undefined || target === null) { - throw new TypeError('Cannot convert undefined or null to object'); - } - - var output = Object(target); - for (var index = 1; index < arguments.length; index++) { - var source = arguments[index]; - if (source !== undefined && source !== null) { - for (var nextKey in source) { - // eslint-disable-next-line no-prototype-builtins - if (source.hasOwnProperty(nextKey)) { - output[nextKey] = source[nextKey]; - } - } - } - } - return output; - }; - })(); -} diff --git a/src/components/prompt/prompt.js b/src/components/prompt/prompt.js index 41d40c4a48..a76083cf04 100644 --- a/src/components/prompt/prompt.js +++ b/src/components/prompt/prompt.js @@ -1,4 +1,4 @@ -define(["browser", 'dialogHelper', 'layoutManager', 'scrollHelper', 'globalize', 'dom', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle'], function(browser, dialogHelper, layoutManager, scrollHelper, globalize, dom, require) { +define(['browser', 'dialogHelper', 'layoutManager', 'scrollHelper', 'globalize', 'dom', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle'], function(browser, dialogHelper, layoutManager, scrollHelper, globalize, dom, require) { 'use strict'; function replaceAll(str, find, replace) { diff --git a/src/components/prompt/prompt.template.html b/src/components/prompt/prompt.template.html index 23b918a1f2..981fa9f102 100644 --- a/src/components/prompt/prompt.template.html +++ b/src/components/prompt/prompt.template.html @@ -1,6 +1,6 @@

diff --git a/src/components/qualityoptions.js b/src/components/qualityOptions.js similarity index 96% rename from src/components/qualityoptions.js rename to src/components/qualityOptions.js index 5ad69cb790..221e13d4ef 100644 --- a/src/components/qualityoptions.js +++ b/src/components/qualityOptions.js @@ -5,6 +5,13 @@ define(['globalize'], function (globalize) { var maxStreamingBitrate = options.currentMaxBitrate; var videoWidth = options.videoWidth; + var videoHeight = options.videoHeight; + + // If the aspect ratio is less than 16/9 (1.77), set the width as if it were pillarboxed. + // 4:3 1440x1080 -> 1920x1080 + if (videoWidth / videoHeight < 16 / 9) { + videoWidth = videoHeight * (16 / 9); + } var maxAllowedWidth = videoWidth || 4096; //var maxAllowedHeight = videoHeight || 2304; diff --git a/src/components/recordingcreator/recordingbutton.js b/src/components/recordingcreator/recordingbutton.js index e9c174bfd6..c4bfc43010 100644 --- a/src/components/recordingcreator/recordingbutton.js +++ b/src/components/recordingcreator/recordingbutton.js @@ -22,7 +22,7 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom' } function setButtonIcon(button, icon) { - var inner = button.querySelector('i'); + var inner = button.querySelector('.material-icons'); inner.classList.remove('fiber_smart_record'); inner.classList.remove('fiber_manual_record'); inner.classList.add(icon); diff --git a/src/components/recordingcreator/recordingcreator.js b/src/components/recordingcreator/recordingcreator.js index a51fac4305..ae26d0debc 100644 --- a/src/components/recordingcreator/recordingcreator.js +++ b/src/components/recordingcreator/recordingcreator.js @@ -36,14 +36,14 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c if (imageTags.Primary) { return apiClient.getScaledImageUrl(item.Id, { - type: "Primary", + type: 'Primary', maxHeight: imageHeight, tag: item.ImageTags.Primary }); } else if (imageTags.Thumb) { return apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", + type: 'Thumb', maxHeight: imageHeight, tag: item.ImageTags.Thumb }); diff --git a/src/components/recordingcreator/recordingcreator.template.html b/src/components/recordingcreator/recordingcreator.template.html index b99ead300a..8d7d75c8f6 100644 --- a/src/components/recordingcreator/recordingcreator.template.html +++ b/src/components/recordingcreator/recordingcreator.template.html @@ -1,5 +1,5 @@
- +

diff --git a/src/components/recordingcreator/recordingeditor.template.html b/src/components/recordingcreator/recordingeditor.template.html index 48256da0a2..f7a9f98ee6 100644 --- a/src/components/recordingcreator/recordingeditor.template.html +++ b/src/components/recordingcreator/recordingeditor.template.html @@ -1,5 +1,5 @@
- +

${HeaderRecordingOptions}

diff --git a/src/components/recordingcreator/recordingfields.js b/src/components/recordingcreator/recordingfields.js index 106fec36bf..b4cbdfab0a 100644 --- a/src/components/recordingcreator/recordingfields.js +++ b/src/components/recordingcreator/recordingfields.js @@ -151,7 +151,7 @@ define(['globalize', 'connectionManager', 'serverNotifications', 'require', 'loa var apiClient = connectionManager.getApiClient(options.serverId); var button = dom.parentWithTag(e.target, 'BUTTON'); - var isChecked = !button.querySelector('i').classList.contains('recordingIcon-active'); + var isChecked = !button.querySelector('.material-icons').classList.contains('recordingIcon-active'); var hasEnabledTimer = this.TimerId && this.Status !== 'Cancelled'; @@ -191,7 +191,7 @@ define(['globalize', 'connectionManager', 'serverNotifications', 'require', 'loa var apiClient = connectionManager.getApiClient(options.serverId); var button = dom.parentWithTag(e.target, 'BUTTON'); - var isChecked = !button.querySelector('i').classList.contains('recordingIcon-active'); + var isChecked = !button.querySelector('.material-icons').classList.contains('recordingIcon-active'); if (isChecked) { options.parent.querySelector('.recordSeriesContainer').classList.remove('hide'); diff --git a/src/components/recordingcreator/recordingfields.template.html b/src/components/recordingcreator/recordingfields.template.html index ce35960716..25a0c3c30d 100644 --- a/src/components/recordingcreator/recordingfields.template.html +++ b/src/components/recordingcreator/recordingfields.template.html @@ -2,7 +2,7 @@
@@ -14,7 +14,7 @@
diff --git a/src/components/recordingcreator/seriesrecordingeditor.template.html b/src/components/recordingcreator/seriesrecordingeditor.template.html index fa9f16561b..582c1a633c 100644 --- a/src/components/recordingcreator/seriesrecordingeditor.template.html +++ b/src/components/recordingcreator/seriesrecordingeditor.template.html @@ -1,5 +1,5 @@
- +

${HeaderSeriesOptions}

diff --git a/src/components/refreshdialog/refreshdialog.js b/src/components/refreshdialog/refreshdialog.js index 1e54d98372..3edb725c4a 100644 --- a/src/components/refreshdialog/refreshdialog.js +++ b/src/components/refreshdialog/refreshdialog.js @@ -110,7 +110,7 @@ define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'connectionM var title = globalize.translate('RefreshMetadata'); html += '
'; - html += ''; + html += ''; html += '

'; html += title; html += '

'; diff --git a/src/components/remotecontrol/remotecontrol.css b/src/components/remotecontrol/remotecontrol.css index 83a1c48e5f..073c925339 100644 --- a/src/components/remotecontrol/remotecontrol.css +++ b/src/components/remotecontrol/remotecontrol.css @@ -1,3 +1,7 @@ +.nowPlayingPage { + padding: 5em 0 0 0 !important; +} + .nowPlayingInfoContainer { display: -webkit-box; display: -webkit-flex; @@ -36,8 +40,30 @@ margin: 0 0 0.5em 0.5em; } +.nowPlayingAlbum a, +.nowPlayingArtist a { + font-weight: normal; + text-align: left !important; + color: inherit !important; +} + +.nowPlayingButtonsContainer { + display: flex; +} + +.nowPlayingInfoContainerMedia { + text-align: left; + margin-bottom: 1em; +} + +.nowPlayingPositionSlider { + width: stretch; +} + .nowPlayingPositionSliderContainer { - margin: 0.7em 0 0.7em 1em; + margin: 0.2em 1em 0.2em 1em; + width: 100%; + z-index: 0; } .nowPlayingInfoButtons { @@ -59,17 +85,32 @@ } .nowPlayingPageImageContainer { - width: 20%; - margin-right: 0.25em; + width: 16%; + margin-right: 1em; position: relative; -webkit-flex-shrink: 0; flex-shrink: 0; } -@media all and (min-width: 50em) { - .nowPlayingPageImageContainer { - width: 16%; - } +.nowPlayingPageImageContainerNoAlbum { + width: 100%; + position: relative; +} + +.nowPlayingPageImageContainerNoAlbum button { + cursor: default; +} + +.nowPlayingPageImageContainerNoAlbum::after { + content: ""; + display: block; + padding-bottom: 100%; +} + +.btnPlayPause { + font-size: xx-large; + padding: 0; + margin: 0; } .nowPlayingInfoControls { @@ -87,14 +128,15 @@ } .nowPlayingPageImage { + display: block; bottom: 0; left: 0; right: 0; + margin: 0 auto; width: 100%; -webkit-box-shadow: 0 0 1.9vh #000; box-shadow: 0 0 1.9vh #000; border: 0.1em solid #222; - user-drag: none; user-select: none; -moz-user-select: none; -webkit-user-drag: none; @@ -102,60 +144,16 @@ -ms-user-select: none; } -@media all and (orientation: portrait) and (max-width: 50em) { - .nowPlayingInfoContainer { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -webkit-flex-direction: column !important; - flex-direction: column !important; - -webkit-box-align: center; - -webkit-align-items: center; - align-items: center; - } - - .nowPlayingPageTitle { - text-align: center; - margin: 0.5em 0 0.75em; - } - - .nowPlayingPositionSliderContainer { - margin: 0.7em 1em; - } - - .nowPlayingInfoButtons { - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center; - } - - .nowPlayingPageImageContainer { - width: auto; - margin-right: 0; - } - - .nowPlayingInfoControls { - margin-top: 1em; - max-width: 100%; - } - - .nowPlayingPageImage { - width: auto; - height: 36vh; - } +.contextMenuList { + padding: 1.5em 0; } -@media all and (orientation: portrait) and (max-width: 40em) { - .nowPlayingPageImage { - height: 30vh; - } +.contextMenuList a { + color: inherit !important; } -.nowPlayingTime { - display: flex; - -webkit-box-align: center; - -webkit-align-items: center; - align-items: center; - margin: 0 1em; +.contextMenuList .material-icons.listItemIcon { + font-size: x-large; } .nowPlayingSecondaryButtons { @@ -167,12 +165,17 @@ align-items: center; -webkit-flex-wrap: wrap; flex-wrap: wrap; - -webkit-box-pack: center; - -webkit-justify-content: center; - justify-content: center; + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + z-index: 0; } -@media all and (min-width: 50em) { +@media all and (min-width: 63em) { + .nowPlayingPage { + padding: 8em 0 0 0 !important; + } + .nowPlayingSecondaryButtons { -webkit-box-flex: 1; -webkit-flex-grow: 1; @@ -181,6 +184,16 @@ -webkit-justify-content: flex-end; justify-content: flex-end; } + + .nowPlayingPageUserDataButtonsTitle { + display: none !important; + } + + .playlistSectionButton, + .nowPlayingPlaylist, + .nowPlayingContextMenu { + background: unset !important; + } } @media all and (min-width: 80em) { @@ -189,6 +202,414 @@ } } +@media all and (orientation: portrait) and (max-width: 47em) { + .remoteControlContent { + padding-left: 7.3% !important; + padding-right: 7.3% !important; + display: flex; + height: 100%; + flex-direction: column; + } + + .nowPlayingInfoContainer { + -webkit-box-orient: vertical !important; + -webkit-box-direction: normal !important; + -webkit-flex-direction: column !important; + flex-direction: column !important; + -webkit-box-align: center; + -webkit-align-items: center; + align-items: center; + width: 100%; + height: calc(100% - 4.2em); + } + + .nowPlayingPageTitle { + /* text-align: center; */ + margin: 0; + } + + .nowPlayingAlbum, + .nowPlayingArtist { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .nowPlayingInfoContainerMedia { + text-align: left !important; + width: 80%; + } + + .nowPlayingPositionSliderContainer { + margin: 0.2em 1em 0.2em 1em; + } + + .nowPlayingInfoButtons { + /* margin: 1.5em 0 0 0; */ + -webkit-box-pack: center; + -webkit-justify-content: center; + justify-content: center; + font-size: x-large; + height: 100%; + } + + .nowPlayingPageImageContainer { + width: 100%; + margin: auto auto 0.5em; + } + + .nowPlayingPageImageContainerNoAlbum .cardImageContainer .cardImageIcon { + font-size: 15em; + color: inherit; + } + + .nowPlayingInfoControls { + margin: 0.5em 0 1em 0; + width: 100%; + -webkit-box-pack: start !important; + -webkit-justify-content: start !important; + justify-content: start !important; + } + + .nowPlayingSecondaryButtons { + -webkit-box-pack: center; + -webkit-justify-content: center; + justify-content: center; + } + + .nowPlayingInfoControls .nowPlayingPageUserDataButtonsTitle { + width: 20%; + font-size: large; + } + + .nowPlayingInfoControls .nowPlayingPageUserDataButtonsTitle button { + padding-top: 0; + padding-right: 0; + margin-right: 0; + float: right; + border-radius: 0; + } + + .nowPlayingInfoButtons .btnRewind { + position: absolute; + left: 0; + margin-left: 0; + padding-left: 7.3%; + font-size: smaller; + } + + .nowPlayingInfoButtons .btnFastForward { + position: absolute; + right: 0; + margin-right: 0; + padding-right: 7.3%; + font-size: smaller; + } + + .paper-icon-button-light:hover { + color: #fff !important; + background-color: transparent !important; + } + + .btnPlayPause { + padding: 0; + margin: 0; + font-size: 1.7em; + } + + .btnPlayPause:hover { + background-color: transparent !important; + } + + .nowPlayingPageImage { + /* width: inherit; */ + overflow-y: hidden; + overflow: hidden; + margin: 0 auto; + } + + .nowPlayingPageImage.nowPlayingPageImageAudio { + width: 100%; + } + + .nowPlayingPageImageContainer.nowPlayingPageImagePoster { + height: 50%; + overflow: hidden; + } + + .nowPlayingPageImageContainer.nowPlayingPageImagePoster img { + height: 100%; + width: auto; + } + + #nowPlayingPage .playlistSection .playlist, + #nowPlayingPage .playlistSection .contextMenu { + position: absolute; + top: 12.2em; + bottom: 4.2em; + overflow: scroll; + padding: 0 1em; + display: inline-block; + left: 0; + right: 0; + z-index: 1000; + } + + .playlistSectionButton { + position: fixed; + bottom: 0; + left: 0; + height: 4.2em; + right: 0; + padding-left: 7.3%; + padding-right: 7.3%; + } + + .playlistSectionButton .btnTogglePlaylist { + font-size: larger; + margin: 0; + padding-left: 0; + } + + .playlistSectionButton .btnSavePlaylist { + margin: 0; + padding-right: 0; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + flex-grow: 1; + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + border-radius: 0; + } + + .playlistSectionButton .btnToggleContextMenu { + font-size: larger; + margin: 0; + padding-right: 0; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + flex-grow: 1; + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + border-radius: 0; + } + + .playlistSectionButton .volumecontrol { + width: 100%; + } + + .remoteControlSection { + margin: 0; + padding: 0 0 4.2em 0; + } + + .nowPlayingButtonsContainer { + display: flex; + height: 100%; + flex-direction: column; + } +} + +@media all and (orientation: landscape) and (max-width: 63em) { + .remoteControlContent { + padding-left: 4.3% !important; + padding-right: 4.3% !important; + display: flex; + height: 100%; + flex-direction: column; + } + + .nowPlayingInfoContainer { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: normal !important; + -webkit-flex-direction: row !important; + flex-direction: row !important; + -webkit-box-align: center; + -webkit-align-items: center; + align-items: center; + width: 100%; + height: calc(100% - 4.2em); + } + + .nowPlayingPageTitle { + /* text-align: center; */ + margin: 0; + } + + .nowPlayingInfoContainerMedia { + text-align: left !important; + width: 80%; + } + + .nowPlayingPositionSliderContainer { + margin: 0.2em 1em 0.2em 1em; + } + + .nowPlayingInfoButtons { + /* margin: 1.5em 0 0 0; */ + -webkit-box-pack: center; + -webkit-justify-content: center; + justify-content: center; + font-size: x-large; + height: 100%; + } + + .nowPlayingPageImageContainer { + width: 30%; + margin: auto 1em auto auto; + } + + .nowPlayingPageImageContainerNoAlbum .cardImageContainer .cardImageIcon { + font-size: 12em; + color: inherit; + } + + .nowPlayingInfoControls { + margin: 0.5em 0 1em 0; + width: 100%; + -webkit-box-pack: start !important; + -webkit-justify-content: start !important; + justify-content: start !important; + } + + .nowPlayingSecondaryButtons { + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + flex-grow: 1; + -webkit-box-pack: center; + -webkit-justify-content: center; + justify-content: center; + } + + .nowPlayingInfoControls .nowPlayingPageUserDataButtonsTitle { + width: 20%; + font-size: large; + } + + .nowPlayingInfoControls .nowPlayingPageUserDataButtonsTitle button { + padding-top: 0; + padding-right: 0; + margin-right: 0; + float: right; + border-radius: 0; + } + + .paper-icon-button-light:hover { + color: #fff !important; + background-color: transparent !important; + } + + .btnPlayPause { + padding: 0; + margin: 0; + font-size: 1.7em; + } + + .btnPlayPause:hover { + background-color: transparent !important; + } + + .nowPlayingPageImage { + /* width: inherit; */ + overflow-y: hidden; + overflow: hidden; + margin: 0 auto; + } + + .nowPlayingPageImage.nowPlayingPageImageAudio { + width: 100%; + } + + .nowPlayingPageImageContainer.nowPlayingPageImagePoster { + height: 100%; + overflow: hidden; + } + + .nowPlayingPageImageContainer.nowPlayingPageImagePoster img { + height: 100%; + width: auto; + } + + #nowPlayingPage .playlistSection .playlist, + #nowPlayingPage .playlistSection .contextMenu { + position: absolute; + top: 7.2em; + bottom: 4.2em; + overflow: scroll; + padding: 0 1em; + display: inline-block; + left: 0; + right: 0; + z-index: 1000; + } + + .playlistSectionButton { + position: fixed; + bottom: 0; + left: 0; + height: 4.2em; + right: 0; + padding-left: 4.3%; + padding-right: 4.3%; + } + + .playlistSectionButton .btnTogglePlaylist { + font-size: larger; + margin: 0; + padding-left: 0; + } + + .playlistSectionButton .btnSavePlaylist { + margin: 0; + padding-right: 0; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + flex-grow: 1; + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + border-radius: 0; + } + + .playlistSectionButton .btnToggleContextMenu { + font-size: larger; + margin: 0; + padding-right: 0; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + flex-grow: 1; + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + justify-content: flex-end; + border-radius: 0; + } + + .playlistSectionButton .volumecontrol { + width: 100%; + } + + .remoteControlSection { + margin: 4.2em 0 0 0; + padding: 0 0 4.2em 0; + } + + .nowPlayingButtonsContainer { + display: flex; + height: 100%; + flex-direction: column; + } +} + +.nowPlayingTime { + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + align-items: center; + margin: 0 1em; +} + .nowPlayingNavButtonContainer { width: 30em; } @@ -214,26 +635,15 @@ width: 9em; } -@media all and (max-width: 50em) { - .nowPlayingInfoButtons .nowPlayingPageUserDataButtons { +@media all and (max-width: 63em) { + .nowPlayingSecondaryButtons .nowPlayingPageUserDataButtons, + .nowPlayingSecondaryButtons .repeatToggleButton, + .nowPlayingInfoButtons .playlist .listItemMediaInfo, + .nowPlayingInfoButtons .btnStop { display: none !important; } - .navigationSection .collapseContent i { + .navigationSection .collapseContent .material-icons { font-size: 4em; } } - -@media all and (max-width: 47em) { - .nowPlayingInfoButtons .repeatToggleButton { - display: none !important; - } -} - -@media all and (max-width: 34em) { - .nowPlayingInfoButtons .btnNowPlayingFastForward, - .nowPlayingInfoButtons .btnNowPlayingRewind, - .nowPlayingInfoButtons .playlist .listItemMediaInfo { - display: none !important; - } -} diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index 7b620d536a..089915a834 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -1,5 +1,5 @@ -define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageLoader", "playbackManager", "nowPlayingHelper", "events", "connectionManager", "apphost", "globalize", "layoutManager", "userSettings", "cardStyle", "emby-itemscontainer", "css!./remotecontrol.css", "emby-ratingbutton"], function (browser, datetime, backdrop, libraryBrowser, listView, imageLoader, playbackManager, nowPlayingHelper, events, connectionManager, appHost, globalize, layoutManager, userSettings) { - "use strict"; +define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'events', 'connectionManager', 'apphost', 'globalize', 'layoutManager', 'userSettings', 'cardBuilder', 'cardStyle', 'emby-itemscontainer', 'css!./remotecontrol.css', 'emby-ratingbutton'], function (browser, datetime, backdrop, libraryBrowser, listView, imageLoader, playbackManager, nowPlayingHelper, events, connectionManager, appHost, globalize, layoutManager, userSettings, cardBuilder) { + 'use strict'; function showAudioMenu(context, player, button, item) { var currentIndex = playbackManager.getAudioStreamIndex(player); @@ -17,7 +17,7 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL return menuItem; }); - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, positionTo: button, @@ -45,11 +45,11 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL }); menuItems.unshift({ id: -1, - name: globalize.translate("ButtonOff"), + name: globalize.translate('ButtonOff'), selected: null == currentIndex }); - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, positionTo: button, @@ -63,22 +63,22 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL function getNowPlayingNameHtml(nowPlayingItem, includeNonNameInfo) { return nowPlayingHelper.getNowPlayingNames(nowPlayingItem, includeNonNameInfo).map(function (i) { return i.text; - }).join("
"); + }).join('
'); } function seriesImageUrl(item, options) { - if ("Episode" !== item.Type) { + if ('Episode' !== item.Type) { return null; } options = options || {}; - options.type = options.type || "Primary"; - if ("Primary" === options.type && item.SeriesPrimaryImageTag) { + options.type = options.type || 'Primary'; + if ('Primary' === options.type && item.SeriesPrimaryImageTag) { options.tag = item.SeriesPrimaryImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); } - if ("Thumb" === options.type) { + if ('Thumb' === options.type) { if (item.SeriesThumbImageTag) { options.tag = item.SeriesThumbImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); @@ -95,7 +95,7 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL function imageUrl(item, options) { options = options || {}; - options.type = options.type || "Primary"; + options.type = options.type || 'Primary'; if (item.ImageTags && item.ImageTags[options.type]) { options.tag = item.ImageTags[options.type]; @@ -110,65 +110,109 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL return null; } - function updateNowPlayingInfo(context, state) { + function updateNowPlayingInfo(context, state, serverId) { var item = state.NowPlayingItem; - var displayName = item ? getNowPlayingNameHtml(item).replace("
", " - ") : ""; - context.querySelector(".nowPlayingPageTitle").innerHTML = displayName; + var displayName = item ? getNowPlayingNameHtml(item).replace('
', ' - ') : ''; + if (typeof item !== 'undefined') { + var nowPlayingServerId = (item.ServerId || serverId); + if (item.Type == 'Audio' || item.MediaStreams[0].Type == 'Audio') { + var songName = item.Name; + if (item.Album != null && item.Artists != null) { + var albumName = item.Album; + var artistName; + if (item.ArtistItems != null) { + artistName = item.ArtistItems[0].Name; + context.querySelector('.nowPlayingAlbum').innerHTML = '${albumName}`; + context.querySelector('.nowPlayingArtist').innerHTML = '${artistName}`; + context.querySelector('.contextMenuAlbum').innerHTML = ' ` + globalize.translate('ViewAlbum') + ''; + context.querySelector('.contextMenuArtist').innerHTML = ' ` + globalize.translate('ViewArtist') + ''; + } else { + artistName = item.Artists; + context.querySelector('.nowPlayingAlbum').innerHTML = albumName; + context.querySelector('.nowPlayingArtist').innerHTML = artistName; + } + } + context.querySelector('.nowPlayingSongName').innerHTML = songName; + } else if (item.Type == 'Episode') { + if (item.SeasonName != null) { + var seasonName = item.SeasonName; + context.querySelector('.nowPlayingSeason').innerHTML = '${seasonName}`; + } + if (item.SeriesName != null) { + var seriesName = item.SeriesName; + if (item.SeriesId != null) { + context.querySelector('.nowPlayingSerie').innerHTML = '${seriesName}`; + } else { + context.querySelector('.nowPlayingSerie').innerHTML = seriesName; + } + } + context.querySelector('.nowPlayingEpisode').innerHTML = item.Name; + } else { + context.querySelector('.nowPlayingPageTitle').innerHTML = displayName; + } - if (displayName.length > 0) { - context.querySelector(".nowPlayingPageTitle").classList.remove("hide"); - } else { - context.querySelector(".nowPlayingPageTitle").classList.add("hide"); - } + if (displayName.length > 0 && item.Type != 'Audio' && item.Type != 'Episode') { + context.querySelector('.nowPlayingPageTitle').classList.remove('hide'); + } else { + context.querySelector('.nowPlayingPageTitle').classList.add('hide'); + } - var url = item ? seriesImageUrl(item, { - maxHeight: 300 * 2 - }) || imageUrl(item, { - maxHeight: 300 * 2 - }) : null; + var url = item ? seriesImageUrl(item, { + maxHeight: 300 * 2 + }) || imageUrl(item, { + maxHeight: 300 * 2 + }) : null; - console.debug("updateNowPlayingInfo"); - setImageUrl(context, url); - if (item) { - backdrop.setBackdrops([item]); - var apiClient = connectionManager.getApiClient(item.ServerId); - apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) { - var userData = fullItem.UserData || {}; - var likes = null == userData.Likes ? "" : userData.Likes; - context.querySelector(".nowPlayingPageUserDataButtons").innerHTML = ''; - }); - } else { - backdrop.clear(); - context.querySelector(".nowPlayingPageUserDataButtons").innerHTML = ""; + console.debug('updateNowPlayingInfo'); + setImageUrl(context, state, url); + if (item) { + backdrop.setBackdrops([item]); + var apiClient = connectionManager.getApiClient(item.ServerId); + apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) { + var userData = fullItem.UserData || {}; + var likes = null == userData.Likes ? '' : userData.Likes; + context.querySelector('.nowPlayingPageUserDataButtonsTitle').innerHTML = ''; + context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = ''; + }); + } else { + backdrop.clear(); + context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = ''; + } } } - function setImageUrl(context, url) { + function setImageUrl(context, state, url) { currentImgUrl = url; - var imgContainer = context.querySelector(".nowPlayingPageImageContainer"); + var item = state.NowPlayingItem; + var imgContainer = context.querySelector('.nowPlayingPageImageContainer'); if (url) { imgContainer.innerHTML = ''; - imgContainer.classList.remove("hide"); + if (item.Type == 'Audio') { + context.querySelector('.nowPlayingPageImage').classList.add('nowPlayingPageImageAudio'); + context.querySelector('.nowPlayingPageImageContainer').classList.remove('nowPlayingPageImageAudio'); + } else { + context.querySelector('.nowPlayingPageImageContainer').classList.add('nowPlayingPageImagePoster'); + context.querySelector('.nowPlayingPageImage').classList.remove('nowPlayingPageImageAudio'); + } } else { - imgContainer.classList.add("hide"); - imgContainer.innerHTML = ""; + imgContainer.innerHTML = '
'; } } function buttonVisible(btn, enabled) { if (enabled) { - btn.classList.remove("hide"); + btn.classList.remove('hide'); } else { - btn.classList.add("hide"); + btn.classList.add('hide'); } } function updateSupportedCommands(context, commands) { - var all = context.querySelectorAll(".btnCommand"); + var all = context.querySelectorAll('.btnCommand'); for (var i = 0, length = all.length; i < length; i++) { - var enableButton = -1 !== commands.indexOf(all[i].getAttribute("data-command")); + var enableButton = -1 !== commands.indexOf(all[i].getAttribute('data-command')); all[i].disabled = !enableButton; } } @@ -178,16 +222,16 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL function toggleRepeat(player) { if (player) { switch (playbackManager.getRepeatMode(player)) { - case "RepeatNone": - playbackManager.setRepeatMode("RepeatAll", player); + case 'RepeatNone': + playbackManager.setRepeatMode('RepeatAll', player); break; - case "RepeatAll": - playbackManager.setRepeatMode("RepeatOne", player); + case 'RepeatAll': + playbackManager.setRepeatMode('RepeatOne', player); break; - case "RepeatOne": - playbackManager.setRepeatMode("RepeatNone", player); + case 'RepeatOne': + playbackManager.setRepeatMode('RepeatNone', player); } } } @@ -199,34 +243,41 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL var supportedCommands = playerInfo.supportedCommands; currentPlayerSupportedCommands = supportedCommands; var playState = state.PlayState || {}; - buttonVisible(context.querySelector(".btnToggleFullscreen"), item && "Video" == item.MediaType && -1 != supportedCommands.indexOf("ToggleFullscreen")); + var isSupportedCommands = supportedCommands.includes('DisplayMessage') || supportedCommands.includes('SendString') || supportedCommands.includes('Select'); + buttonVisible(context.querySelector('.btnToggleFullscreen'), item && 'Video' == item.MediaType && supportedCommands.includes('ToggleFullscreen')); updateAudioTracksDisplay(player, context); updateSubtitleTracksDisplay(player, context); - if (-1 != supportedCommands.indexOf("DisplayMessage") && !currentPlayer.isLocalPlayer) { - context.querySelector(".sendMessageSection").classList.remove("hide"); + if (supportedCommands.includes('DisplayMessage') && !currentPlayer.isLocalPlayer) { + context.querySelector('.sendMessageSection').classList.remove('hide'); } else { - context.querySelector(".sendMessageSection").classList.add("hide"); + context.querySelector('.sendMessageSection').classList.add('hide'); } - if (-1 != supportedCommands.indexOf("SendString") && !currentPlayer.isLocalPlayer) { - context.querySelector(".sendTextSection").classList.remove("hide"); + if (supportedCommands.includes('SendString') && !currentPlayer.isLocalPlayer) { + context.querySelector('.sendTextSection').classList.remove('hide'); } else { - context.querySelector(".sendTextSection").classList.add("hide"); + context.querySelector('.sendTextSection').classList.add('hide'); } - if (-1 != supportedCommands.indexOf("Select") && !currentPlayer.isLocalPlayer) { - context.querySelector(".navigationSection").classList.remove("hide"); + if (supportedCommands.includes('Select') && !currentPlayer.isLocalPlayer) { + context.querySelector('.navigationSection').classList.remove('hide'); } else { - context.querySelector(".navigationSection").classList.add("hide"); + context.querySelector('.navigationSection').classList.add('hide'); } - buttonVisible(context.querySelector(".btnStop"), null != item); - buttonVisible(context.querySelector(".btnNextTrack"), null != item); - buttonVisible(context.querySelector(".btnPreviousTrack"), null != item); - buttonVisible(context.querySelector(".btnRewind"), null != item); - buttonVisible(context.querySelector(".btnFastForward"), null != item); - var positionSlider = context.querySelector(".nowPlayingPositionSlider"); + if (isSupportedCommands && !currentPlayer.isLocalPlayer) { + context.querySelector('.remoteControlSection').classList.remove('hide'); + } else { + context.querySelector('.remoteControlSection').classList.add('hide'); + } + + buttonVisible(context.querySelector('.btnStop'), null != item); + buttonVisible(context.querySelector('.btnNextTrack'), null != item); + buttonVisible(context.querySelector('.btnPreviousTrack'), null != item); + buttonVisible(context.querySelector('.btnRewind'), null != item); + buttonVisible(context.querySelector('.btnFastForward'), null != item); + var positionSlider = context.querySelector('.nowPlayingPositionSlider'); if (positionSlider && item && item.RunTimeTicks) { positionSlider.setKeyboardSteps(userSettings.skipBackLength() * 1000000 / item.RunTimeTicks, @@ -243,10 +294,10 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL updateTimeDisplay(playState.PositionTicks, item ? item.RunTimeTicks : null); updatePlayerVolumeState(context, playState.IsMuted, playState.VolumeLevel); - if (item && "Video" == item.MediaType) { - context.classList.remove("hideVideoButtons"); + if (item && 'Video' == item.MediaType) { + context.classList.remove('hideVideoButtons'); } else { - context.classList.add("hideVideoButtons"); + context.classList.add('hideVideoButtons'); } updateRepeatModeDisplay(playState.RepeatMode); @@ -255,27 +306,27 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL function updateAudioTracksDisplay(player, context) { var supportedCommands = currentPlayerSupportedCommands; - buttonVisible(context.querySelector(".btnAudioTracks"), playbackManager.audioTracks(player).length > 1 && -1 != supportedCommands.indexOf("SetAudioStreamIndex")); + buttonVisible(context.querySelector('.btnAudioTracks'), playbackManager.audioTracks(player).length > 1 && -1 != supportedCommands.indexOf('SetAudioStreamIndex')); } function updateSubtitleTracksDisplay(player, context) { var supportedCommands = currentPlayerSupportedCommands; - buttonVisible(context.querySelector(".btnSubtitles"), playbackManager.subtitleTracks(player).length && -1 != supportedCommands.indexOf("SetSubtitleStreamIndex")); + buttonVisible(context.querySelector('.btnSubtitles'), playbackManager.subtitleTracks(player).length && -1 != supportedCommands.indexOf('SetSubtitleStreamIndex')); } function updateRepeatModeDisplay(repeatMode) { var context = dlg; - var toggleRepeatButton = context.querySelector(".repeatToggleButton"); + var toggleRepeatButton = context.querySelector('.repeatToggleButton'); - if ("RepeatAll" == repeatMode) { - toggleRepeatButton.innerHTML = "repeat"; - toggleRepeatButton.classList.add("repeatButton-active"); - } else if ("RepeatOne" == repeatMode) { - toggleRepeatButton.innerHTML = ""; - toggleRepeatButton.classList.add("repeatButton-active"); + if ('RepeatAll' == repeatMode) { + toggleRepeatButton.innerHTML = ""; + toggleRepeatButton.classList.add('repeatButton-active'); + } else if ('RepeatOne' == repeatMode) { + toggleRepeatButton.innerHTML = ""; + toggleRepeatButton.classList.add('repeatButton-active'); } else { - toggleRepeatButton.innerHTML = "repeat"; - toggleRepeatButton.classList.remove("repeatButton-active"); + toggleRepeatButton.innerHTML = ""; + toggleRepeatButton.classList.remove('repeatButton-active'); } } @@ -285,41 +336,46 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL var showMuteButton = true; var showVolumeSlider = true; - if (-1 === supportedCommands.indexOf("Mute")) { + if (-1 === supportedCommands.indexOf('Mute')) { showMuteButton = false; } - if (-1 === supportedCommands.indexOf("SetVolume")) { + if (-1 === supportedCommands.indexOf('SetVolume')) { showVolumeSlider = false; } - if (currentPlayer.isLocalPlayer && appHost.supports("physicalvolumecontrol")) { + if (currentPlayer.isLocalPlayer && appHost.supports('physicalvolumecontrol')) { showMuteButton = false; showVolumeSlider = false; } + const buttonMute = view.querySelector('.buttonMute'); + const buttonMuteIcon = buttonMute.querySelector('.material-icons'); + + buttonMuteIcon.classList.remove('volume_off', 'volume_up'); + if (isMuted) { - view.querySelector(".buttonMute").setAttribute("title", globalize.translate("Unmute")); - view.querySelector(".buttonMute i").innerHTML = ""; + buttonMute.setAttribute('title', globalize.translate('Unmute')); + buttonMuteIcon.classList.add('volume_off'); } else { - view.querySelector(".buttonMute").setAttribute("title", globalize.translate("Mute")); - view.querySelector(".buttonMute i").innerHTML = ""; + buttonMute.setAttribute('title', globalize.translate('Mute')); + buttonMuteIcon.classList.add('volume_up'); } if (showMuteButton) { - view.querySelector(".buttonMute").classList.remove("hide"); + buttonMute.classList.remove('hide'); } else { - view.querySelector(".buttonMute").classList.add("hide"); + buttonMute.classList.add('hide'); } - var nowPlayingVolumeSlider = context.querySelector(".nowPlayingVolumeSlider"); - var nowPlayingVolumeSliderContainer = context.querySelector(".nowPlayingVolumeSliderContainer"); + var nowPlayingVolumeSlider = context.querySelector('.nowPlayingVolumeSlider'); + var nowPlayingVolumeSliderContainer = context.querySelector('.nowPlayingVolumeSliderContainer'); if (nowPlayingVolumeSlider) { if (showVolumeSlider) { - nowPlayingVolumeSliderContainer.classList.remove("hide"); + nowPlayingVolumeSliderContainer.classList.remove('hide'); } else { - nowPlayingVolumeSliderContainer.classList.add("hide"); + nowPlayingVolumeSliderContainer.classList.add('hide'); } if (!nowPlayingVolumeSlider.dragging) { @@ -330,14 +386,18 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL function updatePlayPauseState(isPaused, isActive) { var context = dlg; - var btnPlayPause = context.querySelector(".btnPlayPause"); - btnPlayPause.querySelector("i").innerHTML = isPaused ? "" : "pause"; + var btnPlayPause = context.querySelector('.btnPlayPause'); + const btnPlayPauseIcon = btnPlayPause.querySelector('.material-icons'); + + btnPlayPauseIcon.classList.remove('play_circle_filled', 'pause_circle_filled'); + btnPlayPauseIcon.classList.add(isPaused ? 'play_circle_filled' : 'pause_circle_filled'); + buttonVisible(btnPlayPause, isActive); } function updateTimeDisplay(positionTicks, runtimeTicks) { var context = dlg; - var positionSlider = context.querySelector(".nowPlayingPositionSlider"); + var positionSlider = context.querySelector('.nowPlayingPositionSlider'); if (positionSlider && !positionSlider.dragging) { if (runtimeTicks) { @@ -349,8 +409,8 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL } } - context.querySelector(".positionTime").innerHTML = null == positionTicks ? "--:--" : datetime.getDisplayRunningTime(positionTicks); - context.querySelector(".runtime").innerHTML = null != runtimeTicks ? datetime.getDisplayRunningTime(runtimeTicks) : "--:--"; + context.querySelector('.positionTime').innerHTML = null == positionTicks ? '--:--' : datetime.getDisplayRunningTime(positionTicks); + context.querySelector('.runtime').innerHTML = null != runtimeTicks ? datetime.getDisplayRunningTime(runtimeTicks) : '--:--'; } function getPlaylistItems(player) { @@ -359,27 +419,27 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL function loadPlaylist(context, player) { getPlaylistItems(player).then(function (items) { - var html = ""; + var html = ''; html += listView.getListViewHtml({ items: items, smallIcon: true, - action: "setplaylistindex", + action: 'setplaylistindex', enableUserDataButtons: false, rightButtons: [{ - icon: "", - title: globalize.translate("ButtonRemove"), - id: "remove" + icon: 'remove_circle_outline', + title: globalize.translate('ButtonRemove'), + id: 'remove' }], dragHandle: true }); if (items.length) { - context.querySelector(".playlistSection").classList.remove("hide"); + context.querySelector('.btnTogglePlaylist').classList.remove('hide'); } else { - context.querySelector(".playlistSection").classList.add("hide"); + context.querySelector('.btnTogglePlaylist').classList.add('hide'); } - var itemsContainer = context.querySelector(".playlist"); + var itemsContainer = context.querySelector('.playlist'); itemsContainer.innerHTML = html; var playlistItemId = playbackManager.getCurrentPlaylistItemId(player); @@ -387,17 +447,20 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL var img = itemsContainer.querySelector('.listItem[data-playlistItemId="' + playlistItemId + '"] .listItemImage'); if (img) { - img.classList.remove("lazy"); - img.classList.add("playlistIndexIndicatorImage"); + img.classList.remove('lazy'); + img.classList.add('playlistIndexIndicatorImage'); } } imageLoader.lazyChildren(itemsContainer); + context.querySelector('.playlist').classList.add('hide'); + context.querySelector('.contextMenu').classList.add('hide'); + context.querySelector('.btnSavePlaylist').classList.add('hide'); }); } function onPlaybackStart(e, state) { - console.debug("remotecontrol event: " + e.type); + console.debug('remotecontrol event: ' + e.type); var player = this; onStateChanged.call(player, e, state); } @@ -425,7 +488,7 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL } function onPlaybackStopped(e, state) { - console.debug("remotecontrol event: " + e.type); + console.debug('remotecontrol event: ' + e.type); var player = this; if (!state.NextMediaType) { @@ -465,16 +528,16 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL var player = currentPlayer; if (player) { - events.off(player, "playbackstart", onPlaybackStart); - events.off(player, "statechange", onStateChanged); - events.off(player, "repeatmodechange", onRepeatModeChange); - events.off(player, "playlistitemremove", onPlaylistUpdate); - events.off(player, "playlistitemmove", onPlaylistUpdate); - events.off(player, "playbackstop", onPlaybackStopped); - events.off(player, "volumechange", onVolumeChanged); - events.off(player, "pause", onPlayPauseStateChanged); - events.off(player, "unpause", onPlayPauseStateChanged); - events.off(player, "timeupdate", onTimeUpdate); + events.off(player, 'playbackstart', onPlaybackStart); + events.off(player, 'statechange', onStateChanged); + events.off(player, 'repeatmodechange', onRepeatModeChange); + events.off(player, 'playlistitemremove', onPlaylistUpdate); + events.off(player, 'playlistitemmove', onPlaylistUpdate); + events.off(player, 'playbackstop', onPlaybackStopped); + events.off(player, 'volumechange', onVolumeChanged); + events.off(player, 'pause', onPlayPauseStateChanged); + events.off(player, 'unpause', onPlayPauseStateChanged); + events.off(player, 'timeupdate', onTimeUpdate); currentPlayer = null; } } @@ -483,18 +546,18 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL if (releaseCurrentPlayer(), currentPlayer = player, player) { var state = playbackManager.getPlayerState(player); onStateChanged.call(player, { - type: "init" + type: 'init' }, state); - events.on(player, "playbackstart", onPlaybackStart); - events.on(player, "statechange", onStateChanged); - events.on(player, "repeatmodechange", onRepeatModeChange); - events.on(player, "playlistitemremove", onPlaylistItemRemoved); - events.on(player, "playlistitemmove", onPlaylistUpdate); - events.on(player, "playbackstop", onPlaybackStopped); - events.on(player, "volumechange", onVolumeChanged); - events.on(player, "pause", onPlayPauseStateChanged); - events.on(player, "unpause", onPlayPauseStateChanged); - events.on(player, "timeupdate", onTimeUpdate); + events.on(player, 'playbackstart', onPlaybackStart); + events.on(player, 'statechange', onStateChanged); + events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'playlistitemremove', onPlaylistItemRemoved); + events.on(player, 'playlistitemmove', onPlaylistUpdate); + events.on(player, 'playbackstop', onPlaybackStopped); + events.on(player, 'volumechange', onVolumeChanged); + events.on(player, 'pause', onPlayPauseStateChanged); + events.on(player, 'unpause', onPlayPauseStateChanged); + events.on(player, 'timeupdate', onTimeUpdate); var playerInfo = playbackManager.getPlayerInfo(); var supportedCommands = playerInfo.supportedCommands; currentPlayerSupportedCommands = supportedCommands; @@ -504,11 +567,11 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL function onBtnCommandClick() { if (currentPlayer) { - if (this.classList.contains("repeatToggleButton")) { + if (this.classList.contains('repeatToggleButton')) { toggleRepeat(currentPlayer); } else { playbackManager.sendCommand({ - Name: this.getAttribute("data-command") + Name: this.getAttribute('data-command') }, currentPlayer); } } @@ -523,7 +586,7 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL } function savePlaylist() { - require(["playlistEditor"], function (playlistEditor) { + require(['playlistEditor'], function (playlistEditor) { getSaveablePlaylistItems().then(function (items) { var serverId = items.length ? items[0].ServerId : ApiClient.serverId(); new playlistEditor().show({ @@ -532,67 +595,67 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL }), serverId: serverId, enableAddToPlayQueue: false, - defaultValue: "new" + defaultValue: 'new' }); }); }); } function bindEvents(context) { - var btnCommand = context.querySelectorAll(".btnCommand"); + var btnCommand = context.querySelectorAll('.btnCommand'); for (var i = 0, length = btnCommand.length; i < length; i++) { - btnCommand[i].addEventListener("click", onBtnCommandClick); + btnCommand[i].addEventListener('click', onBtnCommandClick); } - context.querySelector(".btnToggleFullscreen").addEventListener("click", function (e) { + context.querySelector('.btnToggleFullscreen').addEventListener('click', function (e) { if (currentPlayer) { playbackManager.sendCommand({ - Name: e.target.getAttribute("data-command") + Name: e.target.getAttribute('data-command') }, currentPlayer); } }); - context.querySelector(".btnAudioTracks").addEventListener("click", function (e) { + context.querySelector('.btnAudioTracks').addEventListener('click', function (e) { if (currentPlayer && lastPlayerState && lastPlayerState.NowPlayingItem) { showAudioMenu(context, currentPlayer, e.target, lastPlayerState.NowPlayingItem); } }); - context.querySelector(".btnSubtitles").addEventListener("click", function (e) { + context.querySelector('.btnSubtitles').addEventListener('click', function (e) { if (currentPlayer && lastPlayerState && lastPlayerState.NowPlayingItem) { showSubtitleMenu(context, currentPlayer, e.target, lastPlayerState.NowPlayingItem); } }); - context.querySelector(".btnStop").addEventListener("click", function () { + context.querySelector('.btnStop').addEventListener('click', function () { if (currentPlayer) { playbackManager.stop(currentPlayer); } }); - context.querySelector(".btnPlayPause").addEventListener("click", function () { + context.querySelector('.btnPlayPause').addEventListener('click', function () { if (currentPlayer) { playbackManager.playPause(currentPlayer); } }); - context.querySelector(".btnNextTrack").addEventListener("click", function () { + context.querySelector('.btnNextTrack').addEventListener('click', function () { if (currentPlayer) { playbackManager.nextTrack(currentPlayer); } }); - context.querySelector(".btnRewind").addEventListener("click", function () { + context.querySelector('.btnRewind').addEventListener('click', function () { if (currentPlayer) { playbackManager.rewind(currentPlayer); } }); - context.querySelector(".btnFastForward").addEventListener("click", function () { + context.querySelector('.btnFastForward').addEventListener('click', function () { if (currentPlayer) { playbackManager.fastForward(currentPlayer); } }); - context.querySelector(".btnPreviousTrack").addEventListener("click", function () { + context.querySelector('.btnPreviousTrack').addEventListener('click', function () { if (currentPlayer) { playbackManager.previousTrack(currentPlayer); } }); - context.querySelector(".nowPlayingPositionSlider").addEventListener("change", function () { + context.querySelector('.nowPlayingPositionSlider').addEventListener('change', function () { var value = this.value; if (currentPlayer) { @@ -601,11 +664,11 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL } }); - context.querySelector(".nowPlayingPositionSlider").getBubbleText = function (value) { + context.querySelector('.nowPlayingPositionSlider').getBubbleText = function (value) { var state = lastPlayerState; if (!state || !state.NowPlayingItem || !currentRuntimeTicks) { - return "--:--"; + return '--:--'; } var ticks = currentRuntimeTicks; @@ -614,40 +677,47 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL return datetime.getDisplayRunningTime(ticks); }; - var volumeSliderTimer; - function setVolume() { - clearTimeout(volumeSliderTimer); - volumeSliderTimer = null; - playbackManager.setVolume(this.value, currentPlayer); } - function setVolumeDelayed() { - if (!volumeSliderTimer) { - var that = this; - volumeSliderTimer = setTimeout(function () { - setVolume.call(that); - }, 700); - } - } - - context.querySelector(".nowPlayingVolumeSlider").addEventListener("change", setVolume); - context.querySelector(".nowPlayingVolumeSlider").addEventListener("mousemove", setVolumeDelayed); - context.querySelector(".nowPlayingVolumeSlider").addEventListener("touchmove", setVolumeDelayed); - context.querySelector(".buttonMute").addEventListener("click", function () { + context.querySelector('.nowPlayingVolumeSlider').addEventListener('change', setVolume); + context.querySelector('.nowPlayingVolumeSlider').addEventListener('mousemove', setVolume); + context.querySelector('.nowPlayingVolumeSlider').addEventListener('touchmove', setVolume); + context.querySelector('.buttonMute').addEventListener('click', function () { playbackManager.toggleMute(currentPlayer); }); - var playlistContainer = context.querySelector(".playlist"); - playlistContainer.addEventListener("action-remove", function (e) { + var playlistContainer = context.querySelector('.playlist'); + playlistContainer.addEventListener('action-remove', function (e) { playbackManager.removeFromPlaylist([e.detail.playlistItemId], currentPlayer); }); - playlistContainer.addEventListener("itemdrop", function (e) { + playlistContainer.addEventListener('itemdrop', function (e) { var newIndex = e.detail.newIndex; var playlistItemId = e.detail.playlistItemId; playbackManager.movePlaylistItem(playlistItemId, newIndex, currentPlayer); }); - context.querySelector(".btnSavePlaylist").addEventListener("click", savePlaylist); + context.querySelector('.btnSavePlaylist').addEventListener('click', savePlaylist); + context.querySelector('.btnTogglePlaylist').addEventListener('click', function () { + if (context.querySelector('.playlist').classList.contains('hide')) { + context.querySelector('.playlist').classList.remove('hide'); + context.querySelector('.btnSavePlaylist').classList.remove('hide'); + context.querySelector('.contextMenu').classList.add('hide'); + context.querySelector('.volumecontrol').classList.add('hide'); + } else { + context.querySelector('.playlist').classList.add('hide'); + context.querySelector('.btnSavePlaylist').classList.add('hide'); + context.querySelector('.volumecontrol').classList.remove('hide'); + } + }); + context.querySelector('.btnToggleContextMenu').addEventListener('click', function () { + if (context.querySelector('.contextMenu').classList.contains('hide')) { + context.querySelector('.contextMenu').classList.remove('hide'); + context.querySelector('.btnSavePlaylist').classList.add('hide'); + context.querySelector('.playlist').classList.add('hide'); + } else { + context.querySelector('.contextMenu').classList.add('hide'); + } + }); } function onPlayerChange() { @@ -657,16 +727,16 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL function onMessageSubmit(e) { var form = e.target; playbackManager.sendCommand({ - Name: "DisplayMessage", + Name: 'DisplayMessage', Arguments: { - Header: form.querySelector("#txtMessageTitle").value, - Text: form.querySelector("#txtMessageText", form).value + Header: form.querySelector('#txtMessageTitle').value, + Text: form.querySelector('#txtMessageText', form).value } }, currentPlayer); - form.querySelector("input").value = ""; + form.querySelector('input').value = ''; - require(["toast"], function (toast) { - toast("Message sent."); + require(['toast'], function (toast) { + toast('Message sent.'); }); e.preventDefault(); @@ -677,15 +747,15 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL function onSendStringSubmit(e) { var form = e.target; playbackManager.sendCommand({ - Name: "SendString", + Name: 'SendString', Arguments: { - String: form.querySelector("#txtTypeText", form).value + String: form.querySelector('#txtTypeText', form).value } }, currentPlayer); - form.querySelector("input").value = ""; + form.querySelector('input').value = ''; - require(["toast"], function (toast) { - toast("Text sent."); + require(['toast'], function (toast) { + toast('Text sent.'); }); e.preventDefault(); @@ -694,21 +764,33 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL } function init(ownerView, context) { + let contextmenuHtml = ``; + let volumecontrolHtml = '
'; + volumecontrolHtml += ``; + volumecontrolHtml += '
'; + volumecontrolHtml += '
'; + if (!layoutManager.mobile) { + context.querySelector('.nowPlayingSecondaryButtons').innerHTML += volumecontrolHtml; + context.querySelector('.playlistSectionButton').innerHTML += contextmenuHtml; + } else { + context.querySelector('.playlistSectionButton').innerHTML += volumecontrolHtml + contextmenuHtml; + } + bindEvents(context); - context.querySelector(".sendMessageForm").addEventListener("submit", onMessageSubmit); - context.querySelector(".typeTextForm").addEventListener("submit", onSendStringSubmit); - events.on(playbackManager, "playerchange", onPlayerChange); + context.querySelector('.sendMessageForm').addEventListener('submit', onMessageSubmit); + context.querySelector('.typeTextForm').addEventListener('submit', onSendStringSubmit); + events.on(playbackManager, 'playerchange', onPlayerChange); if (layoutManager.tv) { - var positionSlider = context.querySelector(".nowPlayingPositionSlider"); - positionSlider.classList.add("focusable"); + var positionSlider = context.querySelector('.nowPlayingPositionSlider'); + positionSlider.classList.add('focusable'); positionSlider.enableKeyboardDragging(); } } function onDialogClosed(e) { releaseCurrentPlayer(); - events.off(playbackManager, "playerchange", onPlayerChange); + events.off(playbackManager, 'playerchange', onPlayerChange); lastPlayerState = null; } diff --git a/src/components/sanitizefilename.js b/src/components/sanitizeFilename.js similarity index 93% rename from src/components/sanitizefilename.js rename to src/components/sanitizeFilename.js index adfb852e1f..de7b1a0782 100644 --- a/src/components/sanitizefilename.js +++ b/src/components/sanitizeFilename.js @@ -16,8 +16,8 @@ function isLowSurrogate(codePoint) { } function getByteLength(string) { - if (typeof string !== "string") { - throw new Error("Input must be string"); + if (typeof string !== 'string') { + throw new Error('Input must be string'); } const charLength = string.length; @@ -49,8 +49,8 @@ function getByteLength(string) { } function truncate(string, byteLength) { - if (typeof string !== "string") { - throw new Error("Input must be string"); + if (typeof string !== 'string') { + throw new Error('Input must be string'); } const charLength = string.length; diff --git a/src/components/scrollManager.js b/src/components/scrollManager.js index 6a626cd254..3b01cf1ad1 100644 --- a/src/components/scrollManager.js +++ b/src/components/scrollManager.js @@ -5,9 +5,9 @@ * @module components/scrollManager */ -import dom from "dom"; -import browser from "browser"; -import layoutManager from "layoutManager"; +import dom from 'dom'; +import browser from 'browser'; +import layoutManager from 'layoutManager'; /** * Scroll time in ms. @@ -27,20 +27,20 @@ import layoutManager from "layoutManager"; * @return {number} Minimum vertical scroll. */ function minimumScrollY() { - const topMenu = document.querySelector(".headerTop"); + const topMenu = document.querySelector('.headerTop'); if (topMenu) { return topMenu.clientHeight; } return 0; } - const supportsSmoothScroll = "scrollBehavior" in document.documentElement.style; + const supportsSmoothScroll = 'scrollBehavior' in document.documentElement.style; let supportsScrollToOptions = false; try { - const elem = document.createElement("div"); + const elem = document.createElement('div'); - const opts = Object.defineProperty({}, "behavior", { + const opts = Object.defineProperty({}, 'behavior', { // eslint-disable-next-line getter-return get: function () { supportsScrollToOptions = true; @@ -49,7 +49,7 @@ import layoutManager from "layoutManager"; elem.scrollTo(opts); } catch (e) { - console.error("error checking ScrollToOptions support"); + console.error('error checking ScrollToOptions support'); } /** @@ -92,7 +92,7 @@ import layoutManager from "layoutManager"; * @return {number} Eased value in range [0, 1]. */ function ease(t) { - return t*(2 - t); // easeOutQuad === ease-out + return t * (2 - t); // easeOutQuad === ease-out } /** @@ -210,21 +210,21 @@ import layoutManager from "layoutManager"; */ function getScrollableParent(element, vertical) { if (element) { - let nameScroll = "scrollWidth"; - let nameClient = "clientWidth"; - let nameClass = "scrollX"; + let nameScroll = 'scrollWidth'; + let nameClient = 'clientWidth'; + let nameClass = 'scrollX'; if (vertical) { - nameScroll = "scrollHeight"; - nameClient = "clientHeight"; - nameClass = "scrollY"; + nameScroll = 'scrollHeight'; + nameClient = 'clientHeight'; + nameClass = 'scrollY'; } let parent = element.parentElement; while (parent) { // Skip 'emby-scroller' because it scrolls by itself - if (!parent.classList.contains("emby-scroller") && + if (!parent.classList.contains('emby-scroller') && parent[nameScroll] > parent[nameClient] && parent.classList.contains(nameClass)) { return parent; } @@ -316,7 +316,7 @@ import layoutManager from "layoutManager"; * @param {ScrollToOptions} options - Scroll options. */ function scrollToHelper(scroller, options) { - if ("scrollTo" in scroller) { + if ('scrollTo' in scroller) { if (!supportsScrollToOptions) { const scrollX = (options.left !== undefined ? options.left : scroller.scrollLeft); const scrollY = (options.top !== undefined ? options.top : scroller.scrollTop); @@ -324,7 +324,7 @@ import layoutManager from "layoutManager"; } else { scroller.scrollTo(options); } - } else if ("scrollLeft" in scroller) { + } else if ('scrollLeft' in scroller) { if (options.left !== undefined) { scroller.scrollLeft = options.left; } @@ -344,7 +344,7 @@ import layoutManager from "layoutManager"; * @param {boolean} smooth - Smooth scrolling. */ function builtinScroll(xScroller, scrollX, yScroller, scrollY, smooth) { - const scrollBehavior = smooth ? "smooth" : "instant"; + const scrollBehavior = smooth ? 'smooth' : 'instant'; if (xScroller !== yScroller) { scrollToHelper(xScroller, {left: scrollX, behavior: scrollBehavior}); @@ -402,8 +402,8 @@ import layoutManager from "layoutManager"; k = ease(k); - const x = ox + dx*k; - const y = oy + dy*k; + const x = ox + dx * k; + const y = oy + dy * k; builtinScroll(xScroller, x, yScroller, y, false); @@ -500,7 +500,7 @@ import layoutManager from "layoutManager"; const offsetParent = element.offsetParent; // In Firefox offsetParent.offsetParent is BODY - const 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) { @@ -537,7 +537,7 @@ import layoutManager from "layoutManager"; } if (isEnabled()) { - dom.addEventListener(window, "focusin", function(e) { + dom.addEventListener(window, 'focusin', function(e) { setTimeout(function() { scrollToElement(e.target, useSmoothScroll()); }, 0); diff --git a/src/components/search/searchfields.template.html b/src/components/search/searchfields.template.html index c1e9c64347..7da5e240c5 100644 --- a/src/components/search/searchfields.template.html +++ b/src/components/search/searchfields.template.html @@ -1,5 +1,5 @@
- search +
diff --git a/src/components/search/searchresults.js b/src/components/search/searchresults.js index 02f1feefe1..d4de2349a4 100644 --- a/src/components/search/searchresults.js +++ b/src/components/search/searchresults.js @@ -5,8 +5,8 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', var options = { - SortBy: "IsFavoriteOrLiked,Random", - IncludeItemTypes: "Movie,Series,MusicArtist", + SortBy: 'IsFavoriteOrLiked,Random', + IncludeItemTypes: 'Movie,Series,MusicArtist', Limit: 20, Recursive: true, ImageTypeLimit: 0, @@ -165,7 +165,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "LiveTvProgram", + IncludeItemTypes: 'LiveTvProgram', IsMovie: true, IsKids: false, IsNews: false @@ -194,7 +194,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "Movie" + IncludeItemTypes: 'Movie' }, context, '.movieResults', { @@ -212,7 +212,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "Series" + IncludeItemTypes: 'Series' }, context, '.seriesResults', { @@ -231,7 +231,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "LiveTvProgram", + IncludeItemTypes: 'LiveTvProgram', IsSeries: true, IsSports: false, IsKids: false, @@ -262,7 +262,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "Episode" + IncludeItemTypes: 'Episode' }, context, '.episodeResults', { @@ -363,7 +363,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "LiveTvProgram", + IncludeItemTypes: 'LiveTvProgram', IsMovie: instance.options.collectionType === 'livetv' ? false : null, IsSeries: instance.options.collectionType === 'livetv' ? false : null, IsSports: instance.options.collectionType === 'livetv' ? false : null, @@ -394,8 +394,8 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - MediaTypes: "Video", - ExcludeItemTypes: "Movie,Episode" + MediaTypes: 'Video', + ExcludeItemTypes: 'Movie,Episode' }, context, '.videoResults', { @@ -439,7 +439,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "MusicAlbum" + IncludeItemTypes: 'MusicAlbum' }, context, '.albumResults', { @@ -456,7 +456,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "Audio" + IncludeItemTypes: 'Audio' }, context, '.songResults', { @@ -475,7 +475,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - MediaTypes: "Photo" + MediaTypes: 'Photo' }, context, '.photoResults', { @@ -492,7 +492,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "PhotoAlbum" + IncludeItemTypes: 'PhotoAlbum' }, context, '.photoAlbumResults', { @@ -508,7 +508,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "Book" + IncludeItemTypes: 'Book' }, context, '.bookResults', { @@ -525,7 +525,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "AudioBook" + IncludeItemTypes: 'AudioBook' }, context, '.audioBookResults', { @@ -541,7 +541,7 @@ define(['layoutManager', 'globalize', 'require', 'events', 'connectionManager', IncludeGenres: false, IncludeStudios: false, IncludeArtists: false, - IncludeItemTypes: "Playlist" + IncludeItemTypes: 'Playlist' }, context, '.playlistResults', { diff --git a/src/components/serverRestartDialog.js b/src/components/serverRestartDialog.js index ef012fe44a..4c52d5f01b 100644 --- a/src/components/serverRestartDialog.js +++ b/src/components/serverRestartDialog.js @@ -12,7 +12,7 @@ define(['loading', 'events', 'dialogHelper', 'dom', 'layoutManager', 'scrollHelp } // Don't use apiclient method because we don't want it reporting authentication under the old version - apiClient.getJSON(apiClient.getUrl("System/Info")).then(function (info) { + apiClient.getJSON(apiClient.getUrl('System/Info')).then(function (info) { // If this is back to false, the restart completed if (!info.IsShuttingDown) { diff --git a/src/components/serviceworker/notifications.js b/src/components/serviceworker/notifications.js index 33f54bb64d..5f96d01a4d 100644 --- a/src/components/serviceworker/notifications.js +++ b/src/components/serviceworker/notifications.js @@ -20,7 +20,7 @@ case 'restart': return apiClient.restartServer(); default: - clients.openWindow("/"); + clients.openWindow('/'); return Promise.resolve(); } }); @@ -35,7 +35,7 @@ var action = event.action; if (!action) { - clients.openWindow("/"); + clients.openWindow('/'); event.waitUntil(Promise.resolve()); return; } diff --git a/src/components/skinManager.js b/src/components/skinManager.js index d5b045c44e..c38d34181b 100644 --- a/src/components/skinManager.js +++ b/src/components/skinManager.js @@ -1,4 +1,4 @@ -define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdrop', 'globalize', 'require', 'appSettings'], function (appHost, userSettings, browser, events, pluginManager, backdrop, globalize, require, appSettings) { +define(['apphost', 'userSettings', 'browser', 'events', 'backdrop', 'globalize', 'require', 'appSettings'], function (appHost, userSettings, browser, events, backdrop, globalize, require, appSettings) { 'use strict'; var themeStyleElement; @@ -24,25 +24,25 @@ define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdr function getThemes() { return [{ - name: "Apple TV", - id: "appletv" + name: 'Apple TV', + id: 'appletv' }, { - name: "Blue Radiance", - id: "blueradiance" + name: 'Blue Radiance', + id: 'blueradiance' }, { - name: "Dark", - id: "dark", + name: 'Dark', + id: 'dark', isDefault: true, isDefaultServerDashboard: true }, { - name: "Light", - id: "light" + name: 'Light', + id: 'light' }, { - name: "Purple Haze", - id: "purplehaze" + name: 'Purple Haze', + id: 'purplehaze' }, { - name: "Windows Media Center", - id: "wmc" + name: 'Windows Media Center', + id: 'wmc' }]; } @@ -57,7 +57,6 @@ define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdr var selectedTheme; for (var i = 0, length = themes.length; i < length; i++) { - var theme = themes[i]; if (theme[isDefaultProperty]) { defaultTheme = theme; @@ -91,7 +90,7 @@ define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdr function onThemeLoaded() { document.documentElement.classList.remove('preload'); try { - var color = getComputedStyle(document.querySelector('.skinHeader')).getPropertyValue("background-color"); + var color = getComputedStyle(document.querySelector('.skinHeader')).getPropertyValue('background-color'); if (color) { appHost.setThemeColor(color); } @@ -137,6 +136,8 @@ define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdr function onViewBeforeShow(e) { if (e.detail && e.detail.type === 'video-osd') { + // This removes the space that the scrollbar takes while playing a video + document.body.classList.remove('force-scroll'); return; } @@ -155,6 +156,9 @@ define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdr } } } + // This keeps the scrollbar always present in all pages, so we avoid clipping while switching between pages + // that need the scrollbar and pages that don't. + document.body.classList.add('force-scroll'); } document.addEventListener('viewshow', onViewBeforeShow); diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index 26dc303def..6ff88a00c9 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -14,7 +14,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f */ function getImageUrl(item, options, apiClient) { options = options || {}; - options.type = options.type || "Primary"; + options.type = options.type || 'Primary'; if (typeof (item) === 'string') { return apiClient.getScaledImageUrl(item, options); @@ -45,7 +45,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f */ function getBackdropImageUrl(item, options, apiClient) { options = options || {}; - options.type = options.type || "Backdrop"; + options.type = options.type || 'Backdrop'; // If not resizing, get the original image if (!options.maxWidth && !options.width && !options.maxHeight && !options.height) { @@ -66,17 +66,17 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f * @param {object} item - Item used to generate the image URL. * @returns {string} URL of the item's image. */ - function getImgUrl(item) { + function getImgUrl(item, user) { var apiClient = connectionManager.getApiClient(item.ServerId); var imageOptions = {}; if (item.BackdropImageTags && item.BackdropImageTags.length) { return getBackdropImageUrl(item, imageOptions, apiClient); } else { - if (item.MediaType === 'Photo') { + if (item.MediaType === 'Photo' && user && user.Policy.EnableContentDownloading) { return apiClient.getItemDownloadUrl(item.Id); } - imageOptions.type = "Primary"; + imageOptions.type = 'Primary'; return getImageUrl(item, imageOptions, apiClient); } } @@ -92,7 +92,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f function getIcon(icon, cssClass, canFocus, autoFocus) { var tabIndex = canFocus ? '' : ' tabindex="-1"'; autoFocus = autoFocus ? ' autofocus' : ''; - return ''; + return ''; } /** @@ -123,7 +123,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f var _osdOpen = false; // Use autoplay on Chromecast since it is non-interactive. - options.interactive = !browser.chromecast; + if (browser.chromecast) options.interactive = false; /** * Creates the HTML markup for the dialog and the OSD. @@ -155,7 +155,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f html += '
'; if (actionButtonsOnTop) { - if (appHost.supports('filedownload')) { + if (appHost.supports('filedownload') && options.user && options.user.Policy.EnableContentDownloading) { html += getIcon('file_download', 'btnDownload slideshowButton', true); } if (appHost.supports('sharing')) { @@ -169,7 +169,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f html += '
'; html += getIcon('play_arrow', 'btnSlideshowPause slideshowButton', true, true); - if (appHost.supports('filedownload')) { + if (appHost.supports('filedownload') && options.user && options.user.Policy.EnableContentDownloading) { html += getIcon('file_download', 'btnDownload slideshowButton', true); } if (appHost.supports('sharing')) { @@ -224,9 +224,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f * Handles OSD changes when the autoplay is started. */ function onAutoplayStart() { - var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause i'); + var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons'); if (btnSlideshowPause) { - btnSlideshowPause.classList.replace("play_arrow", "pause"); + btnSlideshowPause.classList.replace('play_arrow', 'pause'); } } @@ -234,9 +234,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f * Handles OSD changes when the autoplay is stopped. */ function onAutoplayStop() { - var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause i'); + var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons'); if (btnSlideshowPause) { - btnSlideshowPause.classList.replace("pause", "play_arrow"); + btnSlideshowPause.classList.replace('pause', 'play_arrow'); } } @@ -258,6 +258,11 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f direction: 'horizontal', // Loop is disabled due to the virtual slides option not supporting it. loop: false, + zoom: { + minRatio: 1, + toggle: true, + containerClass: 'slider-zoom-container' + }, autoplay: !options.interactive, keyboard: { enabled: true @@ -307,8 +312,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f */ function getSwiperSlideHtmlFromItem(item) { return getSwiperSlideHtmlFromSlide({ - imageUrl: getImgUrl(item), - originalImage: getImgUrl(item, true), + originalImage: getImgUrl(item, currentOptions.user), //title: item.Name, //description: item.Overview Id: item.Id, @@ -414,7 +418,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f * Toggles the autoplay feature of the Swiper instance. */ function playPause() { - var paused = !dialog.querySelector('.btnSlideshowPause i').classList.contains("pause"); + var paused = !dialog.querySelector('.btnSlideshowPause .material-icons').classList.contains('pause'); if (paused) { play(); } else { @@ -434,6 +438,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f inputManager.off(window, onInputCommand); document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); + // Shows page scrollbar + document.body.classList.remove('hide-scroll'); + document.body.classList.add('force-scroll'); } /** @@ -599,13 +606,15 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f */ self.show = function () { createElements(options); + // Hides page scrollbar + document.body.classList.remove('force-scroll'); + document.body.classList.add('hide-scroll'); }; /** * Hides the slideshow element. */ self.hide = function () { - var dialog = dialog; if (dialog) { dialogHelper.close(dialog); } diff --git a/src/components/slideshow/style.css b/src/components/slideshow/style.css index 2bea7c9696..f1fea508d7 100644 --- a/src/components/slideshow/style.css +++ b/src/components/slideshow/style.css @@ -41,17 +41,13 @@ } .swiper-slide-img { - width: auto; - height: auto; - max-width: 100%; max-height: 100%; - -ms-transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); - -moz-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); - position: absolute; - left: 50%; - top: 50%; + max-width: 100%; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + margin: auto; } .slideshowButtonIcon { @@ -138,3 +134,14 @@ .slideSubtitle { color: #ccc; } + +.swiper-slide { + display: flex; + flex-direction: column; +} + +.slider-zoom-container { + margin: auto; + max-height: 100%; + max-width: 100%; +} diff --git a/src/components/sortmenu/sortmenu.js b/src/components/sortmenu/sortmenu.js index a0b40acaf6..9950afb89a 100644 --- a/src/components/sortmenu/sortmenu.js +++ b/src/components/sortmenu/sortmenu.js @@ -66,7 +66,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana var html = ''; html += '
'; - html += ''; + html += ''; html += '

${Sort}

'; html += '
'; diff --git a/src/components/subtitleeditor/subtitleeditor.js b/src/components/subtitleeditor/subtitleeditor.js index 1ff30712f6..e9bcc0bfca 100644 --- a/src/components/subtitleeditor/subtitleeditor.js +++ b/src/components/subtitleeditor/subtitleeditor.js @@ -48,7 +48,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', var apiClient = connectionManager.getApiClient(currentItem.ServerId); apiClient.ajax({ - type: "POST", + type: 'POST', url: apiClient.getUrl(url) }).then(function () { @@ -87,7 +87,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', apiClient.ajax({ - type: "DELETE", + type: 'DELETE', url: apiClient.getUrl(url) }).then(function () { @@ -132,7 +132,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', itemHtml += '<' + tagName + ' class="' + className + '" data-index="' + s.Index + '">'; - itemHtml += ''; + itemHtml += ''; itemHtml += '
'; @@ -149,7 +149,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', if (!layoutManager.tv) { if (s.Path) { - itemHtml += ''; + itemHtml += ''; } } @@ -232,11 +232,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', html += '
'; } html += '

' + provider + '

'; - if (layoutManager.tv) { - html += '
'; - } else { - html += '
'; - } + html += '
'; lastProvider = provider; } @@ -248,7 +244,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', html += '<' + tagName + ' class="' + className + '" data-subid="' + result.Id + '">'; - html += ''; + html += ''; var bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line'; @@ -281,7 +277,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', html += '
'; if (!layoutManager.tv) { - html += ''; + html += ''; } html += ''; @@ -397,7 +393,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', var items = []; items.push({ - name: Globalize.translate('Download'), + name: globalize.translate('Download'), id: 'download' }); diff --git a/src/components/subtitleeditor/subtitleeditor.template.html b/src/components/subtitleeditor/subtitleeditor.template.html index 8719cc9305..7471972f59 100644 --- a/src/components/subtitleeditor/subtitleeditor.template.html +++ b/src/components/subtitleeditor/subtitleeditor.template.html @@ -1,9 +1,8 @@
- +

${Subtitles}

- info${Help} - + ${Help}
@@ -18,7 +17,7 @@
- +
diff --git a/src/components/subtitlesettings/subtitleappearancehelper.js b/src/components/subtitlesettings/subtitleappearancehelper.js index fdc64f0dfa..bec8156aca 100644 --- a/src/components/subtitlesettings/subtitleappearancehelper.js +++ b/src/components/subtitlesettings/subtitleappearancehelper.js @@ -1,5 +1,5 @@ define([], function () { - "use strict"; + 'use strict'; function getTextStyles(settings, isCue) { diff --git a/src/components/subtitlesettings/subtitlesettings.js b/src/components/subtitlesettings/subtitlesettings.js index 2c86929192..d728360d05 100644 --- a/src/components/subtitlesettings/subtitlesettings.js +++ b/src/components/subtitlesettings/subtitlesettings.js @@ -1,13 +1,13 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loading', 'connectionManager', 'subtitleAppearanceHelper', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-input', 'emby-checkbox', 'flexStyles'], function (require, globalize, appSettings, appHost, focusManager, loading, connectionManager, subtitleAppearanceHelper, dom, events) { - "use strict"; + 'use strict'; function populateLanguages(select, languages) { - var html = ""; + var html = ''; - html += ""; + html += "'; for (var i = 0, length = languages.length; i < length; i++) { var culture = languages[i]; - html += ""; + html += "'; } select.innerHTML = html; @@ -37,8 +37,8 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi populateLanguages(selectSubtitleLanguage, allCultures); - selectSubtitleLanguage.value = user.Configuration.SubtitleLanguagePreference || ""; - context.querySelector('#selectSubtitlePlaybackMode').value = user.Configuration.SubtitleMode || ""; + selectSubtitleLanguage.value = user.Configuration.SubtitleLanguagePreference || ''; + context.querySelector('#selectSubtitlePlaybackMode').value = user.Configuration.SubtitleMode || ''; context.querySelector('#selectSubtitlePlaybackMode').dispatchEvent(new CustomEvent('change', {})); diff --git a/src/components/subtitlesync/subtitlesync.js b/src/components/subtitlesync/subtitlesync.js index 29d110f12e..fb986ec348 100644 --- a/src/components/subtitlesync/subtitlesync.js +++ b/src/components/subtitlesync/subtitlesync.js @@ -1,5 +1,5 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', 'css!./subtitlesync'], function (playbackManager, layoutManager, template, css) { - "use strict"; + 'use strict'; var player; var subtitleSyncSlider; @@ -13,35 +13,39 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', document.body.appendChild(parent); parent.innerHTML = template; - subtitleSyncSlider = parent.querySelector(".subtitleSyncSlider"); - subtitleSyncTextField = parent.querySelector(".subtitleSyncTextField"); - subtitleSyncCloseButton = parent.querySelector(".subtitleSync-closeButton"); - subtitleSyncContainer = parent.querySelector(".subtitleSyncContainer"); + subtitleSyncSlider = parent.querySelector('.subtitleSyncSlider'); + subtitleSyncTextField = parent.querySelector('.subtitleSyncTextField'); + subtitleSyncCloseButton = parent.querySelector('.subtitleSync-closeButton'); + subtitleSyncContainer = parent.querySelector('.subtitleSyncContainer'); if (layoutManager.tv) { - subtitleSyncSlider.classList.add("focusable"); + subtitleSyncSlider.classList.add('focusable'); // HACK: Delay to give time for registered element attach (Firefox) setTimeout(function () { subtitleSyncSlider.enableKeyboardDragging(); }, 0); } - subtitleSyncContainer.classList.add("hide"); + subtitleSyncContainer.classList.add('hide'); subtitleSyncTextField.updateOffset = function(offset) { - this.textContent = offset + "s"; + this.textContent = offset + 's'; }; - subtitleSyncTextField.addEventListener("keypress", function(event) { + subtitleSyncTextField.addEventListener('click', function () { + // keep focus to prevent fade with osd + this.hasFocus = true; + }); - if (event.key === "Enter") { + subtitleSyncTextField.addEventListener('keydown', function(event) { + if (event.key === 'Enter') { // if input key is enter search for float pattern var inputOffset = /[-+]?\d+\.?\d*/g.exec(this.textContent); if (inputOffset) { inputOffset = inputOffset[0]; // replace current text by considered offset - this.textContent = inputOffset + "s"; + this.textContent = inputOffset + 's'; inputOffset = parseFloat(inputOffset); // set new offset @@ -50,12 +54,12 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', subtitleSyncSlider.updateOffset( getPercentageFromOffset(inputOffset)); } else { - this.textContent = (playbackManager.getPlayerSubtitleOffset(player) || 0) + "s"; + this.textContent = (playbackManager.getPlayerSubtitleOffset(player) || 0) + 's'; } this.hasFocus = false; event.preventDefault(); } else { - // keep focus to prevent fade with bottom layout + // keep focus to prevent fade with osd this.hasFocus = true; if (event.key.match(/[+-\d.s]/) === null) { event.preventDefault(); @@ -63,12 +67,19 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', } }); + subtitleSyncTextField.blur = function() { + // prevent textfield to blur while element has focus + if (!this.hasFocus && this.prototype) { + this.prototype.blur(); + } + }; + subtitleSyncSlider.updateOffset = function(percent) { // default value is 0s = 50% this.value = percent === undefined ? 50 : percent; }; - subtitleSyncSlider.addEventListener("change", function () { + subtitleSyncSlider.addEventListener('change', function () { // set new offset playbackManager.setSubtitleOffset(getOffsetFromPercentage(this.value), player); // synchronize with textField value @@ -76,7 +87,7 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', getOffsetFromPercentage(this.value)); }); - subtitleSyncSlider.addEventListener("touchmove", function () { + subtitleSyncSlider.addEventListener('touchmove', function () { // set new offset playbackManager.setSubtitleOffset(getOffsetFromPercentage(this.value), player); // synchronize with textField value @@ -87,13 +98,13 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', subtitleSyncSlider.getBubbleHtml = function (value) { var newOffset = getOffsetFromPercentage(value); return '

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

"; + (newOffset > 0 ? '+' : '') + parseFloat(newOffset) + 's' + + ''; }; - subtitleSyncCloseButton.addEventListener("click", function() { + subtitleSyncCloseButton.addEventListener('click', function() { playbackManager.disableShowingSubtitleOffset(player); - SubtitleSync.prototype.toggle("forceToHide"); + SubtitleSync.prototype.toggle('forceToHide'); }); instance.element = parent; @@ -122,7 +133,7 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', } SubtitleSync.prototype.destroy = function() { - SubtitleSync.prototype.toggle("forceToHide"); + SubtitleSync.prototype.toggle('forceToHide'); if (player) { playbackManager.disableShowingSubtitleOffset(player); playbackManager.setSubtitleOffset(0, player); @@ -140,28 +151,26 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', /* eslint-disable no-fallthrough */ switch (action) { case undefined: - // if showing subtitle sync is enabled - if (playbackManager.isShowingSubtitleOffsetEnabled(player) && - // if there is an external subtitle stream enabled - playbackManager.canHandleOffsetOnCurrentSubtitle(player)) { - // if no subtitle offset is defined - if (!playbackManager.getPlayerSubtitleOffset(player)) { + // if showing subtitle sync is enabled and if there is an external subtitle stream enabled + if (playbackManager.isShowingSubtitleOffsetEnabled(player) && playbackManager.canHandleOffsetOnCurrentSubtitle(player)) { + // if no subtitle offset is defined or element has focus (offset being defined) + if (!(playbackManager.getPlayerSubtitleOffset(player) || subtitleSyncTextField.hasFocus)) { // set default offset to '0' = 50% - subtitleSyncSlider.value = "50"; - subtitleSyncTextField.textContent = "0s"; + subtitleSyncSlider.value = '50'; + subtitleSyncTextField.textContent = '0s'; playbackManager.setSubtitleOffset(0, player); } // show subtitle sync - subtitleSyncContainer.classList.remove("hide"); + subtitleSyncContainer.classList.remove('hide'); break; // stop here } // else continue and hide - case "hide": + case 'hide': // only break if element has focus if (subtitleSyncTextField.hasFocus) { break; } - case "forceToHide": - subtitleSyncContainer.classList.add("hide"); + case 'forceToHide': + subtitleSyncContainer.classList.add('hide'); break; } /* eslint-enable no-fallthrough */ diff --git a/src/components/subtitlesync/subtitlesync.template.html b/src/components/subtitlesync/subtitlesync.template.html index 999e4d0bb9..4ca039aa03 100644 --- a/src/components/subtitlesync/subtitlesync.template.html +++ b/src/components/subtitlesync/subtitlesync.template.html @@ -1,5 +1,5 @@
- +
0s
diff --git a/src/components/syncplay/groupSelectionMenu.js b/src/components/syncplay/groupSelectionMenu.js new file mode 100644 index 0000000000..067100ad73 --- /dev/null +++ b/src/components/syncplay/groupSelectionMenu.js @@ -0,0 +1,189 @@ +import events from 'events'; +import connectionManager from 'connectionManager'; +import playbackManager from 'playbackManager'; +import syncPlayManager from 'syncPlayManager'; +import loading from 'loading'; +import toast from 'toast'; +import actionsheet from 'actionsheet'; +import globalize from 'globalize'; +import playbackPermissionManager from 'playbackPermissionManager'; + +/** + * Gets active player id. + * @returns {string} The player's id. + */ +function getActivePlayerId () { + var info = playbackManager.getPlayerInfo(); + return info ? info.id : null; +} + +/** + * Used when user needs to join a group. + * @param {HTMLElement} button - Element where to place the menu. + * @param {Object} user - Current user. + * @param {Object} apiClient - ApiClient. + */ +function showNewJoinGroupSelection (button, user, apiClient) { + const sessionId = getActivePlayerId() || 'none'; + const inSession = sessionId !== 'none'; + const policy = user.localUser ? user.localUser.Policy : {}; + let playingItemId; + try { + const playState = playbackManager.getPlayerState(); + playingItemId = playState.NowPlayingItem.Id; + console.debug('Item', playingItemId, 'is currently playing.'); + } catch (error) { + playingItemId = ''; + console.debug('No item is currently playing.'); + } + + apiClient.sendSyncPlayCommand(sessionId, 'ListGroups').then(function (response) { + response.json().then(function (groups) { + var menuItems = groups.map(function (group) { + return { + name: group.PlayingItemName, + icon: 'group', + id: group.GroupId, + selected: false, + secondaryText: group.Participants.join(', ') + }; + }); + + if (inSession && policy.SyncPlayAccess === 'CreateAndJoinGroups') { + menuItems.push({ + name: globalize.translate('LabelSyncPlayNewGroup'), + icon: 'add', + id: 'new-group', + selected: true, + secondaryText: globalize.translate('LabelSyncPlayNewGroupDescription') + }); + } + + if (menuItems.length === 0) { + if (inSession && policy.SyncPlayAccess === 'JoinGroups') { + toast({ + text: globalize.translate('MessageSyncPlayCreateGroupDenied') + }); + } else { + toast({ + text: globalize.translate('MessageSyncPlayNoGroupsAvailable') + }); + } + loading.hide(); + return; + } + + var menuOptions = { + title: globalize.translate('HeaderSyncPlaySelectGroup'), + items: menuItems, + positionTo: button, + resolveOnClick: true, + border: true + }; + + actionsheet.show(menuOptions).then(function (id) { + if (id == 'new-group') { + apiClient.sendSyncPlayCommand(sessionId, 'NewGroup'); + } else { + apiClient.sendSyncPlayCommand(sessionId, 'JoinGroup', { + GroupId: id, + PlayingItemId: playingItemId + }); + } + }).catch((error) => { + console.error('SyncPlay: unexpected error listing groups:', error); + }); + + loading.hide(); + }); + }).catch(function (error) { + console.error(error); + loading.hide(); + toast({ + text: globalize.translate('MessageSyncPlayErrorAccessingGroups') + }); + }); +} + +/** + * Used when user has joined a group. + * @param {HTMLElement} button - Element where to place the menu. + * @param {Object} user - Current user. + * @param {Object} apiClient - ApiClient. + */ +function showLeaveGroupSelection (button, user, apiClient) { + const sessionId = getActivePlayerId(); + if (!sessionId) { + syncPlayManager.signalError(); + toast({ + text: globalize.translate('MessageSyncPlayErrorNoActivePlayer') + }); + showNewJoinGroupSelection(button, user, apiClient); + return; + } + + const menuItems = [{ + name: globalize.translate('LabelSyncPlayLeaveGroup'), + icon: 'meeting_room', + id: 'leave-group', + selected: true, + secondaryText: globalize.translate('LabelSyncPlayLeaveGroupDescription') + }]; + + var menuOptions = { + title: globalize.translate('HeaderSyncPlayEnabled'), + items: menuItems, + positionTo: button, + resolveOnClick: true, + border: true + }; + + actionsheet.show(menuOptions).then(function (id) { + if (id == 'leave-group') { + apiClient.sendSyncPlayCommand(sessionId, 'LeaveGroup'); + } + }).catch((error) => { + console.error('SyncPlay: unexpected error showing group menu:', error); + }); + + loading.hide(); +} + +// Register to SyncPlay events +let syncPlayEnabled = false; +events.on(syncPlayManager, 'enabled', function (e, enabled) { + syncPlayEnabled = enabled; +}); + +/** + * Shows a menu to handle SyncPlay groups. + * @param {HTMLElement} button - Element where to place the menu. + */ +export function show (button) { + loading.show(); + + // TODO: should feature be disabled if playback permission is missing? + playbackPermissionManager.check().then(() => { + console.debug('Playback is allowed.'); + }).catch((error) => { + console.error('Playback not allowed!', error); + toast({ + text: globalize.translate('MessageSyncPlayPlaybackPermissionRequired') + }); + }); + + const apiClient = connectionManager.currentApiClient(); + connectionManager.user(apiClient).then((user) => { + if (syncPlayEnabled) { + showLeaveGroupSelection(button, user, apiClient); + } else { + showNewJoinGroupSelection(button, user, apiClient); + } + }).catch((error) => { + console.error(error); + loading.hide(); + toast({ + text: globalize.translate('MessageSyncPlayNoGroupsAvailable') + }); + }); +} diff --git a/src/components/syncplay/playbackPermissionManager.js b/src/components/syncplay/playbackPermissionManager.js new file mode 100644 index 0000000000..3c258ad18d --- /dev/null +++ b/src/components/syncplay/playbackPermissionManager.js @@ -0,0 +1,51 @@ +/** + * Creates an audio element that plays a silent sound. + * @returns {HTMLMediaElement} The audio element. + */ +function createTestMediaElement () { + + const elem = document.createElement('audio'); + elem.classList.add('testMediaPlayerAudio'); + elem.classList.add('hide'); + + document.body.appendChild(elem); + + elem.volume = 1; // Volume should not be zero to trigger proper permissions + elem.src = 'assets/audio/silence.mp3'; // Silent sound + + return elem; +} + +/** + * Destroys a media element. + * @param {HTMLMediaElement} elem The element to destroy. + */ +function destroyTestMediaElement (elem) { + elem.pause(); + elem.remove(); +} + +/** + * Class that manages the playback permission. + */ +class PlaybackPermissionManager { + /** + * Tests playback permission. Grabs the permission when called inside a click event (or any other valid user interaction). + * @returns {Promise} Promise that resolves succesfully if playback permission is allowed. + */ + check () { + return new Promise((resolve, reject) => { + const media = createTestMediaElement(); + media.play().then(() => { + resolve(); + }).catch((error) => { + reject(error); + }).finally(() => { + destroyTestMediaElement(media); + }); + }); + } +} + +/** PlaybackPermissionManager singleton. */ +export default new PlaybackPermissionManager(); diff --git a/src/components/syncplay/syncPlayManager.js b/src/components/syncplay/syncPlayManager.js new file mode 100644 index 0000000000..f04d1aeb8c --- /dev/null +++ b/src/components/syncplay/syncPlayManager.js @@ -0,0 +1,838 @@ +/** + * Module that manages the SyncPlay feature. + * @module components/syncplay/syncPlayManager + */ + +import events from 'events'; +import connectionManager from 'connectionManager'; +import playbackManager from 'playbackManager'; +import timeSyncManager from 'timeSyncManager'; +import toast from 'toast'; +import globalize from 'globalize'; + +/** + * Waits for an event to be triggered on an object. An optional timeout can specified after which the promise is rejected. + * @param {Object} emitter Object on which to listen for events. + * @param {string} eventType Event name to listen for. + * @param {number} timeout Time in milliseconds before rejecting promise if event does not trigger. + * @returns {Promise} A promise that resolves when the event is triggered. + */ +function waitForEventOnce(emitter, eventType, timeout) { + return new Promise((resolve, reject) => { + let rejectTimeout; + if (timeout) { + rejectTimeout = setTimeout(() => { + reject('Timed out.'); + }, timeout); + } + const callback = () => { + events.off(emitter, eventType, callback); + if (rejectTimeout) { + clearTimeout(rejectTimeout); + } + resolve(arguments); + }; + events.on(emitter, eventType, callback); + }); +} + +/** + * Gets active player id. + * @returns {string} The player's id. + */ +function getActivePlayerId() { + var info = playbackManager.getPlayerInfo(); + return info ? info.id : null; +} + +/** + * Playback synchronization + */ +const MaxAcceptedDelaySpeedToSync = 50; // milliseconds, delay after which SpeedToSync is enabled +const MaxAcceptedDelaySkipToSync = 300; // milliseconds, delay after which SkipToSync is enabled +const SyncMethodThreshold = 2000; // milliseconds, switches between SpeedToSync or SkipToSync +const SpeedToSyncTime = 1000; // milliseconds, duration in which the playback is sped up +const MaxAttemptsSpeedToSync = 3; // attempts before disabling SpeedToSync +const MaxAttemptsSync = 5; // attempts before disabling syncing at all + +/** + * Other constants + */ +const WaitForEventDefaultTimeout = 30000; // milliseconds +const WaitForPlayerEventTimeout = 500; // milliseconds + +/** + * Class that manages the SyncPlay feature. + */ +class SyncPlayManager { + constructor() { + this.playbackRateSupported = false; + this.syncEnabled = false; + this.playbackDiffMillis = 0; // used for stats + this.syncMethod = 'None'; // used for stats + this.syncAttempts = 0; + this.lastSyncTime = new Date(); + this.syncWatcherTimeout = null; // interval that watches playback time and syncs it + + this.lastPlaybackWaiting = null; // used to determine if player's buffering + this.minBufferingThresholdMillis = 1000; + + this.currentPlayer = null; + this.localPlayerPlaybackRate = 1.0; // used to restore user PlaybackRate + + this.syncPlayEnabledAt = null; // Server time of when SyncPlay has been enabled + this.syncPlayReady = false; // SyncPlay is ready after first ping to server + + this.lastCommand = null; + this.queuedCommand = null; + + this.scheduledCommand = null; + this.syncTimeout = null; + + this.timeOffsetWithServer = 0; // server time minus local time + this.roundTripDuration = 0; + this.notifySyncPlayReady = false; + + events.on(playbackManager, 'playbackstart', (player, state) => { + this.onPlaybackStart(player, state); + }); + + events.on(playbackManager, 'playbackstop', (stopInfo) => { + this.onPlaybackStop(stopInfo); + }); + + events.on(playbackManager, 'playerchange', () => { + this.onPlayerChange(); + }); + + this.bindToPlayer(playbackManager.getCurrentPlayer()); + + events.on(this, 'timeupdate', (event) => { + this.syncPlaybackTime(); + }); + + events.on(timeSyncManager, 'update', (event, error, timeOffset, ping) => { + if (error) { + console.debug('SyncPlay, time update issue', error); + return; + } + + this.timeOffsetWithServer = timeOffset; + this.roundTripDuration = ping * 2; + + if (this.notifySyncPlayReady) { + this.syncPlayReady = true; + events.trigger(this, 'ready'); + this.notifySyncPlayReady = false; + } + + // Report ping + if (this.syncEnabled) { + const apiClient = connectionManager.currentApiClient(); + const sessionId = getActivePlayerId(); + + if (!sessionId) { + this.signalError(); + toast({ + text: globalize.translate('MessageSyncPlayErrorMissingSession') + }); + return; + } + + apiClient.sendSyncPlayCommand(sessionId, 'UpdatePing', { + Ping: ping + }); + } + }); + } + + /** + * Called when playback starts. + */ + onPlaybackStart (player, state) { + events.trigger(this, 'playbackstart', [player, state]); + } + + /** + * Called when playback stops. + */ + onPlaybackStop (stopInfo) { + events.trigger(this, 'playbackstop', [stopInfo]); + if (this.isSyncPlayEnabled()) { + this.disableSyncPlay(false); + } + } + + /** + * Called when the player changes. + */ + onPlayerChange () { + this.bindToPlayer(playbackManager.getCurrentPlayer()); + events.trigger(this, 'playerchange', [this.currentPlayer]); + } + + /** + * Called when playback unpauses. + */ + onPlayerUnpause () { + events.trigger(this, 'unpause', [this.currentPlayer]); + } + + /** + * Called when playback pauses. + */ + onPlayerPause() { + events.trigger(this, 'pause', [this.currentPlayer]); + } + + /** + * Called on playback progress. + * @param {Object} e The time update event. + */ + onTimeUpdate (e) { + // NOTICE: this event is unreliable, at least in Safari + // which just stops firing the event after a while. + events.trigger(this, 'timeupdate', [e]); + } + + /** + * Called when playback is resumed. + */ + onPlaying () { + // TODO: implement group wait + this.lastPlaybackWaiting = null; + events.trigger(this, 'playing'); + } + + /** + * Called when playback is buffering. + */ + onWaiting () { + // TODO: implement group wait + if (!this.lastPlaybackWaiting) { + this.lastPlaybackWaiting = new Date(); + } + events.trigger(this, 'waiting'); + } + + /** + * Gets playback buffering status. + * @returns {boolean} _true_ if player is buffering, _false_ otherwise. + */ + isBuffering () { + if (this.lastPlaybackWaiting === null) return false; + return (new Date() - this.lastPlaybackWaiting) > this.minBufferingThresholdMillis; + } + + /** + * Binds to the player's events. + * @param {Object} player The player. + */ + bindToPlayer (player) { + if (player !== this.currentPlayer) { + this.releaseCurrentPlayer(); + this.currentPlayer = player; + if (!player) return; + } + + // FIXME: the following are needed because the 'events' module + // is changing the scope when executing the callbacks. + // For instance, calling 'onPlayerUnpause' from the wrong scope breaks things because 'this' + // points to 'player' (the event emitter) instead of pointing to the SyncPlayManager singleton. + const self = this; + this._onPlayerUnpause = () => { + self.onPlayerUnpause(); + }; + + this._onPlayerPause = () => { + self.onPlayerPause(); + }; + + this._onTimeUpdate = (e) => { + self.onTimeUpdate(e); + }; + + this._onPlaying = () => { + self.onPlaying(); + }; + + this._onWaiting = () => { + self.onWaiting(); + }; + + events.on(player, 'unpause', this._onPlayerUnpause); + events.on(player, 'pause', this._onPlayerPause); + events.on(player, 'timeupdate', this._onTimeUpdate); + events.on(player, 'playing', this._onPlaying); + events.on(player, 'waiting', this._onWaiting); + + // Save player current PlaybackRate value + if (player.supports && player.supports('PlaybackRate')) { + this.localPlayerPlaybackRate = player.getPlaybackRate(); + } + } + + /** + * Removes the bindings to the current player's events. + */ + releaseCurrentPlayer () { + var player = this.currentPlayer; + if (player) { + events.off(player, 'unpause', this._onPlayerUnpause); + events.off(player, 'pause', this._onPlayerPause); + events.off(player, 'timeupdate', this._onTimeUpdate); + events.off(player, 'playing', this._onPlaying); + events.off(player, 'waiting', this._onWaiting); + // Restore player original PlaybackRate value + if (this.playbackRateSupported) { + player.setPlaybackRate(this.localPlayerPlaybackRate); + this.localPlayerPlaybackRate = 1.0; + } + this.currentPlayer = null; + this.playbackRateSupported = false; + } + } + + /** + * Handles a group update from the server. + * @param {Object} cmd The group update. + * @param {Object} apiClient The ApiClient. + */ + processGroupUpdate (cmd, apiClient) { + switch (cmd.Type) { + case 'PrepareSession': + this.prepareSession(apiClient, cmd.GroupId, cmd.Data); + break; + case 'UserJoined': + toast({ + text: globalize.translate('MessageSyncPlayUserJoined', cmd.Data) + }); + break; + case 'UserLeft': + toast({ + text: globalize.translate('MessageSyncPlayUserLeft', cmd.Data) + }); + break; + case 'GroupJoined': + this.enableSyncPlay(apiClient, new Date(cmd.Data), true); + break; + case 'NotInGroup': + case 'GroupLeft': + this.disableSyncPlay(true); + break; + case 'GroupWait': + toast({ + text: globalize.translate('MessageSyncPlayGroupWait', cmd.Data) + }); + break; + case 'GroupDoesNotExist': + toast({ + text: globalize.translate('MessageSyncPlayGroupDoesNotExist') + }); + break; + case 'CreateGroupDenied': + toast({ + text: globalize.translate('MessageSyncPlayCreateGroupDenied') + }); + break; + case 'JoinGroupDenied': + toast({ + text: globalize.translate('MessageSyncPlayJoinGroupDenied') + }); + break; + case 'LibraryAccessDenied': + toast({ + text: globalize.translate('MessageSyncPlayLibraryAccessDenied') + }); + break; + default: + console.error('processSyncPlayGroupUpdate: command is not recognised: ' + cmd.Type); + break; + } + } + + /** + * Handles a playback command from the server. + * @param {Object} cmd The playback command. + * @param {Object} apiClient The ApiClient. + */ + processCommand (cmd, apiClient) { + if (cmd === null) return; + + if (!this.isSyncPlayEnabled()) { + console.debug('SyncPlay processCommand: SyncPlay not enabled, ignoring command', cmd); + return; + } + + if (!this.syncPlayReady) { + console.debug('SyncPlay processCommand: SyncPlay not ready, queued command', cmd); + this.queuedCommand = cmd; + return; + } + + cmd.When = new Date(cmd.When); + cmd.EmittedAt = new Date(cmd.EmitttedAt); + + if (cmd.EmitttedAt < this.syncPlayEnabledAt) { + console.debug('SyncPlay processCommand: ignoring old command', cmd); + return; + } + + // Check if new command differs from last one + if (this.lastCommand && + this.lastCommand.When === cmd.When && + this.lastCommand.PositionTicks === cmd.PositionTicks && + this.Command === cmd.Command + ) { + console.debug('SyncPlay processCommand: ignoring duplicate command', cmd); + return; + } + + this.lastCommand = cmd; + console.log('SyncPlay will', cmd.Command, 'at', cmd.When, 'PositionTicks', cmd.PositionTicks); + + switch (cmd.Command) { + case 'Play': + this.schedulePlay(cmd.When, cmd.PositionTicks); + break; + case 'Pause': + this.schedulePause(cmd.When, cmd.PositionTicks); + break; + case 'Seek': + this.scheduleSeek(cmd.When, cmd.PositionTicks); + break; + default: + console.error('processCommand: command is not recognised: ' + cmd.Type); + break; + } + } + + /** + * Prepares this client to join a group by loading the required content. + * @param {Object} apiClient The ApiClient. + * @param {string} groupId The group to join. + * @param {Object} sessionData Info about the content to load. + */ + prepareSession (apiClient, groupId, sessionData) { + const serverId = apiClient.serverInfo().Id; + playbackManager.play({ + ids: sessionData.ItemIds, + startPositionTicks: sessionData.StartPositionTicks, + mediaSourceId: sessionData.MediaSourceId, + audioStreamIndex: sessionData.AudioStreamIndex, + subtitleStreamIndex: sessionData.SubtitleStreamIndex, + startIndex: sessionData.StartIndex, + serverId: serverId + }).then(() => { + waitForEventOnce(this, 'playbackstart', WaitForEventDefaultTimeout).then(() => { + var sessionId = getActivePlayerId(); + if (!sessionId) { + console.error('Missing sessionId!'); + toast({ + text: globalize.translate('MessageSyncPlayErrorMissingSession') + }); + return; + } + // Get playing item id + let playingItemId; + try { + const playState = playbackManager.getPlayerState(); + playingItemId = playState.NowPlayingItem.Id; + } catch (error) { + playingItemId = ''; + } + // Make sure the server has received the player state + waitForEventOnce(playbackManager, 'reportplayback', WaitForEventDefaultTimeout).then((success) => { + this.localPause(); + if (!success) { + console.warning('Error reporting playback state to server. Joining group will fail.'); + } + apiClient.sendSyncPlayCommand(sessionId, 'JoinGroup', { + GroupId: groupId, + PlayingItemId: playingItemId + }); + }).catch(() => { + console.error('Timed out while waiting for `reportplayback` event!'); + toast({ + text: globalize.translate('MessageSyncPlayErrorMedia') + }); + return; + }); + }).catch(() => { + console.error('Timed out while waiting for `playbackstart` event!'); + if (!this.isSyncPlayEnabled()) { + toast({ + text: globalize.translate('MessageSyncPlayErrorMedia') + }); + } + return; + }); + }).catch((error) => { + console.error(error); + toast({ + text: globalize.translate('MessageSyncPlayErrorMedia') + }); + }); + } + + /** + * Enables SyncPlay. + * @param {Object} apiClient The ApiClient. + * @param {Date} enabledAt When SyncPlay has been enabled. Server side date. + * @param {boolean} showMessage Display message. + */ + enableSyncPlay (apiClient, enabledAt, showMessage = false) { + this.syncPlayEnabledAt = enabledAt; + this.injectPlaybackManager(); + events.trigger(this, 'enabled', [true]); + + waitForEventOnce(this, 'ready').then(() => { + this.processCommand(this.queuedCommand, apiClient); + this.queuedCommand = null; + }); + + this.syncPlayReady = false; + this.notifySyncPlayReady = true; + + timeSyncManager.forceUpdate(); + + if (showMessage) { + toast({ + text: globalize.translate('MessageSyncPlayEnabled') + }); + } + } + + /** + * Disables SyncPlay. + * @param {boolean} showMessage Display message. + */ + disableSyncPlay (showMessage = false) { + this.syncPlayEnabledAt = null; + this.syncPlayReady = false; + this.lastCommand = null; + this.queuedCommand = null; + this.syncEnabled = false; + events.trigger(this, 'enabled', [false]); + this.restorePlaybackManager(); + + if (showMessage) { + toast({ + text: globalize.translate('MessageSyncPlayDisabled') + }); + } + } + + /** + * Gets SyncPlay status. + * @returns {boolean} _true_ if user joined a group, _false_ otherwise. + */ + isSyncPlayEnabled () { + return this.syncPlayEnabledAt !== null; + } + + /** + * Schedules a resume playback on the player at the specified clock time. + * @param {Date} playAtTime The server's UTC time at which to resume playback. + * @param {number} positionTicks The PositionTicks from where to resume. + */ + schedulePlay (playAtTime, positionTicks) { + this.clearScheduledCommand(); + const currentTime = new Date(); + const playAtTimeLocal = timeSyncManager.serverDateToLocal(playAtTime); + + if (playAtTimeLocal > currentTime) { + const playTimeout = playAtTimeLocal - currentTime; + this.localSeek(positionTicks); + + this.scheduledCommand = setTimeout(() => { + this.localUnpause(); + + this.syncTimeout = setTimeout(() => { + this.syncEnabled = true; + }, SyncMethodThreshold / 2); + + }, playTimeout); + + console.debug('Scheduled play in', playTimeout / 1000.0, 'seconds.'); + } else { + // Group playback already started + const serverPositionTicks = positionTicks + (currentTime - playAtTimeLocal) * 10000; + waitForEventOnce(this, 'unpause').then(() => { + this.localSeek(serverPositionTicks); + }); + this.localUnpause(); + + this.syncTimeout = setTimeout(() => { + this.syncEnabled = true; + }, SyncMethodThreshold / 2); + } + } + + /** + * Schedules a pause playback on the player at the specified clock time. + * @param {Date} pauseAtTime The server's UTC time at which to pause playback. + * @param {number} positionTicks The PositionTicks where player will be paused. + */ + schedulePause (pauseAtTime, positionTicks) { + this.clearScheduledCommand(); + const currentTime = new Date(); + const pauseAtTimeLocal = timeSyncManager.serverDateToLocal(pauseAtTime); + + const callback = () => { + waitForEventOnce(this, 'pause', WaitForPlayerEventTimeout).then(() => { + this.localSeek(positionTicks); + }).catch(() => { + // Player was already paused, seeking + this.localSeek(positionTicks); + }); + this.localPause(); + }; + + if (pauseAtTimeLocal > currentTime) { + const pauseTimeout = pauseAtTimeLocal - currentTime; + this.scheduledCommand = setTimeout(callback, pauseTimeout); + + console.debug('Scheduled pause in', pauseTimeout / 1000.0, 'seconds.'); + } else { + callback(); + } + } + + /** + * Schedules a seek playback on the player at the specified clock time. + * @param {Date} pauseAtTime The server's UTC time at which to seek playback. + * @param {number} positionTicks The PositionTicks where player will be seeked. + */ + scheduleSeek (seekAtTime, positionTicks) { + this.schedulePause(seekAtTime, positionTicks); + } + + /** + * Clears the current scheduled command. + */ + clearScheduledCommand () { + clearTimeout(this.scheduledCommand); + clearTimeout(this.syncTimeout); + + this.syncEnabled = false; + if (this.currentPlayer) { + this.currentPlayer.setPlaybackRate(1); + } + this.clearSyncIcon(); + } + + /** + * Overrides some PlaybackManager's methods to intercept playback commands. + */ + injectPlaybackManager () { + if (!this.isSyncPlayEnabled()) return; + if (playbackManager.syncPlayEnabled) return; + + // TODO: make this less hacky + playbackManager._localUnpause = playbackManager.unpause; + playbackManager._localPause = playbackManager.pause; + playbackManager._localSeek = playbackManager.seek; + + playbackManager.unpause = this.playRequest; + playbackManager.pause = this.pauseRequest; + playbackManager.seek = this.seekRequest; + playbackManager.syncPlayEnabled = true; + } + + /** + * Restores original PlaybackManager's methods. + */ + restorePlaybackManager () { + if (this.isSyncPlayEnabled()) return; + if (!playbackManager.syncPlayEnabled) return; + + playbackManager.unpause = playbackManager._localUnpause; + playbackManager.pause = playbackManager._localPause; + playbackManager.seek = playbackManager._localSeek; + playbackManager.syncPlayEnabled = false; + } + + /** + * Overrides PlaybackManager's unpause method. + */ + playRequest (player) { + var apiClient = connectionManager.currentApiClient(); + var sessionId = getActivePlayerId(); + apiClient.sendSyncPlayCommand(sessionId, 'PlayRequest'); + } + + /** + * Overrides PlaybackManager's pause method. + */ + pauseRequest (player) { + var apiClient = connectionManager.currentApiClient(); + var sessionId = getActivePlayerId(); + apiClient.sendSyncPlayCommand(sessionId, 'PauseRequest'); + // Pause locally as well, to give the user some little control + playbackManager._localUnpause(player); + } + + /** + * Overrides PlaybackManager's seek method. + */ + seekRequest (PositionTicks, player) { + var apiClient = connectionManager.currentApiClient(); + var sessionId = getActivePlayerId(); + apiClient.sendSyncPlayCommand(sessionId, 'SeekRequest', { + PositionTicks: PositionTicks + }); + } + + /** + * Calls original PlaybackManager's unpause method. + */ + localUnpause(player) { + if (playbackManager.syncPlayEnabled) { + playbackManager._localUnpause(player); + } else { + playbackManager.unpause(player); + } + } + + /** + * Calls original PlaybackManager's pause method. + */ + localPause(player) { + if (playbackManager.syncPlayEnabled) { + playbackManager._localPause(player); + } else { + playbackManager.pause(player); + } + } + + /** + * Calls original PlaybackManager's seek method. + */ + localSeek(PositionTicks, player) { + if (playbackManager.syncPlayEnabled) { + playbackManager._localSeek(PositionTicks, player); + } else { + playbackManager.seek(PositionTicks, player); + } + } + + /** + * Attempts to sync playback time with estimated server time. + * + * When sync is enabled, the following will be checked: + * - check if local playback time is close enough to the server playback time + * If it is not, then a playback time sync will be attempted. + * Two methods of syncing are available: + * - SpeedToSync: speeds up the media for some time to catch up (default is one second) + * - SkipToSync: seeks the media to the estimated correct time + * SpeedToSync aims to reduce the delay as much as possible, whereas SkipToSync is less pretentious. + */ + syncPlaybackTime () { + // Attempt to sync only when media is playing. + if (!this.lastCommand || this.lastCommand.Command !== 'Play' || this.isBuffering()) return; + + const currentTime = new Date(); + + // Avoid overloading the browser + const elapsed = currentTime - this.lastSyncTime; + if (elapsed < SyncMethodThreshold / 2) return; + this.lastSyncTime = currentTime; + + const playAtTime = this.lastCommand.When; + + const currentPositionTicks = playbackManager.currentTime(); + // Estimate PositionTicks on server + const serverPositionTicks = this.lastCommand.PositionTicks + ((currentTime - playAtTime) + this.timeOffsetWithServer) * 10000; + // Measure delay that needs to be recovered + // diff might be caused by the player internally starting the playback + const diffMillis = (serverPositionTicks - currentPositionTicks) / 10000.0; + + this.playbackDiffMillis = diffMillis; + + if (this.syncEnabled) { + const absDiffMillis = Math.abs(diffMillis); + // TODO: SpeedToSync sounds bad on songs + // TODO: SpeedToSync is failing on Safari (Mojave); even if playbackRate is supported, some delay seems to exist + if (this.playbackRateSupported && absDiffMillis > MaxAcceptedDelaySpeedToSync && absDiffMillis < SyncMethodThreshold) { + // Disable SpeedToSync if it keeps failing + if (this.syncAttempts > MaxAttemptsSpeedToSync) { + this.playbackRateSupported = false; + } + // SpeedToSync method + const speed = 1 + diffMillis / SpeedToSyncTime; + + this.currentPlayer.setPlaybackRate(speed); + this.syncEnabled = false; + this.syncAttempts++; + this.showSyncIcon('SpeedToSync (x' + speed + ')'); + + this.syncTimeout = setTimeout(() => { + this.currentPlayer.setPlaybackRate(1); + this.syncEnabled = true; + this.clearSyncIcon(); + }, SpeedToSyncTime); + } else if (absDiffMillis > MaxAcceptedDelaySkipToSync) { + // Disable SkipToSync if it keeps failing + if (this.syncAttempts > MaxAttemptsSync) { + this.syncEnabled = false; + this.showSyncIcon('Sync disabled (too many attempts)'); + } + // SkipToSync method + this.localSeek(serverPositionTicks); + this.syncEnabled = false; + this.syncAttempts++; + this.showSyncIcon('SkipToSync (' + this.syncAttempts + ')'); + + this.syncTimeout = setTimeout(() => { + this.syncEnabled = true; + this.clearSyncIcon(); + }, SyncMethodThreshold / 2); + } else { + // Playback is synced + if (this.syncAttempts > 0) { + console.debug('Playback has been synced after', this.syncAttempts, 'attempts.'); + } + this.syncAttempts = 0; + } + } + } + + /** + * Gets SyncPlay stats. + * @returns {Object} The SyncPlay stats. + */ + getStats () { + return { + TimeOffset: this.timeOffsetWithServer, + PlaybackDiff: this.playbackDiffMillis, + SyncMethod: this.syncMethod + }; + } + + /** + * Emits an event to update the SyncPlay status icon. + */ + showSyncIcon (syncMethod) { + this.syncMethod = syncMethod; + events.trigger(this, 'syncing', [true, this.syncMethod]); + } + + /** + * Emits an event to clear the SyncPlay status icon. + */ + clearSyncIcon () { + this.syncMethod = 'None'; + events.trigger(this, 'syncing', [false, this.syncMethod]); + } + + /** + * Signals an error state, which disables and resets SyncPlay for a new session. + */ + signalError () { + this.disableSyncPlay(); + } +} + +/** SyncPlayManager singleton. */ +export default new SyncPlayManager(); diff --git a/src/components/syncplay/timeSyncManager.js b/src/components/syncplay/timeSyncManager.js new file mode 100644 index 0000000000..ca92939576 --- /dev/null +++ b/src/components/syncplay/timeSyncManager.js @@ -0,0 +1,207 @@ +/** + * Module that manages time syncing with server. + * @module components/syncplay/timeSyncManager + */ + +import events from 'events'; +import connectionManager from 'connectionManager'; + +/** + * Time estimation + */ +const NumberOfTrackedMeasurements = 8; +const PollingIntervalGreedy = 1000; // milliseconds +const PollingIntervalLowProfile = 60000; // milliseconds +const GreedyPingCount = 3; + +/** + * Class that stores measurement data. + */ +class Measurement { + /** + * Creates a new measurement. + * @param {Date} requestSent Client's timestamp of the request transmission + * @param {Date} requestReceived Server's timestamp of the request reception + * @param {Date} responseSent Server's timestamp of the response transmission + * @param {Date} responseReceived Client's timestamp of the response reception + */ + constructor(requestSent, requestReceived, responseSent, responseReceived) { + this.requestSent = requestSent.getTime(); + this.requestReceived = requestReceived.getTime(); + this.responseSent = responseSent.getTime(); + this.responseReceived = responseReceived.getTime(); + } + + /** + * Time offset from server. + */ + getOffset () { + return ((this.requestReceived - this.requestSent) + (this.responseSent - this.responseReceived)) / 2; + } + + /** + * Get round-trip delay. + */ + getDelay () { + return (this.responseReceived - this.requestSent) - (this.responseSent - this.requestReceived); + } + + /** + * Get ping time. + */ + getPing () { + return this.getDelay() / 2; + } +} + +/** + * Class that manages time syncing with server. + */ +class TimeSyncManager { + constructor() { + this.pingStop = true; + this.pollingInterval = PollingIntervalGreedy; + this.poller = null; + this.pings = 0; // number of pings + this.measurement = null; // current time sync + this.measurements = []; + + this.startPing(); + } + + /** + * Gets status of time sync. + * @returns {boolean} _true_ if a measurement has been done, _false_ otherwise. + */ + isReady() { + return !!this.measurement; + } + + /** + * Gets time offset with server. + * @returns {number} The time offset. + */ + getTimeOffset () { + return this.measurement ? this.measurement.getOffset() : 0; + } + + /** + * Gets ping time to server. + * @returns {number} The ping time. + */ + getPing () { + return this.measurement ? this.measurement.getPing() : 0; + } + + /** + * Updates time offset between server and client. + * @param {Measurement} measurement The new measurement. + */ + updateTimeOffset(measurement) { + this.measurements.push(measurement); + if (this.measurements.length > NumberOfTrackedMeasurements) { + this.measurements.shift(); + } + + // Pick measurement with minimum delay + const sortedMeasurements = this.measurements.slice(0); + sortedMeasurements.sort((a, b) => a.getDelay() - b.getDelay()); + this.measurement = sortedMeasurements[0]; + } + + /** + * Schedules a ping request to the server. Triggers time offset update. + */ + requestPing() { + if (!this.poller) { + this.poller = setTimeout(() => { + this.poller = null; + const apiClient = connectionManager.currentApiClient(); + const requestSent = new Date(); + apiClient.getServerTime().then((response) => { + const responseReceived = new Date(); + response.json().then((data) => { + const requestReceived = new Date(data.RequestReceptionTime); + const responseSent = new Date(data.ResponseTransmissionTime); + + const measurement = new Measurement(requestSent, requestReceived, responseSent, responseReceived); + this.updateTimeOffset(measurement); + + // Avoid overloading server + if (this.pings >= GreedyPingCount) { + this.pollingInterval = PollingIntervalLowProfile; + } else { + this.pings++; + } + + events.trigger(this, 'update', [null, this.getTimeOffset(), this.getPing()]); + }); + }).catch((error) => { + console.error(error); + events.trigger(this, 'update', [error, null, null]); + }).finally(() => { + this.requestPing(); + }); + + }, this.pollingInterval); + } + } + + /** + * Drops accumulated measurements. + */ + resetMeasurements () { + this.measurement = null; + this.measurements = []; + } + + /** + * Starts the time poller. + */ + startPing() { + this.requestPing(); + } + + /** + * Stops the time poller. + */ + stopPing() { + if (this.poller) { + clearTimeout(this.poller); + this.poller = null; + } + } + + /** + * Resets poller into greedy mode. + */ + forceUpdate() { + this.stopPing(); + this.pollingInterval = PollingIntervalGreedy; + this.pings = 0; + this.startPing(); + } + + /** + * Converts server time to local time. + * @param {Date} server The time to convert. + * @returns {Date} Local time. + */ + serverDateToLocal(server) { + // server - local = offset + return new Date(server.getTime() - this.getTimeOffset()); + } + + /** + * Converts local time to server time. + * @param {Date} local The time to convert. + * @returns {Date} Server time. + */ + localDateToServer(local) { + // server - local = offset + return new Date(local.getTime() + this.getTimeOffset()); + } +} + +/** TimeSyncManager singleton. */ +export default new TimeSyncManager(); diff --git a/src/components/tabbedview/itemstab.js b/src/components/tabbedview/itemstab.js index 6cfd408424..884cfa2015 100644 --- a/src/components/tabbedview/itemstab.js +++ b/src/components/tabbedview/itemstab.js @@ -181,7 +181,8 @@ define(['playbackManager', 'userSettings', 'alphaPicker', 'alphaNumericShortcuts return; } - btnSortIcon.innerHTML = values.sortOrder === 'Descending' ? '' : ''; + btnSortIcon.classList.remove('arrow_downward', 'arrow_upward'); + btnSortIcon.classList.add(values.sortOrder === 'Descending' ? 'arrow_downward' : 'arrow_upward'); } function bindAll(elems, eventName, fn) { diff --git a/src/components/thememediaplayer.js b/src/components/themeMediaPlayer.js similarity index 100% rename from src/components/thememediaplayer.js rename to src/components/themeMediaPlayer.js diff --git a/src/components/toast/toast.js b/src/components/toast/toast.js index 22eb53396c..7b8e49e4d2 100644 --- a/src/components/toast/toast.js +++ b/src/components/toast/toast.js @@ -26,7 +26,7 @@ define(['css!./toast'], function () { }; } - var elem = document.createElement("div"); + var elem = document.createElement('div'); elem.classList.add('toast'); elem.innerHTML = options.text; diff --git a/src/components/tunerpicker.js b/src/components/tunerPicker.js similarity index 54% rename from src/components/tunerpicker.js rename to src/components/tunerPicker.js index 4dd5ecd3de..e7c92851a4 100644 --- a/src/components/tunerpicker.js +++ b/src/components/tunerPicker.js @@ -1,38 +1,38 @@ -define(["dialogHelper", "dom", "layoutManager", "connectionManager", "globalize", "loading", "browser", "focusManager", "scrollHelper", "material-icons", "formDialogStyle", "emby-button", "emby-itemscontainer", "cardStyle"], function (dialogHelper, dom, layoutManager, connectionManager, globalize, loading, browser, focusManager, scrollHelper) { - "use strict"; +define(['dialogHelper', 'dom', 'layoutManager', 'connectionManager', 'globalize', 'loading', 'browser', 'focusManager', 'scrollHelper', 'material-icons', 'formDialogStyle', 'emby-button', 'emby-itemscontainer', 'cardStyle'], function (dialogHelper, dom, layoutManager, connectionManager, globalize, loading, browser, focusManager, scrollHelper) { + 'use strict'; var enableFocusTransform = !browser.slow && !browser.edge; function getEditorHtml() { - var html = ""; + var html = ''; html += '
'; html += '
'; html += '
'; - html += "

" + globalize.translate("DetectingDevices") + "...

"; - html += "

" + globalize.translate("MessagePleaseWait") + "

"; - html += "
"; - html += '

' + globalize.translate("HeaderNewDevices") + "

"; + html += '

' + globalize.translate('DetectingDevices') + '...

'; + html += '

' + globalize.translate('MessagePleaseWait') + '

'; + html += '
'; + html += '

' + globalize.translate('HeaderNewDevices') + '

'; html += '
'; - html += "
"; - html += "
"; - return html += "
"; + html += '
'; + html += '
'; + return html += '
'; } function getDeviceHtml(device) { var padderClass; - var html = ""; - var cssClass = "card scalableCard"; - var cardBoxCssClass = "cardBox visualCardBox"; - cssClass += " backdropCard backdropCard-scalable"; - padderClass = "cardPadder-backdrop"; + var html = ''; + var cssClass = 'card scalableCard'; + var cardBoxCssClass = 'cardBox visualCardBox'; + cssClass += ' backdropCard backdropCard-scalable'; + padderClass = 'cardPadder-backdrop'; // TODO move card creation code to Card component if (layoutManager.tv) { - cssClass += " show-focus"; + cssClass += ' show-focus'; if (enableFocusTransform) { - cssClass += " show-animation"; + cssClass += ' show-animation'; } } @@ -41,56 +41,56 @@ define(["dialogHelper", "dom", "layoutManager", "connectionManager", "globalize" html += '
'; html += '
'; html += '
'; - html += '
dvr
'; - html += "
"; - html += "
"; + html += '
'; + html += '
'; + html += '
'; html += '
'; - html += '
' + getTunerName(device.Type) + "
"; - html += '
' + device.FriendlyName + "
"; + html += '
' + getTunerName(device.Type) + '
'; + html += '
' + device.FriendlyName + '
'; html += '
'; - html += device.Url || " "; - html += "
"; - html += "
"; - html += "
"; - return html += ""; + html += device.Url || ' '; + html += '
'; + html += '
'; + html += '
'; + return html += ''; } function getTunerName(providerId) { switch (providerId = providerId.toLowerCase()) { - case "m3u": - return "M3U"; + case 'm3u': + return 'M3U'; - case "hdhomerun": - return "HDHomerun"; + case 'hdhomerun': + return 'HDHomerun'; - case "hauppauge": - return "Hauppauge"; + case 'hauppauge': + return 'Hauppauge'; - case "satip": - return "DVB"; + case 'satip': + return 'DVB'; default: - return "Unknown"; + return 'Unknown'; } } function renderDevices(view, devices) { var i; var length; - var html = ""; + var html = ''; for (i = 0, length = devices.length; i < length; i++) { html += getDeviceHtml(devices[i]); } if (devices.length) { - view.querySelector(".devicesHeader").classList.remove("hide"); + view.querySelector('.devicesHeader').classList.remove('hide'); } else { - html = "


" + globalize.translate("NoNewDevicesFound") + "

"; - view.querySelector(".devicesHeader").classList.add("hide"); + html = '


' + globalize.translate('NoNewDevicesFound') + '

'; + view.querySelector('.devicesHeader').classList.add('hide'); } - var elem = view.querySelector(".results"); + var elem = view.querySelector('.results'); elem.innerHTML = html; if (layoutManager.tv) { @@ -100,13 +100,13 @@ define(["dialogHelper", "dom", "layoutManager", "connectionManager", "globalize" function discoverDevices(view, apiClient) { loading.show(); - view.querySelector(".loadingContent").classList.remove("hide"); - return ApiClient.getJSON(ApiClient.getUrl("LiveTv/Tuners/Discvover", { + view.querySelector('.loadingContent').classList.remove('hide'); + return ApiClient.getJSON(ApiClient.getUrl('LiveTv/Tuners/Discvover', { NewDevicesOnly: true })).then(function (devices) { currentDevices = devices; renderDevices(view, devices); - view.querySelector(".loadingContent").classList.add("hide"); + view.querySelector('.loadingContent').classList.add('hide'); loading.hide(); }); } @@ -119,31 +119,31 @@ define(["dialogHelper", "dom", "layoutManager", "connectionManager", "globalize" }; if (layoutManager.tv) { - dialogOptions.size = "fullscreen"; + dialogOptions.size = 'fullscreen'; } else { - dialogOptions.size = "small"; + dialogOptions.size = 'small'; } var dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add("formDialog"); - var html = ""; + dlg.classList.add('formDialog'); + var html = ''; html += '
'; - html += ''; + html += ''; html += '

'; - html += globalize.translate("HeaderLiveTvTunerSetup"); - html += "

"; - html += "
"; + html += globalize.translate('HeaderLiveTvTunerSetup'); + html += ''; + html += '
'; html += getEditorHtml(); dlg.innerHTML = html; - dlg.querySelector(".btnCancel").addEventListener("click", function () { + dlg.querySelector('.btnCancel').addEventListener('click', function () { dialogHelper.close(dlg); }); var deviceResult; - dlg.querySelector(".results").addEventListener("click", function (e) { - var tunerCard = dom.parentWithClass(e.target, "card"); + dlg.querySelector('.results').addEventListener('click', function (e) { + var tunerCard = dom.parentWithClass(e.target, 'card'); if (tunerCard) { - var deviceId = tunerCard.getAttribute("data-id"); + var deviceId = tunerCard.getAttribute('data-id'); deviceResult = currentDevices.filter(function (d) { return d.DeviceId === deviceId; })[0]; @@ -152,14 +152,14 @@ define(["dialogHelper", "dom", "layoutManager", "connectionManager", "globalize" }); if (layoutManager.tv) { - scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), false); + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); } var apiClient = connectionManager.getApiClient(options.serverId); discoverDevices(dlg, apiClient); if (layoutManager.tv) { - scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), false); + scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); } return dialogHelper.open(dlg).then(function () { diff --git a/src/components/tvproviders/schedulesdirect.js b/src/components/tvproviders/schedulesdirect.js index cf11e5736d..be1cdf575b 100644 --- a/src/components/tvproviders/schedulesdirect.js +++ b/src/components/tvproviders/schedulesdirect.js @@ -1,31 +1,31 @@ -define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "emby-select", "emby-button", "flexStyles"], function ($, loading) { - "use strict"; +define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emby-input', 'emby-select', 'emby-button', 'flexStyles'], function ($, loading, globalize) { + 'use strict'; return function (page, providerId, options) { function reload() { loading.show(); - ApiClient.getNamedConfiguration("livetv").then(function (config) { + ApiClient.getNamedConfiguration('livetv').then(function (config) { var info = config.ListingProviders.filter(function (i) { return i.Id === providerId; })[0] || {}; listingsId = info.ListingsId; - $("#selectListing", page).val(info.ListingsId || ""); - page.querySelector(".txtUser").value = info.Username || ""; - page.querySelector(".txtPass").value = ""; - page.querySelector(".txtZipCode").value = info.ZipCode || ""; + $('#selectListing', page).val(info.ListingsId || ''); + page.querySelector('.txtUser').value = info.Username || ''; + page.querySelector('.txtPass').value = ''; + page.querySelector('.txtZipCode').value = info.ZipCode || ''; if (info.Username && info.Password) { - page.querySelector(".listingsSection").classList.remove("hide"); + page.querySelector('.listingsSection').classList.remove('hide'); } else { - page.querySelector(".listingsSection").classList.add("hide"); + page.querySelector('.listingsSection').classList.add('hide'); } - page.querySelector(".chkAllTuners").checked = info.EnableAllTuners; + page.querySelector('.chkAllTuners').checked = info.EnableAllTuners; if (info.EnableAllTuners) { - page.querySelector(".selectTunersSection").classList.add("hide"); + page.querySelector('.selectTunersSection').classList.add('hide'); } else { - page.querySelector(".selectTunersSection").classList.remove("hide"); + page.querySelector('.selectTunersSection').classList.remove('hide'); } setCountry(info); @@ -34,7 +34,7 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em } function setCountry(info) { - ApiClient.getJSON(ApiClient.getUrl("LiveTv/ListingProviders/SchedulesDirect/Countries")).then(function (result) { + ApiClient.getJSON(ApiClient.getUrl('LiveTv/ListingProviders/SchedulesDirect/Countries')).then(function (result) { var i; var length; var countryList = []; @@ -42,7 +42,7 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em for (var region in result) { var countries = result[region]; - if (countries.length && "ZZZ" !== region) { + if (countries.length && 'ZZZ' !== region) { for (i = 0, length = countries.length; i < length; i++) { countryList.push({ name: countries[i].fullName, @@ -63,13 +63,13 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em return 0; }); - $("#selectCountry", page).html(countryList.map(function (c) { - return '"; - }).join("")).val(info.Country || ""); - $(page.querySelector(".txtZipCode")).trigger("change"); + $('#selectCountry', page).html(countryList.map(function (c) { + return ''; + }).join('')).val(info.Country || ''); + $(page.querySelector('.txtZipCode')).trigger('change'); }, function () { // ApiClient.getJSON() error handler Dashboard.alert({ - message: Globalize.translate("ErrorGettingTvLineups") + message: globalize.translate('ErrorGettingTvLineups') }); }); loading.hide(); @@ -77,11 +77,11 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em function sha256(str) { if (!self.TextEncoder) { - return Promise.resolve(""); + return Promise.resolve(''); } - var buffer = new TextEncoder("utf-8").encode(str); - return crypto.subtle.digest("SHA-256", buffer).then(function (hash) { + var buffer = new TextEncoder('utf-8').encode(str); + return crypto.subtle.digest('SHA-256', buffer).then(function (hash) { return hex(hash); }); } @@ -93,22 +93,22 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em for (var i = 0; i < view.byteLength; i += 4) { var value = view.getUint32(i); var stringValue = value.toString(16); - var paddedValue = ("00000000" + stringValue).slice(-"00000000".length); + var paddedValue = ('00000000' + stringValue).slice(-'00000000'.length); hexCodes.push(paddedValue); } - return hexCodes.join(""); + return hexCodes.join(''); } function submitLoginForm() { loading.show(); - sha256(page.querySelector(".txtPass").value).then(function (passwordHash) { + sha256(page.querySelector('.txtPass').value).then(function (passwordHash) { var info = { - Type: "SchedulesDirect", - Username: page.querySelector(".txtUser").value, + Type: 'SchedulesDirect', + Username: page.querySelector('.txtUser').value, EnableAllTuners: true, Password: passwordHash, - Pw: page.querySelector(".txtPass").value + Pw: page.querySelector('.txtPass').value }; var id = providerId; @@ -117,56 +117,56 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em } ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("LiveTv/ListingProviders", { + type: 'POST', + url: ApiClient.getUrl('LiveTv/ListingProviders', { ValidateLogin: true }), data: JSON.stringify(info), - contentType: "application/json", - dataType: "json" + contentType: 'application/json', + dataType: 'json' }).then(function (result) { Dashboard.processServerConfigurationUpdateResult(); providerId = result.Id; reload(); }, function () { Dashboard.alert({ // ApiClient.ajax() error handler - message: Globalize.translate("ErrorSavingTvProvider") + message: globalize.translate('ErrorSavingTvProvider') }); }); }); } function submitListingsForm() { - var selectedListingsId = $("#selectListing", page).val(); + var selectedListingsId = $('#selectListing', page).val(); if (!selectedListingsId) { return void Dashboard.alert({ - message: Globalize.translate("ErrorPleaseSelectLineup") + message: globalize.translate('ErrorPleaseSelectLineup') }); } loading.show(); var id = providerId; - ApiClient.getNamedConfiguration("livetv").then(function (config) { + ApiClient.getNamedConfiguration('livetv').then(function (config) { var info = config.ListingProviders.filter(function (i) { return i.Id === id; })[0]; - info.ZipCode = page.querySelector(".txtZipCode").value; - info.Country = $("#selectCountry", page).val(); + info.ZipCode = page.querySelector('.txtZipCode').value; + info.Country = $('#selectCountry', page).val(); info.ListingsId = selectedListingsId; - info.EnableAllTuners = page.querySelector(".chkAllTuners").checked; - info.EnabledTuners = info.EnableAllTuners ? [] : $(".chkTuner", page).get().filter(function (i) { + info.EnableAllTuners = page.querySelector('.chkAllTuners').checked; + info.EnabledTuners = info.EnableAllTuners ? [] : $('.chkTuner', page).get().filter(function (i) { return i.checked; }).map(function (i) { - return i.getAttribute("data-id"); + return i.getAttribute('data-id'); }); ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("LiveTv/ListingProviders", { + type: 'POST', + url: ApiClient.getUrl('LiveTv/ListingProviders', { ValidateListings: true }), data: JSON.stringify(info), - contentType: "application/json" + contentType: 'application/json' }).then(function (result) { loading.hide(); @@ -174,11 +174,11 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em Dashboard.processServerConfigurationUpdateResult(); } - Events.trigger(self, "submitted"); + Events.trigger(self, 'submitted'); }, function () { loading.hide(); Dashboard.alert({ - message: Globalize.translate("ErrorAddingListingsToSchedulesDirect") + message: globalize.translate('ErrorAddingListingsToSchedulesDirect') }); }); }); @@ -186,79 +186,79 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em function refreshListings(value) { if (!value) { - return void $("#selectListing", page).html(""); + return void $('#selectListing', page).html(''); } loading.show(); ApiClient.ajax({ - type: "GET", - url: ApiClient.getUrl("LiveTv/ListingProviders/Lineups", { + type: 'GET', + url: ApiClient.getUrl('LiveTv/ListingProviders/Lineups', { Id: providerId, Location: value, - Country: $("#selectCountry", page).val() + Country: $('#selectCountry', page).val() }), - dataType: "json" + dataType: 'json' }).then(function (result) { - $("#selectListing", page).html(result.map(function (o) { - return '"; + $('#selectListing', page).html(result.map(function (o) { + return ''; })); if (listingsId) { - $("#selectListing", page).val(listingsId); + $('#selectListing', page).val(listingsId); } loading.hide(); }, function (result) { Dashboard.alert({ - message: Globalize.translate("ErrorGettingTvLineups") + message: globalize.translate('ErrorGettingTvLineups') }); - refreshListings(""); + refreshListings(''); loading.hide(); }); } function getTunerName(providerId) { switch (providerId = providerId.toLowerCase()) { - case "m3u": - return "M3U Playlist"; - case "hdhomerun": - return "HDHomerun"; - case "satip": - return "DVB"; + case 'm3u': + return 'M3U Playlist'; + case 'hdhomerun': + return 'HDHomerun'; + case 'satip': + return 'DVB'; default: - return "Unknown"; + return 'Unknown'; } } function refreshTunerDevices(page, providerInfo, devices) { - var html = ""; + var html = ''; for (var i = 0, length = devices.length; i < length; i++) { var device = devices[i]; html += '
'; var enabledTuners = providerInfo.EnabledTuners || []; var isChecked = providerInfo.EnableAllTuners || -1 !== enabledTuners.indexOf(device.Id); - var checkedAttribute = isChecked ? " checked" : ""; - html += '"; + var checkedAttribute = isChecked ? ' checked' : ''; + html += ''; html += '
'; html += '
'; html += device.FriendlyName || getTunerName(device.Type); - html += "
"; + html += '
'; html += '
'; html += device.Url; - html += "
"; - html += "
"; - html += "
"; + html += '
'; + html += '
'; + html += '
'; } - page.querySelector(".tunerList").innerHTML = html; + page.querySelector('.tunerList').innerHTML = html; } var listingsId; var self = this; self.submit = function () { - page.querySelector(".btnSubmitListingsContainer").click(); + page.querySelector('.btnSubmitListingsContainer').click(); }; self.init = function () { @@ -267,30 +267,30 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em // Only hide the buttons if explicitly set to false; default to showing if undefined or null // FIXME: rename this option to clarify logic var hideCancelButton = options.showCancelButton === false; - page.querySelector(".btnCancel").classList.toggle("hide", hideCancelButton); + page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton); var hideSubmitButton = options.showSubmitButton === false; - page.querySelector(".btnSubmitListings").classList.toggle("hide", hideSubmitButton); + page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton); - $(".formLogin", page).on("submit", function () { + $('.formLogin', page).on('submit', function () { submitLoginForm(); return false; }); - $(".formListings", page).on("submit", function () { + $('.formListings', page).on('submit', function () { submitListingsForm(); return false; }); - $(".txtZipCode", page).on("change", function () { + $('.txtZipCode', page).on('change', function () { refreshListings(this.value); }); - page.querySelector(".chkAllTuners").addEventListener("change", function (e) { + page.querySelector('.chkAllTuners').addEventListener('change', function (e) { if (e.target.checked) { - page.querySelector(".selectTunersSection").classList.add("hide"); + page.querySelector('.selectTunersSection').classList.add('hide'); } else { - page.querySelector(".selectTunersSection").classList.remove("hide"); + page.querySelector('.selectTunersSection').classList.remove('hide'); } }); - $(".createAccountHelp", page).html(Globalize.translate("MessageCreateAccountAt", 'http://www.schedulesdirect.org')); + $('.createAccountHelp', page).html(globalize.translate('MessageCreateAccountAt', 'http://www.schedulesdirect.org')); reload(); }; }; diff --git a/src/components/tvproviders/schedulesdirect.template.html b/src/components/tvproviders/schedulesdirect.template.html index 3cfba06fe1..abe19b50f5 100644 --- a/src/components/tvproviders/schedulesdirect.template.html +++ b/src/components/tvproviders/schedulesdirect.template.html @@ -1,7 +1,7 @@

Schedules Direct

- ${Help} + ${Help}

diff --git a/src/components/tvproviders/xmltv.js b/src/components/tvproviders/xmltv.js index 7e7d381f09..4c39b14430 100644 --- a/src/components/tvproviders/xmltv.js +++ b/src/components/tvproviders/xmltv.js @@ -1,5 +1,5 @@ -define(["jQuery", "loading", "emby-checkbox", "emby-input", "listViewStyle", "paper-icon-button-light"], function ($, loading) { - "use strict"; +define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listViewStyle', 'paper-icon-button-light'], function ($, loading, globalize) { + 'use strict'; return function (page, providerId, options) { function getListingProvider(config, id) { @@ -15,26 +15,26 @@ define(["jQuery", "loading", "emby-checkbox", "emby-input", "listViewStyle", "pa return getListingProvider(); } - return ApiClient.getJSON(ApiClient.getUrl("LiveTv/ListingProviders/Default")); + return ApiClient.getJSON(ApiClient.getUrl('LiveTv/ListingProviders/Default')); } function reload() { loading.show(); - ApiClient.getNamedConfiguration("livetv").then(function (config) { + ApiClient.getNamedConfiguration('livetv').then(function (config) { getListingProvider(config, providerId).then(function (info) { - page.querySelector(".txtPath").value = info.Path || ""; - page.querySelector(".txtKids").value = (info.KidsCategories || []).join("|"); - page.querySelector(".txtNews").value = (info.NewsCategories || []).join("|"); - page.querySelector(".txtSports").value = (info.SportsCategories || []).join("|"); - page.querySelector(".txtMovies").value = (info.MovieCategories || []).join("|"); - page.querySelector(".txtMoviePrefix").value = info.MoviePrefix || ""; - page.querySelector(".txtUserAgent").value = info.UserAgent || ""; - page.querySelector(".chkAllTuners").checked = info.EnableAllTuners; + page.querySelector('.txtPath').value = info.Path || ''; + page.querySelector('.txtKids').value = (info.KidsCategories || []).join('|'); + page.querySelector('.txtNews').value = (info.NewsCategories || []).join('|'); + page.querySelector('.txtSports').value = (info.SportsCategories || []).join('|'); + page.querySelector('.txtMovies').value = (info.MovieCategories || []).join('|'); + page.querySelector('.txtMoviePrefix').value = info.MoviePrefix || ''; + page.querySelector('.txtUserAgent').value = info.UserAgent || ''; + page.querySelector('.chkAllTuners').checked = info.EnableAllTuners; - if (page.querySelector(".chkAllTuners").checked) { - page.querySelector(".selectTunersSection").classList.add("hide"); + if (page.querySelector('.chkAllTuners').checked) { + page.querySelector('.selectTunersSection').classList.add('hide'); } else { - page.querySelector(".selectTunersSection").classList.remove("hide"); + page.querySelector('.selectTunersSection').classList.remove('hide'); } refreshTunerDevices(page, info, config.TunerHosts); @@ -47,7 +47,7 @@ define(["jQuery", "loading", "emby-checkbox", "emby-input", "listViewStyle", "pa var value = txtInput.value; if (value) { - return value.split("|"); + return value.split('|'); } return []; @@ -56,31 +56,31 @@ define(["jQuery", "loading", "emby-checkbox", "emby-input", "listViewStyle", "pa function submitListingsForm() { loading.show(); var id = providerId; - ApiClient.getNamedConfiguration("livetv").then(function (config) { + ApiClient.getNamedConfiguration('livetv').then(function (config) { var info = config.ListingProviders.filter(function (provider) { return provider.Id === id; })[0] || {}; - info.Type = "xmltv"; - info.Path = page.querySelector(".txtPath").value; - info.MoviePrefix = page.querySelector(".txtMoviePrefix").value || null; - info.UserAgent = page.querySelector(".txtUserAgent").value || null; - info.MovieCategories = getCategories(page.querySelector(".txtMovies")); - info.KidsCategories = getCategories(page.querySelector(".txtKids")); - info.NewsCategories = getCategories(page.querySelector(".txtNews")); - info.SportsCategories = getCategories(page.querySelector(".txtSports")); - info.EnableAllTuners = page.querySelector(".chkAllTuners").checked; - info.EnabledTuners = info.EnableAllTuners ? [] : $(".chkTuner", page).get().filter(function (tuner) { + info.Type = 'xmltv'; + info.Path = page.querySelector('.txtPath').value; + info.MoviePrefix = page.querySelector('.txtMoviePrefix').value || null; + info.UserAgent = page.querySelector('.txtUserAgent').value || null; + info.MovieCategories = getCategories(page.querySelector('.txtMovies')); + info.KidsCategories = getCategories(page.querySelector('.txtKids')); + info.NewsCategories = getCategories(page.querySelector('.txtNews')); + info.SportsCategories = getCategories(page.querySelector('.txtSports')); + info.EnableAllTuners = page.querySelector('.chkAllTuners').checked; + info.EnabledTuners = info.EnableAllTuners ? [] : $('.chkTuner', page).get().filter(function (tuner) { return tuner.checked; }).map(function (tuner) { - return tuner.getAttribute("data-id"); + return tuner.getAttribute('data-id'); }); ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("LiveTv/ListingProviders", { + type: 'POST', + url: ApiClient.getUrl('LiveTv/ListingProviders', { ValidateListings: true }), data: JSON.stringify(info), - contentType: "application/json" + contentType: 'application/json' }).then(function (result) { loading.hide(); @@ -88,11 +88,11 @@ define(["jQuery", "loading", "emby-checkbox", "emby-input", "listViewStyle", "pa Dashboard.processServerConfigurationUpdateResult(); } - Events.trigger(self, "submitted"); + Events.trigger(self, 'submitted'); }, function () { loading.hide(); Dashboard.alert({ - message: Globalize.translate("ErrorAddingXmlTvFile") + message: globalize.translate('ErrorAddingXmlTvFile') }); }); }); @@ -100,51 +100,51 @@ define(["jQuery", "loading", "emby-checkbox", "emby-input", "listViewStyle", "pa function getTunerName(providerId) { switch (providerId = providerId.toLowerCase()) { - case "m3u": - return "M3U Playlist"; - case "hdhomerun": - return "HDHomerun"; - case "satip": - return "DVB"; + case 'm3u': + return 'M3U Playlist'; + case 'hdhomerun': + return 'HDHomerun'; + case 'satip': + return 'DVB'; default: - return "Unknown"; + return 'Unknown'; } } function refreshTunerDevices(page, providerInfo, devices) { - var html = ""; + var html = ''; for (var i = 0, length = devices.length; i < length; i++) { var device = devices[i]; html += '
'; var enabledTuners = providerInfo.EnabledTuners || []; var isChecked = providerInfo.EnableAllTuners || -1 !== enabledTuners.indexOf(device.Id); - var checkedAttribute = isChecked ? " checked" : ""; - html += '"; + var checkedAttribute = isChecked ? ' checked' : ''; + html += ''; html += '
'; html += '
'; html += device.FriendlyName || getTunerName(device.Type); - html += "
"; + html += '
'; html += '
'; html += device.Url; - html += "
"; - html += "
"; - html += ""; + html += ''; + html += ''; + html += ''; } - page.querySelector(".tunerList").innerHTML = html; + page.querySelector('.tunerList').innerHTML = html; } function onSelectPathClick(e) { - var page = $(e.target).parents(".xmltvForm")[0]; + var page = $(e.target).parents('.xmltvForm')[0]; - require(["directorybrowser"], function (directoryBrowser) { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ includeFiles: true, callback: function (path) { if (path) { - var txtPath = page.querySelector(".txtPath"); + var txtPath = page.querySelector('.txtPath'); txtPath.value = path; txtPath.focus(); } @@ -157,7 +157,7 @@ define(["jQuery", "loading", "emby-checkbox", "emby-input", "listViewStyle", "pa var self = this; self.submit = function () { - page.querySelector(".btnSubmitListings").click(); + page.querySelector('.btnSubmitListings').click(); }; self.init = function () { @@ -166,21 +166,21 @@ define(["jQuery", "loading", "emby-checkbox", "emby-input", "listViewStyle", "pa // Only hide the buttons if explicitly set to false; default to showing if undefined or null // FIXME: rename this option to clarify logic var hideCancelButton = options.showCancelButton === false; - page.querySelector(".btnCancel").classList.toggle("hide", hideCancelButton); + page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton); var hideSubmitButton = options.showSubmitButton === false; - page.querySelector(".btnSubmitListings").classList.toggle("hide", hideSubmitButton); + page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton); - $("form", page).on("submit", function () { + $('form', page).on('submit', function () { submitListingsForm(); return false; }); - page.querySelector("#btnSelectPath").addEventListener("click", onSelectPathClick); - page.querySelector(".chkAllTuners").addEventListener("change", function (evt) { + page.querySelector('#btnSelectPath').addEventListener('click', onSelectPathClick); + page.querySelector('.chkAllTuners').addEventListener('change', function (evt) { if (evt.target.checked) { - page.querySelector(".selectTunersSection").classList.add("hide"); + page.querySelector('.selectTunersSection').classList.add('hide'); } else { - page.querySelector(".selectTunersSection").classList.remove("hide"); + page.querySelector('.selectTunersSection').classList.remove('hide'); } }); reload(); diff --git a/src/components/tvproviders/xmltv.template.html b/src/components/tvproviders/xmltv.template.html index 7f6490e621..72c29904b3 100644 --- a/src/components/tvproviders/xmltv.template.html +++ b/src/components/tvproviders/xmltv.template.html @@ -1,7 +1,7 @@

Xml TV

- ${Help} + ${Help}
@@ -12,7 +12,7 @@
- +
${XmlTvPathHelp}
diff --git a/src/components/upnextdialog/upnextdialog.css b/src/components/upnextdialog/upnextdialog.css index 15f91b29d9..05e3b10f57 100644 --- a/src/components/upnextdialog/upnextdialog.css +++ b/src/components/upnextdialog/upnextdialog.css @@ -63,8 +63,8 @@ height: auto; width: 100%; box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37); - border: 0; user-drag: none; + border: 0; user-select: none; -moz-user-select: none; -webkit-user-drag: none; diff --git a/src/components/upnextdialog/upnextdialog.js b/src/components/upnextdialog/upnextdialog.js index 225859e0b3..3e9c9f9c5e 100644 --- a/src/components/upnextdialog/upnextdialog.js +++ b/src/components/upnextdialog/upnextdialog.js @@ -10,7 +10,7 @@ define(['dom', 'playbackManager', 'connectionManager', 'events', 'mediaInfo', 'l } options = options || {}; - options.type = options.type || "Primary"; + options.type = options.type || 'Primary'; if (options.type === 'Primary') { @@ -44,7 +44,7 @@ define(['dom', 'playbackManager', 'connectionManager', 'events', 'mediaInfo', 'l function imageUrl(item, options) { options = options || {}; - options.type = options.type || "Primary"; + options.type = options.type || 'Primary'; if (item.ImageTags && item.ImageTags[options.type]) { diff --git a/src/components/userdatabuttons/userdatabuttons.js b/src/components/userdatabuttons/userdatabuttons.js index 2636be1e1d..86b9902133 100644 --- a/src/components/userdatabuttons/userdatabuttons.js +++ b/src/components/userdatabuttons/userdatabuttons.js @@ -30,7 +30,7 @@ define(['connectionManager', 'globalize', 'dom', 'itemHelper', 'paper-icon-butto iconCssClass += 'material-icons'; - return ''; + return ''; } function onContainerClick(e) { @@ -90,10 +90,10 @@ define(['connectionManager', 'globalize', 'dom', 'itemHelper', 'paper-icon-butto return html; } - var btnCssClass = "btnUserData"; + var btnCssClass = 'btnUserData'; if (cssClass) { - btnCssClass += " " + cssClass; + btnCssClass += ' ' + cssClass; } var iconCssClass = options.iconCssClass; diff --git a/src/components/viewContainer.js b/src/components/viewContainer.js index 607e7029d1..6c83e4a3ba 100644 --- a/src/components/viewContainer.js +++ b/src/components/viewContainer.js @@ -1,16 +1,16 @@ -define(["browser", "dom", "layoutManager", "css!components/viewManager/viewContainer"], function (browser, dom, layoutManager) { - "use strict"; +define(['browser', 'dom', 'layoutManager', 'css!components/viewManager/viewContainer'], function (browser, dom, layoutManager) { + 'use strict'; function setControllerClass(view, options) { if (options.controllerFactory) { return Promise.resolve(); } - var controllerUrl = view.getAttribute("data-controller"); + var controllerUrl = view.getAttribute('data-controller'); if (controllerUrl) { - if (0 === controllerUrl.indexOf("__plugin/")) { - controllerUrl = controllerUrl.substring("__plugin/".length); + if (0 === controllerUrl.indexOf('__plugin/')) { + controllerUrl = controllerUrl.substring('__plugin/'.length); } controllerUrl = Dashboard.getConfigurationResourceUrl(controllerUrl); @@ -38,21 +38,21 @@ define(["browser", "dom", "layoutManager", "css!components/viewManager/viewConta pageIndex = 0; } - var isPluginpage = -1 !== options.url.toLowerCase().indexOf("/configurationpage"); + var isPluginpage = -1 !== options.url.toLowerCase().indexOf('/configurationpage'); var newViewInfo = normalizeNewView(options, isPluginpage); var newView = newViewInfo.elem; var modulesToLoad = []; if (isPluginpage) { - modulesToLoad.push("legacyDashboard"); + modulesToLoad.push('legacyDashboard'); } if (newViewInfo.hasjQuerySelect) { - modulesToLoad.push("legacySelectMenu"); + modulesToLoad.push('legacySelectMenu'); } if (newViewInfo.hasjQueryChecked) { - modulesToLoad.push("fnchecked"); + modulesToLoad.push('fnchecked'); } return new Promise(function (resolve) { @@ -65,12 +65,12 @@ define(["browser", "dom", "layoutManager", "css!components/viewManager/viewConta var view = newView; - if ("string" == typeof view) { - view = document.createElement("div"); + if ('string' == typeof view) { + view = document.createElement('div'); view.innerHTML = newView; } - view.classList.add("mainAnimatedPage"); + view.classList.add('mainAnimatedPage'); if (currentPage) { if (newViewInfo.hasScript && window.$) { @@ -88,17 +88,17 @@ define(["browser", "dom", "layoutManager", "css!components/viewManager/viewConta } if (options.type) { - view.setAttribute("data-type", options.type); + view.setAttribute('data-type', options.type); } var properties = []; if (options.fullscreen) { - properties.push("fullscreen"); + properties.push('fullscreen'); } if (properties.length) { - view.setAttribute("data-properties", properties.join(",")); + view.setAttribute('data-properties', properties.join(',')); } allPages[pageIndex] = view; @@ -133,11 +133,11 @@ define(["browser", "dom", "layoutManager", "css!components/viewManager/viewConta function parseHtml(html, hasScript) { if (hasScript) { - html = replaceAll(html, "\x3c!----\x3e", "<\/script>"); + html = replaceAll(html, '\x3c!----\x3e', '<\/script>'); } - var wrapper = document.createElement("div"); + var wrapper = document.createElement('div'); wrapper.innerHTML = html; return wrapper.querySelector('div[data-role="page"]'); } @@ -149,11 +149,11 @@ define(["browser", "dom", "layoutManager", "css!components/viewManager/viewConta return viewHtml; } - var hasScript = -1 !== viewHtml.indexOf("= 0 && rec.top < vpHeight + thresholdY; - var bViz = rec.bottom > 0 && rec.bottom <= vpHeight + thresholdY; - var lViz = rec.left >= 0 && rec.left < vpWidth + thresholdX; - var rViz = rec.right > 0 && rec.right <= vpWidth + thresholdX; - var vVisible = partial ? tViz || bViz : tViz && bViz; - var hVisible = partial ? lViz || rViz : lViz && rViz; - - return vVisible && hVisible; - } - - return visibleInViewport; -}); diff --git a/src/config.example.json b/src/config.template.json similarity index 100% rename from src/config.example.json rename to src/config.template.json diff --git a/src/controllers/apikeys.js b/src/controllers/apikeys.js deleted file mode 100644 index 448ffa29a7..0000000000 --- a/src/controllers/apikeys.js +++ /dev/null @@ -1,83 +0,0 @@ -define(["datetime", "loading", "libraryMenu", "dom", "globalize", "emby-button"], function (datetime, loading, libraryMenu, dom, globalize) { - "use strict"; - - function revoke(page, key) { - require(["confirm"], function (confirm) { - confirm(globalize.translate("MessageConfirmRevokeApiKey"), globalize.translate("HeaderConfirmRevokeApiKey")).then(function () { - loading.show(); - ApiClient.ajax({ - type: "DELETE", - url: ApiClient.getUrl("Auth/Keys/" + key) - }).then(function () { - loadData(page); - }); - }); - }); - } - - function renderKeys(page, keys) { - var rows = keys.map(function (item) { - var html = ""; - html += '
'; - html += '"; - html += '"; - html += '"; - html += '"; - return html += ""; - }).join(""); - page.querySelector(".resultBody").innerHTML = rows; - loading.hide(); - } - - function loadData(page) { - loading.show(); - ApiClient.getJSON(ApiClient.getUrl("Auth/Keys")).then(function (result) { - renderKeys(page, result.Items); - }); - } - - function showNewKeyPrompt(page) { - require(["prompt"], function (prompt) { - prompt({ - title: globalize.translate("HeaderNewApiKey"), - label: globalize.translate("LabelAppName"), - description: globalize.translate("LabelAppNameExample") - }).then(function (value) { - ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("Auth/Keys", { - App: value - }) - }).then(function () { - loadData(page); - }); - }); - }); - } - - pageIdOn("pageinit", "apiKeysPage", function () { - var page = this; - page.querySelector(".btnNewKey").addEventListener("click", function () { - showNewKeyPrompt(page); - }); - page.querySelector(".tblApiKeys").addEventListener("click", function (e) { - var btnRevoke = dom.parentWithClass(e.target, "btnRevoke"); - - if (btnRevoke) { - revoke(page, btnRevoke.getAttribute("data-token")); - } - }); - }); - pageIdOn("pagebeforeshow", "apiKeysPage", function () { - loadData(this); - }); -}); diff --git a/src/controllers/auth/addserver.js b/src/controllers/auth/addserver.js index a55ba3066c..622d19082e 100644 --- a/src/controllers/auth/addserver.js +++ b/src/controllers/auth/addserver.js @@ -1,52 +1,52 @@ -define(["appSettings", "loading", "browser", "emby-button"], function(appSettings, loading, browser) { - "use strict"; +define(['appSettings', 'loading', 'browser', 'globalize', 'emby-button'], function(appSettings, loading, browser, globalize) { + 'use strict'; function handleConnectionResult(page, result) { loading.hide(); switch (result.State) { - case "SignedIn": + case 'SignedIn': var apiClient = result.ApiClient; Dashboard.onServerChanged(apiClient.getCurrentUserId(), apiClient.accessToken(), apiClient); - Dashboard.navigate("home.html"); + Dashboard.navigate('home.html'); break; - case "ServerSignIn": - Dashboard.navigate("login.html?serverid=" + result.Servers[0].Id, false, "none"); + case 'ServerSignIn': + Dashboard.navigate('login.html?serverid=' + result.Servers[0].Id, false, 'none'); break; - case "ServerSelection": - Dashboard.navigate("selectserver.html", false, "none"); + case 'ServerSelection': + Dashboard.navigate('selectserver.html', false, 'none'); break; - case "ServerUpdateNeeded": + case 'ServerUpdateNeeded': Dashboard.alert({ - message: Globalize.translate("ServerUpdateNeeded", 'https://github.com/jellyfin/jellyfin') + message: globalize.translate('ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin') }); break; - case "Unavailable": + case 'Unavailable': Dashboard.alert({ - message: Globalize.translate("MessageUnableToConnectToServer"), - title: Globalize.translate("HeaderConnectionFailure") + message: globalize.translate('MessageUnableToConnectToServer'), + title: globalize.translate('HeaderConnectionFailure') }); } } function submitServer(page) { loading.show(); - var host = page.querySelector("#txtServerHost").value; + var host = page.querySelector('#txtServerHost').value; ConnectionManager.connectToAddress(host, { enableAutoLogin: appSettings.enableAutoLogin() }).then(function(result) { handleConnectionResult(page, result); }, function() { handleConnectionResult(page, { - State: "Unavailable" + State: 'Unavailable' }); }); } return function(view, params) { - view.querySelector(".addServerForm").addEventListener("submit", onServerSubmit); - view.querySelector(".btnCancel").addEventListener("click", goBack); + view.querySelector('.addServerForm').addEventListener('submit', onServerSubmit); + view.querySelector('.btnCancel').addEventListener('click', goBack); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(view); }); @@ -57,7 +57,7 @@ define(["appSettings", "loading", "browser", "emby-button"], function(appSetting } function goBack() { - require(["appRouter"], function(appRouter) { + require(['appRouter'], function(appRouter) { appRouter.back(); }); } diff --git a/src/controllers/auth/forgotpassword.js b/src/controllers/auth/forgotpassword.js index e0f8ea4ef8..3756bf814d 100644 --- a/src/controllers/auth/forgotpassword.js +++ b/src/controllers/auth/forgotpassword.js @@ -1,34 +1,34 @@ -define([], function () { - "use strict"; +define(['globalize'], function (globalize) { + 'use strict'; function processForgotPasswordResult(result) { - if ("ContactAdmin" == result.Action) { + if ('ContactAdmin' == result.Action) { return void Dashboard.alert({ - message: Globalize.translate("MessageContactAdminToResetPassword"), - title: Globalize.translate("HeaderForgotPassword") + message: globalize.translate('MessageContactAdminToResetPassword'), + title: globalize.translate('HeaderForgotPassword') }); } - if ("InNetworkRequired" == result.Action) { + if ('InNetworkRequired' == result.Action) { return void Dashboard.alert({ - message: Globalize.translate("MessageForgotPasswordInNetworkRequired"), - title: Globalize.translate("HeaderForgotPassword") + message: globalize.translate('MessageForgotPasswordInNetworkRequired'), + title: globalize.translate('HeaderForgotPassword') }); } - if ("PinCode" == result.Action) { - var msg = Globalize.translate("MessageForgotPasswordFileCreated"); - msg += "
"; - msg += "
"; - msg += "Enter PIN here to finish Password Reset
"; - msg += "
"; + if ('PinCode' == result.Action) { + var msg = globalize.translate('MessageForgotPasswordFileCreated'); + msg += '
'; + msg += '
'; + msg += 'Enter PIN here to finish Password Reset
'; + msg += '
'; msg += result.PinFile; - msg += "
"; + msg += '
'; return void Dashboard.alert({ message: msg, - title: Globalize.translate("HeaderForgotPassword"), + title: globalize.translate('HeaderForgotPassword'), callback: function () { - Dashboard.navigate("forgotpasswordpin.html"); + Dashboard.navigate('forgotpasswordpin.html'); } }); } @@ -37,17 +37,17 @@ define([], function () { return function (view, params) { function onSubmit(e) { ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("Users/ForgotPassword"), - dataType: "json", + type: 'POST', + url: ApiClient.getUrl('Users/ForgotPassword'), + dataType: 'json', data: { - EnteredUsername: view.querySelector("#txtName").value + EnteredUsername: view.querySelector('#txtName').value } }).then(processForgotPasswordResult); e.preventDefault(); return false; } - view.querySelector("form").addEventListener("submit", onSubmit); + view.querySelector('form').addEventListener('submit', onSubmit); }; }); diff --git a/src/controllers/auth/forgotpasswordpin.js b/src/controllers/auth/forgotpasswordpin.js index 47b1c899b9..2a51890d2f 100644 --- a/src/controllers/auth/forgotpasswordpin.js +++ b/src/controllers/auth/forgotpasswordpin.js @@ -1,41 +1,41 @@ -define([], function () { - "use strict"; +define(['globalize'], function (globalize) { + 'use strict'; function processForgotPasswordResult(result) { if (result.Success) { - var msg = Globalize.translate("MessagePasswordResetForUsers"); - msg += "
"; - msg += "
"; - msg += result.UsersReset.join("
"); + var msg = globalize.translate('MessagePasswordResetForUsers'); + msg += '
'; + msg += '
'; + msg += result.UsersReset.join('
'); return void Dashboard.alert({ message: msg, - title: Globalize.translate("HeaderPasswordReset"), + title: globalize.translate('HeaderPasswordReset'), callback: function () { - window.location.href = "index.html"; + window.location.href = 'index.html'; } }); } Dashboard.alert({ - message: Globalize.translate("MessageInvalidForgotPasswordPin"), - title: Globalize.translate("HeaderPasswordReset") + message: globalize.translate('MessageInvalidForgotPasswordPin'), + title: globalize.translate('HeaderPasswordReset') }); } return function (view, params) { function onSubmit(e) { ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("Users/ForgotPassword/Pin"), - dataType: "json", + type: 'POST', + url: ApiClient.getUrl('Users/ForgotPassword/Pin'), + dataType: 'json', data: { - Pin: view.querySelector("#txtPin").value + Pin: view.querySelector('#txtPin').value } }).then(processForgotPasswordResult); e.preventDefault(); return false; } - view.querySelector("form").addEventListener("submit", onSubmit); + view.querySelector('form').addEventListener('submit', onSubmit); }; }); diff --git a/src/controllers/auth/login.js b/src/controllers/auth/login.js index 4296b8bfb3..c0c37e27d6 100644 --- a/src/controllers/auth/login.js +++ b/src/controllers/auth/login.js @@ -1,5 +1,5 @@ -define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layoutManager", "browser", "cardStyle", "emby-checkbox"], function (appHost, appSettings, dom, connectionManager, loading, layoutManager, browser) { - "use strict"; +define(['apphost', 'appSettings', 'dom', 'connectionManager', 'loading', 'layoutManager', 'browser', 'globalize', 'cardStyle', 'emby-checkbox'], function (appHost, appSettings, dom, connectionManager, loading, layoutManager, browser, globalize) { + 'use strict'; var enableFocusTransform = !browser.slow && !browser.edge; @@ -7,56 +7,58 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout loading.show(); apiClient.authenticateUserByName(username, password).then(function (result) { var user = result.User; - var serverId = getParameterByName("serverid"); + var serverId = getParameterByName('serverid'); var newUrl; if (user.Policy.IsAdministrator && !serverId) { - newUrl = "dashboard.html"; + newUrl = 'dashboard.html'; } else { - newUrl = "home.html"; + newUrl = 'home.html'; } loading.hide(); Dashboard.onServerChanged(user.Id, result.AccessToken, apiClient); Dashboard.navigate(newUrl); }, function (response) { - page.querySelector("#txtManualName").value = ""; - page.querySelector("#txtManualPassword").value = ""; + page.querySelector('#txtManualName').value = ''; + page.querySelector('#txtManualPassword').value = ''; loading.hide(); - if (response.status === 401) { - require(["toast"], function (toast) { - toast(Globalize.translate("MessageInvalidUser")); + const UnauthorizedOrForbidden = [401, 403]; + if (UnauthorizedOrForbidden.includes(response.status)) { + require(['toast'], function (toast) { + const messageKey = response.status === 401 ? 'MessageInvalidUser' : 'MessageUnauthorizedUser'; + toast(globalize.translate(messageKey)); }); } else { Dashboard.alert({ - message: Globalize.translate("MessageUnableToConnectToServer"), - title: Globalize.translate("HeaderConnectionFailure") + message: globalize.translate('MessageUnableToConnectToServer'), + title: globalize.translate('HeaderConnectionFailure') }); } }); } function showManualForm(context, showCancel, focusPassword) { - context.querySelector(".chkRememberLogin").checked = appSettings.enableAutoLogin(); - context.querySelector(".manualLoginForm").classList.remove("hide"); - context.querySelector(".visualLoginForm").classList.add("hide"); - context.querySelector(".btnManual").classList.add("hide"); + context.querySelector('.chkRememberLogin').checked = appSettings.enableAutoLogin(); + context.querySelector('.manualLoginForm').classList.remove('hide'); + context.querySelector('.visualLoginForm').classList.add('hide'); + context.querySelector('.btnManual').classList.add('hide'); if (focusPassword) { - context.querySelector("#txtManualPassword").focus(); + context.querySelector('#txtManualPassword').focus(); } else { - context.querySelector("#txtManualName").focus(); + context.querySelector('#txtManualName').focus(); } if (showCancel) { - context.querySelector(".btnCancel").classList.remove("hide"); + context.querySelector('.btnCancel').classList.remove('hide'); } else { - context.querySelector(".btnCancel").classList.add("hide"); + context.querySelector('.btnCancel').classList.add('hide'); } } - var metroColors = ["#6FBD45", "#4BB3DD", "#4164A5", "#E12026", "#800080", "#E1B222", "#008040", "#0094FF", "#FF00C7", "#FF870F", "#7F0037"]; + var metroColors = ['#6FBD45', '#4BB3DD', '#4164A5', '#E12026', '#800080', '#E1B222', '#008040', '#0094FF', '#FF00C7', '#FF870F', '#7F0037']; function getRandomMetroColor() { var index = Math.floor(Math.random() * (metroColors.length - 1)); @@ -80,23 +82,23 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout } function loadUserList(context, apiClient, users) { - var html = ""; + var html = ''; for (var i = 0; i < users.length; i++) { var user = users[i]; // TODO move card creation code to Card component - var cssClass = "card squareCard scalableCard squareCard-scalable"; + var cssClass = 'card squareCard scalableCard squareCard-scalable'; if (layoutManager.tv) { - cssClass += " show-focus"; + cssClass += ' show-focus'; if (enableFocusTransform) { - cssClass += " show-animation"; + cssClass += ' show-animation'; } } - var cardBoxCssClass = "cardBox cardBox-bottompadded"; + var cardBoxCssClass = 'cardBox cardBox-bottompadded'; html += '"; + html += '
' + user.Name + '
'; + html += ''; + html += ''; + html += ''; } - context.querySelector("#divUsers").innerHTML = html; + context.querySelector('#divUsers').innerHTML = html; } return function (view, params) { @@ -141,60 +143,60 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout } function showVisualForm() { - view.querySelector(".visualLoginForm").classList.remove("hide"); - view.querySelector(".manualLoginForm").classList.add("hide"); - view.querySelector(".btnManual").classList.remove("hide"); + view.querySelector('.visualLoginForm').classList.remove('hide'); + view.querySelector('.manualLoginForm').classList.add('hide'); + view.querySelector('.btnManual').classList.remove('hide'); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(view); }); } - view.querySelector("#divUsers").addEventListener("click", function (e) { - var card = dom.parentWithClass(e.target, "card"); - var cardContent = card ? card.querySelector(".cardContent") : null; + view.querySelector('#divUsers').addEventListener('click', function (e) { + var card = dom.parentWithClass(e.target, 'card'); + var cardContent = card ? card.querySelector('.cardContent') : null; if (cardContent) { var context = view; - var id = cardContent.getAttribute("data-userid"); - var name = cardContent.getAttribute("data-username"); - var haspw = cardContent.getAttribute("data-haspw"); + var id = cardContent.getAttribute('data-userid'); + var name = cardContent.getAttribute('data-username'); + var haspw = cardContent.getAttribute('data-haspw'); if (id === 'manual') { - context.querySelector("#txtManualName").value = ""; + context.querySelector('#txtManualName').value = ''; showManualForm(context, true); } else if (haspw == 'false') { - authenticateUserByName(context, getApiClient(), name, ""); + authenticateUserByName(context, getApiClient(), name, ''); } else { - context.querySelector("#txtManualName").value = name; - context.querySelector("#txtManualPassword").value = ""; + context.querySelector('#txtManualName').value = name; + context.querySelector('#txtManualPassword').value = ''; showManualForm(context, true, true); } } }); - view.querySelector(".manualLoginForm").addEventListener("submit", function (e) { - appSettings.enableAutoLogin(view.querySelector(".chkRememberLogin").checked); + view.querySelector('.manualLoginForm').addEventListener('submit', function (e) { + appSettings.enableAutoLogin(view.querySelector('.chkRememberLogin').checked); var apiClient = getApiClient(); - authenticateUserByName(view, apiClient, view.querySelector("#txtManualName").value, view.querySelector("#txtManualPassword").value); + authenticateUserByName(view, apiClient, view.querySelector('#txtManualName').value, view.querySelector('#txtManualPassword').value); e.preventDefault(); return false; }); - view.querySelector(".btnForgotPassword").addEventListener("click", function () { - Dashboard.navigate("forgotpassword.html"); + view.querySelector('.btnForgotPassword').addEventListener('click', function () { + Dashboard.navigate('forgotpassword.html'); }); - view.querySelector(".btnCancel").addEventListener("click", showVisualForm); - view.querySelector(".btnManual").addEventListener("click", function () { - view.querySelector("#txtManualName").value = ""; + view.querySelector('.btnCancel').addEventListener('click', showVisualForm); + view.querySelector('.btnManual').addEventListener('click', function () { + view.querySelector('#txtManualName').value = ''; showManualForm(view, true); }); - view.querySelector(".btnSelectServer").addEventListener("click", function () { + view.querySelector('.btnSelectServer').addEventListener('click', function () { Dashboard.selectServer(); }); - view.addEventListener("viewshow", function (e) { + view.addEventListener('viewshow', function (e) { loading.show(); if (!appHost.supports('multiserver')) { - view.querySelector(".btnSelectServer").classList.add("hide"); + view.querySelector('.btnSelectServer').classList.add('hide'); } var apiClient = getApiClient(); @@ -203,14 +205,14 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout showVisualForm(); loadUserList(view, apiClient, users); } else { - view.querySelector("#txtManualName").value = ""; + view.querySelector('#txtManualName').value = ''; showManualForm(view, false, false); } }).catch().then(function () { loading.hide(); }); - apiClient.getJSON(apiClient.getUrl("Branding/Configuration")).then(function (options) { - view.querySelector(".disclaimer").textContent = options.LoginDisclaimer || ""; + apiClient.getJSON(apiClient.getUrl('Branding/Configuration')).then(function (options) { + view.querySelector('.disclaimer').textContent = options.LoginDisclaimer || ''; }); }); }; diff --git a/src/controllers/auth/selectserver.js b/src/controllers/auth/selectserver.js index e766dbdb5c..ba88313484 100644 --- a/src/controllers/auth/selectserver.js +++ b/src/controllers/auth/selectserver.js @@ -1,5 +1,5 @@ -define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focusManager", "connectionManager", "globalize", "actionsheet", "dom", "browser", "material-icons", "flexStyles", "emby-scroller", "emby-itemscontainer", "cardStyle", "emby-button"], function (loading, appRouter, layoutManager, appSettings, appHost, focusManager, connectionManager, globalize, actionSheet, dom, browser) { - "use strict"; +define(['loading', 'appRouter', 'layoutManager', 'appSettings', 'apphost', 'focusManager', 'connectionManager', 'globalize', 'actionsheet', 'dom', 'browser', 'material-icons', 'flexStyles', 'emby-scroller', 'emby-itemscontainer', 'cardStyle', 'emby-button'], function (loading, appRouter, layoutManager, appSettings, appHost, focusManager, connectionManager, globalize, actionSheet, dom, browser) { + 'use strict'; var enableFocusTransform = !browser.slow && !browser.edge; @@ -8,8 +8,8 @@ define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focu return { name: server.Name, showIcon: true, - icon: "cast", - cardType: "", + icon: 'cast', + cardType: '', id: server.Id, server: server }; @@ -18,28 +18,28 @@ define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focu var cardImageContainer; if (item.showIcon) { - cardImageContainer = '' + item.icon + ""; + cardImageContainer = ''; } else { cardImageContainer = '
'; } // TODO move card creation code to Card component - var cssClass = "card overflowSquareCard loginSquareCard scalableCard overflowSquareCard-scalable"; + var cssClass = 'card overflowSquareCard loginSquareCard scalableCard overflowSquareCard-scalable'; if (layoutManager.tv) { - cssClass += " show-focus"; + cssClass += ' show-focus'; if (enableFocusTransform) { - cssClass += " show-animation"; + cssClass += ' show-animation'; } } - var cardBoxCssClass = "cardBox"; + var cardBoxCssClass = 'cardBox'; var innerOpening = '
'; var cardContainer = ''; - cardContainer += ''; return cardContainer; - }).join(""); - var itemsContainer = view.querySelector(".servers"); + }).join(''); + var itemsContainer = view.querySelector('.servers'); if (!items.length) { - html = '

' + globalize.translate("MessageNoServersAvailable") + "

"; + html = '

' + globalize.translate('MessageNoServersAvailable') + '

'; } itemsContainer.innerHTML = html; @@ -66,20 +66,20 @@ define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focu } function updatePageStyle(view, params) { - if (params.showuser == "1") { - view.classList.add("libraryPage"); - view.classList.remove("standalonePage"); - view.classList.add("noSecondaryNavPage"); + if (params.showuser == '1') { + view.classList.add('libraryPage'); + view.classList.remove('standalonePage'); + view.classList.add('noSecondaryNavPage'); } else { - view.classList.add("standalonePage"); - view.classList.remove("libraryPage"); - view.classList.remove("noSecondaryNavPage"); + view.classList.add('standalonePage'); + view.classList.remove('libraryPage'); + view.classList.remove('noSecondaryNavPage'); } } function showGeneralError() { loading.hide(); - alertText(globalize.translate("DefaultErrorMessage")); + alertText(globalize.translate('DefaultErrorMessage')); } function alertText(text) { @@ -89,13 +89,13 @@ define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focu } function alertTextWithOptions(options) { - require(["alert"], function (alert) { + require(['alert'], function (alert) { alert(options); }); } function showServerConnectionFailure() { - alertText(globalize.translate("MessageUnableToConnectToServer"), globalize.translate("HeaderConnectionFailure")); + alertText(globalize.translate('MessageUnableToConnectToServer')); } return function (view, params) { @@ -108,20 +108,20 @@ define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focu var apiClient = result.ApiClient; switch (result.State) { - case "SignedIn": + case 'SignedIn': Dashboard.onServerChanged(apiClient.getCurrentUserId(), apiClient.accessToken(), apiClient); - Dashboard.navigate("home.html"); + Dashboard.navigate('home.html'); break; - case "ServerSignIn": + case 'ServerSignIn': Dashboard.onServerChanged(null, null, apiClient); - Dashboard.navigate("login.html?serverid=" + result.Servers[0].Id); + Dashboard.navigate('login.html?serverid=' + result.Servers[0].Id); break; - case "ServerUpdateNeeded": + case 'ServerUpdateNeeded': alertTextWithOptions({ - text: globalize.translate("core#ServerUpdateNeeded", "https://github.com/jellyfin/jellyfin"), - html: globalize.translate("core#ServerUpdateNeeded", 'https://github.com/jellyfin/jellyfin') + text: globalize.translate('core#ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin'), + html: globalize.translate('core#ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin') }); break; @@ -142,23 +142,23 @@ define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focu function onServerClick(server) { var menuItems = []; menuItems.push({ - name: globalize.translate("Connect"), - id: "connect" + name: globalize.translate('Connect'), + id: 'connect' }); menuItems.push({ - name: globalize.translate("Delete"), - id: "delete" + name: globalize.translate('Delete'), + id: 'delete' }); actionSheet.show({ items: menuItems, title: server.Name }).then(function (id) { switch (id) { - case "connect": + case 'connect': connectToServer(server); break; - case "delete": + case 'delete': deleteServer(server); } }); @@ -180,7 +180,7 @@ define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focu var servers; updatePageStyle(view, params); - view.addEventListener("viewshow", function (e) { + view.addEventListener('viewshow', function (e) { var isRestored = e.detail.isRestored; appRouter.setTitle(null); @@ -188,16 +188,16 @@ define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focu loadServers(); } }); - view.querySelector(".servers").addEventListener("click", function (e) { - var card = dom.parentWithClass(e.target, "card"); + view.querySelector('.servers').addEventListener('click', function (e) { + var card = dom.parentWithClass(e.target, 'card'); if (card) { - var url = card.getAttribute("data-url"); + var url = card.getAttribute('data-url'); if (url) { appRouter.show(url); } else { - var id = card.getAttribute("data-id"); + var id = card.getAttribute('data-id'); onServerClick(servers.filter(function (s) { return s.Id === id; })[0]); diff --git a/src/controllers/dashboard/apikeys.js b/src/controllers/dashboard/apikeys.js new file mode 100644 index 0000000000..f43bfd0329 --- /dev/null +++ b/src/controllers/dashboard/apikeys.js @@ -0,0 +1,83 @@ +define(['datetime', 'loading', 'libraryMenu', 'dom', 'globalize', 'emby-button'], function (datetime, loading, libraryMenu, dom, globalize) { + 'use strict'; + + function revoke(page, key) { + require(['confirm'], function (confirm) { + confirm(globalize.translate('MessageConfirmRevokeApiKey'), globalize.translate('HeaderConfirmRevokeApiKey')).then(function () { + loading.show(); + ApiClient.ajax({ + type: 'DELETE', + url: ApiClient.getUrl('Auth/Keys/' + key) + }).then(function () { + loadData(page); + }); + }); + }); + } + + function renderKeys(page, keys) { + var rows = keys.map(function (item) { + var html = ''; + html += '
'; + html += ''; + html += ''; + html += ''; + html += ''; + return html += ''; + }).join(''); + page.querySelector('.resultBody').innerHTML = rows; + loading.hide(); + } + + function loadData(page) { + loading.show(); + ApiClient.getJSON(ApiClient.getUrl('Auth/Keys')).then(function (result) { + renderKeys(page, result.Items); + }); + } + + function showNewKeyPrompt(page) { + require(['prompt'], function (prompt) { + prompt({ + title: globalize.translate('HeaderNewApiKey'), + label: globalize.translate('LabelAppName'), + description: globalize.translate('LabelAppNameExample') + }).then(function (value) { + ApiClient.ajax({ + type: 'POST', + url: ApiClient.getUrl('Auth/Keys', { + App: value + }) + }).then(function () { + loadData(page); + }); + }); + }); + } + + pageIdOn('pageinit', 'apiKeysPage', function () { + var page = this; + page.querySelector('.btnNewKey').addEventListener('click', function () { + showNewKeyPrompt(page); + }); + page.querySelector('.tblApiKeys').addEventListener('click', function (e) { + var btnRevoke = dom.parentWithClass(e.target, 'btnRevoke'); + + if (btnRevoke) { + revoke(page, btnRevoke.getAttribute('data-token')); + } + }); + }); + pageIdOn('pagebeforeshow', 'apiKeysPage', function () { + loadData(this); + }); +}); diff --git a/src/controllers/dashboard/dashboard.js b/src/controllers/dashboard/dashboard.js index 2901a69668..1bae9a0b34 100644 --- a/src/controllers/dashboard/dashboard.js +++ b/src/controllers/dashboard/dashboard.js @@ -1,24 +1,24 @@ -define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globalize", "date-fns", "dfnshelper", "loading", "connectionManager", "playMethodHelper", "cardBuilder", "imageLoader", "components/activitylog", "scripts/imagehelper", "indicators", "listViewStyle", "emby-button", "flexStyles", "emby-button", "emby-itemscontainer"], function (datetime, events, itemHelper, serverNotifications, dom, globalize, datefns, dfnshelper, loading, connectionManager, playMethodHelper, cardBuilder, imageLoader, ActivityLog, imageHelper, indicators) { - "use strict"; +define(['datetime', 'events', 'itemHelper', 'serverNotifications', 'dom', 'globalize', 'date-fns', 'dfnshelper', 'loading', 'connectionManager', 'playMethodHelper', 'cardBuilder', 'imageLoader', 'components/activitylog', 'scripts/imagehelper', 'indicators', 'listViewStyle', 'emby-button', 'flexStyles', 'emby-button', 'emby-itemscontainer'], function (datetime, events, itemHelper, serverNotifications, dom, globalize, datefns, dfnshelper, loading, connectionManager, playMethodHelper, cardBuilder, imageLoader, ActivityLog, imageHelper, indicators) { + 'use strict'; function showPlaybackInfo(btn, session) { - require(["alert"], function (alert) { + require(['alert'], function (alert) { var title; var text = []; var displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session); - if (displayPlayMethod === "DirectStream") { - title = globalize.translate("DirectStreaming"); - text.push(globalize.translate("DirectStreamHelp1")); - text.push("
"); - text.push(globalize.translate("DirectStreamHelp2")); - } else if (displayPlayMethod === "Transcode") { - title = globalize.translate("Transcoding"); - text.push(globalize.translate("MediaIsBeingConverted")); + if (displayPlayMethod === 'DirectStream') { + title = globalize.translate('DirectStreaming'); + text.push(globalize.translate('DirectStreamHelp1')); + text.push('
'); + text.push(globalize.translate('DirectStreamHelp2')); + } else if (displayPlayMethod === 'Transcode') { + title = globalize.translate('Transcoding'); + text.push(globalize.translate('MediaIsBeingConverted')); if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length) { - text.push("
"); - text.push(globalize.translate("LabelReasonForTranscoding")); + text.push('
'); + text.push(globalize.translate('LabelReasonForTranscoding')); session.TranscodingInfo.TranscodeReasons.forEach(function (transcodeReason) { text.push(globalize.translate(transcodeReason)); }); @@ -26,18 +26,18 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } alert({ - text: text.join("
"), + text: text.join('
'), title: title }); }); } function showSendMessageForm(btn, session) { - require(["prompt"], function (prompt) { + require(['prompt'], function (prompt) { prompt({ - title: globalize.translate("HeaderSendMessage"), - label: globalize.translate("LabelMessageText"), - confirmText: globalize.translate("ButtonSend") + title: globalize.translate('HeaderSendMessage'), + label: globalize.translate('LabelMessageText'), + confirmText: globalize.translate('ButtonSend') }).then(function (text) { if (text) { connectionManager.getApiClient(session.ServerId).sendMessageCommand(session.Id, { @@ -50,20 +50,20 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } function showOptionsMenu(btn, session) { - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { var menuItems = []; if (session.ServerId && session.DeviceId !== connectionManager.deviceId()) { menuItems.push({ - name: globalize.translate("SendMessage"), - id: "sendmessage" + name: globalize.translate('SendMessage'), + id: 'sendmessage' }); } if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length) { menuItems.push({ - name: globalize.translate("ViewPlaybackInfo"), - id: "transcodinginfo" + name: globalize.translate('ViewPlaybackInfo'), + id: 'transcodinginfo' }); } @@ -72,11 +72,11 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa positionTo: btn }).then(function (id) { switch (id) { - case "sendmessage": + case 'sendmessage': showSendMessageForm(btn, session); break; - case "transcodinginfo": + case 'transcodinginfo': showPlaybackInfo(btn, session); } }); @@ -84,28 +84,28 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } function onActiveDevicesClick(evt) { - var btn = dom.parentWithClass(evt.target, "sessionCardButton"); + var btn = dom.parentWithClass(evt.target, 'sessionCardButton'); if (btn) { - var card = dom.parentWithClass(btn, "card"); + var card = dom.parentWithClass(btn, 'card'); if (card) { var sessionId = card.id; var session = (DashboardPage.sessionsList || []).filter(function (dashboardSession) { - return "session" + dashboardSession.Id === sessionId; + return 'session' + dashboardSession.Id === sessionId; })[0]; if (session) { - if (btn.classList.contains("btnCardOptions")) { + if (btn.classList.contains('btnCardOptions')) { showOptionsMenu(btn, session); - } else if (btn.classList.contains("btnSessionInfo")) { + } else if (btn.classList.contains('btnSessionInfo')) { showPlaybackInfo(btn, session); - } else if (btn.classList.contains("btnSessionSendMessage")) { + } else if (btn.classList.contains('btnSessionSendMessage')) { showSendMessageForm(btn, session); - } else if (btn.classList.contains("btnSessionStop")) { - connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, "Stop"); - } else if (btn.classList.contains("btnSessionPlayPause") && session.PlayState) { - connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, "PlayPause"); + } else if (btn.classList.contains('btnSessionStop')) { + connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'Stop'); + } else if (btn.classList.contains('btnSessionPlayPause') && session.PlayState) { + connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'PlayPause'); } } } @@ -135,31 +135,31 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa apiClient.getLiveTvRecordings({ UserId: Dashboard.getCurrentUserId(), IsInProgress: true, - Fields: "CanDelete,PrimaryImageAspectRatio", + Fields: 'CanDelete,PrimaryImageAspectRatio', EnableTotalRecordCount: false, - EnableImageTypes: "Primary,Thumb,Backdrop" + EnableImageTypes: 'Primary,Thumb,Backdrop' }).then(function (result) { - var itemsContainer = view.querySelector(".activeRecordingItems"); + var itemsContainer = view.querySelector('.activeRecordingItems'); if (!result.Items.length) { - view.querySelector(".activeRecordingsSection").classList.add("hide"); - return void(itemsContainer.innerHTML = ""); + view.querySelector('.activeRecordingsSection').classList.add('hide'); + return void(itemsContainer.innerHTML = ''); } - view.querySelector(".activeRecordingsSection").classList.remove("hide"); + view.querySelector('.activeRecordingsSection').classList.remove('hide'); itemsContainer.innerHTML = cardBuilder.getCardsHtml({ items: result.Items, - shape: "auto", - defaultShape: "backdrop", + shape: 'auto', + defaultShape: 'backdrop', showTitle: true, showParentTitle: true, coverImage: true, cardLayout: false, centerText: true, - preferThumb: "auto", + preferThumb: 'auto', overlayText: false, overlayMoreButton: true, - action: "none", + action: 'none', centerPlayButton: true }); imageLoader.lazyChildren(itemsContainer); @@ -168,28 +168,22 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa function reloadSystemInfo(view, apiClient) { apiClient.getSystemInfo().then(function (systemInfo) { - view.querySelector("#serverName").innerHTML = globalize.translate("DashboardServerName", systemInfo.ServerName); - var localizedVersion = globalize.translate("DashboardVersionNumber", systemInfo.Version); + view.querySelector('#serverName').innerHTML = globalize.translate('DashboardServerName', systemInfo.ServerName); + var localizedVersion = globalize.translate('DashboardVersionNumber', systemInfo.Version); - if (systemInfo.SystemUpdateLevel !== "Release") { - localizedVersion += " " + systemInfo.SystemUpdateLevel; + if (systemInfo.SystemUpdateLevel !== 'Release') { + localizedVersion += ' ' + systemInfo.SystemUpdateLevel; } - view.querySelector("#versionNumber").innerHTML = localizedVersion; - view.querySelector("#operatingSystem").innerHTML = globalize.translate("DashboardOperatingSystem", systemInfo.OperatingSystem); - view.querySelector("#architecture").innerHTML = globalize.translate("DashboardArchitecture", systemInfo.SystemArchitecture); + view.querySelector('#versionNumber').innerHTML = localizedVersion; + view.querySelector('#operatingSystem').innerHTML = globalize.translate('DashboardOperatingSystem', systemInfo.OperatingSystem); + view.querySelector('#architecture').innerHTML = globalize.translate('DashboardArchitecture', systemInfo.SystemArchitecture); - if (systemInfo.CanSelfRestart) { - view.querySelector("#btnRestartServer").classList.remove("hide"); - } else { - view.querySelector("#btnRestartServer").classList.add("hide"); - } - - view.querySelector("#cachePath").innerHTML = systemInfo.CachePath; - view.querySelector("#logPath").innerHTML = systemInfo.LogPath; - view.querySelector("#transcodePath").innerHTML = systemInfo.TranscodingTempPath; - view.querySelector("#metadataPath").innerHTML = systemInfo.InternalMetadataPath; - view.querySelector("#webPath").innerHTML = systemInfo.WebPath; + view.querySelector('#cachePath').innerHTML = systemInfo.CachePath; + view.querySelector('#logPath').innerHTML = systemInfo.LogPath; + view.querySelector('#transcodePath').innerHTML = systemInfo.TranscodingTempPath; + view.querySelector('#metadataPath').innerHTML = systemInfo.InternalMetadataPath; + view.querySelector('#webPath').innerHTML = systemInfo.WebPath; }); } @@ -211,25 +205,25 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } function renderActiveConnections(view, sessions) { - var html = ""; + var html = ''; DashboardPage.sessionsList = sessions; - var parentElement = view.querySelector(".activeDevices"); - var cardElem = parentElement.querySelector(".card"); + var parentElement = view.querySelector('.activeDevices'); + var cardElem = parentElement.querySelector('.card'); if (cardElem) { - cardElem.classList.add("deadSession"); + cardElem.classList.add('deadSession'); } for (var i = 0, length = sessions.length; i < length; i++) { var session = sessions[i]; - var rowId = "session" + session.Id; - var elem = view.querySelector("#" + rowId); + var rowId = 'session' + session.Id; + var elem = view.querySelector('#' + rowId); if (elem) { DashboardPage.updateSession(elem, session); } else { var nowPlayingItem = session.NowPlayingItem; - var className = "scalableCard card activeSession backdropCard backdropCard-scalable"; + var className = 'scalableCard card activeSession backdropCard backdropCard-scalable'; html += '
'; html += '
'; html += '
'; @@ -253,65 +247,65 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } html += '
'; - html += '
' + session.DeviceName + "
"; - html += '
' + DashboardPage.getAppSecondaryText(session) + "
"; - html += "
"; - html += "
"; + html += '
' + session.DeviceName + '
'; + html += '
' + DashboardPage.getAppSecondaryText(session) + '
'; + html += '
'; + html += '
'; html += '
'; var nowPlayingName = DashboardPage.getNowPlayingName(session); html += '
'; html += nowPlayingName.html; - html += "
"; - html += '
' + DashboardPage.getSessionNowPlayingTime(session) + "
"; + html += '
'; + html += '
' + DashboardPage.getSessionNowPlayingTime(session) + '
'; html += ''; if (nowPlayingItem && nowPlayingItem.RunTimeTicks) { var percent = 100 * (session.PlayState.PositionTicks || 0) / nowPlayingItem.RunTimeTicks; html += indicators.getProgressHtml(percent, { - containerClass: "playbackProgress" + containerClass: 'playbackProgress' }); } else { // need to leave the element in just in case the device starts playback html += indicators.getProgressHtml(0, { - containerClass: "playbackProgress hide" + containerClass: 'playbackProgress hide' }); } if (session.TranscodingInfo && session.TranscodingInfo.CompletionPercentage) { var percent = session.TranscodingInfo.CompletionPercentage.toFixed(1); html += indicators.getProgressHtml(percent, { - containerClass: "transcodingProgress" + containerClass: 'transcodingProgress' }); } else { // same issue as playbackProgress element above html += indicators.getProgressHtml(0, { - containerClass: "transcodingProgress hide" + containerClass: 'transcodingProgress hide' }); } - html += ""; - html += ""; - html += ""; + html += ''; + html += ''; + html += ''; html += '
'; html += '
'; - var btnCssClass = session.ServerId && session.NowPlayingItem && session.SupportsRemoteControl ? "" : " hide"; - var playIcon = session.PlayState.IsPaused ? 'pause' : 'play'; + var btnCssClass = session.ServerId && session.NowPlayingItem && session.SupportsRemoteControl ? '' : ' hide'; + const playIcon = session.PlayState.IsPaused ? 'pause' : 'play_arrow'; - html += ''; - html += ''; + html += ''; + html += ''; - btnCssClass = session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length ? "" : " hide"; - html += ''; + btnCssClass = session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length ? '' : ' hide'; + html += ''; - btnCssClass = session.ServerId && -1 !== session.SupportedCommands.indexOf("DisplayMessage") && session.DeviceId !== connectionManager.deviceId() ? "" : " hide"; - html += ''; - html += "
"; + btnCssClass = session.ServerId && -1 !== session.SupportedCommands.indexOf('DisplayMessage') && session.DeviceId !== connectionManager.deviceId() ? '' : ' hide'; + html += ''; + html += '
'; html += '
'; html += DashboardPage.getSessionNowPlayingStreamInfo(session); - html += "
"; + html += ''; html += '
'; var userImage = DashboardPage.getUserImage(session); @@ -319,16 +313,16 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa html += '
'; html += DashboardPage.getUsersHtml(session); - html += "
"; - html += "
"; - html += ""; - html += ""; - html += ""; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; } } - parentElement.insertAdjacentHTML("beforeend", html); - var deadSessionElem = parentElement.querySelector(".deadSession"); + parentElement.insertAdjacentHTML('beforeend', html); + var deadSessionElem = parentElement.querySelector('.deadSession'); if (deadSessionElem) { deadSessionElem.parentNode.removeChild(deadSessionElem); @@ -336,9 +330,9 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } function renderRunningTasks(view, tasks) { - var html = ""; + var html = ''; tasks = tasks.filter(function (task) { - if ("Idle" != task.State) { + if ('Idle' != task.State) { return !task.IsHidden; } @@ -346,59 +340,59 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa }); if (tasks.length) { - view.querySelector(".runningTasksContainer").classList.remove("hide"); + view.querySelector('.runningTasksContainer').classList.remove('hide'); } else { - view.querySelector(".runningTasksContainer").classList.add("hide"); + view.querySelector('.runningTasksContainer').classList.add('hide'); } for (var i = 0, length = tasks.length; i < length; i++) { var task = tasks[i]; - html += "

"; - html += task.Name + "
"; + html += '

'; + html += task.Name + '
'; - if (task.State === "Running") { + if (task.State === 'Running') { var progress = (task.CurrentProgressPercentage || 0).toFixed(1); html += ''; - html += progress + "%"; - html += ""; - html += "" + progress + "%"; - html += ''; - } else if (task.State === "Cancelling") { - html += '' + globalize.translate("LabelStopping") + ""; + html += progress + '%'; + html += ''; + html += "" + progress + '%'; + html += ''; + } else if (task.State === 'Cancelling') { + html += '' + globalize.translate('LabelStopping') + ''; } - html += "

"; + html += '

'; } - view.querySelector("#divRunningTasks").innerHTML = html; + view.querySelector('#divRunningTasks').innerHTML = html; } window.DashboardPage = { startInterval: function (apiClient) { - apiClient.sendMessage("SessionsStart", "0,1500"); - apiClient.sendMessage("ScheduledTasksInfoStart", "0,1000"); + apiClient.sendMessage('SessionsStart', '0,1500'); + apiClient.sendMessage('ScheduledTasksInfoStart', '0,1000'); }, stopInterval: function (apiClient) { - apiClient.sendMessage("SessionsStop"); - apiClient.sendMessage("ScheduledTasksInfoStop"); + apiClient.sendMessage('SessionsStop'); + apiClient.sendMessage('ScheduledTasksInfoStop'); }, getSessionNowPlayingStreamInfo: function (session) { - var html = ""; + var html = ''; var showTranscodingInfo = false; var displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session); - if (displayPlayMethod === "DirectStream") { - html += globalize.translate("DirectStreaming"); - } else if (displayPlayMethod === "Transcode") { - html += globalize.translate("Transcoding"); + if (displayPlayMethod === 'DirectStream') { + html += globalize.translate('DirectStreaming'); + } else if (displayPlayMethod === 'Transcode') { + html += globalize.translate('Transcoding'); if (session.TranscodingInfo && session.TranscodingInfo.Framerate) { - html += " (" + session.TranscodingInfo.Framerate + " fps)"; + html += ' (' + session.TranscodingInfo.Framerate + ' fps)'; } showTranscodingInfo = true; - } else if (displayPlayMethod === "DirectPlay") { - html += globalize.translate("DirectPlaying"); + } else if (displayPlayMethod === 'DirectPlay') { + html += globalize.translate('DirectPlaying'); } if (showTranscodingInfo) { @@ -407,9 +401,9 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (session.TranscodingInfo) { if (session.TranscodingInfo.Bitrate) { if (session.TranscodingInfo.Bitrate > 1e6) { - line.push((session.TranscodingInfo.Bitrate / 1e6).toFixed(1) + " Mbps"); + line.push((session.TranscodingInfo.Bitrate / 1e6).toFixed(1) + ' Mbps'); } else { - line.push(Math.floor(session.TranscodingInfo.Bitrate / 1e3) + " Kbps"); + line.push(Math.floor(session.TranscodingInfo.Bitrate / 1e3) + ' Kbps'); } } @@ -427,7 +421,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } if (line.length) { - html += " - " + line.join(" "); + html += ' - ' + line.join(' '); } } @@ -435,43 +429,43 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa }, getSessionNowPlayingTime: function (session) { var nowPlayingItem = session.NowPlayingItem; - var html = ""; + var html = ''; if (nowPlayingItem) { if (session.PlayState.PositionTicks) { html += datetime.getDisplayRunningTime(session.PlayState.PositionTicks); } else { - html += "0:00"; + html += '0:00'; } - html += " / "; + html += ' / '; if (nowPlayingItem && nowPlayingItem.RunTimeTicks) { html += datetime.getDisplayRunningTime(nowPlayingItem.RunTimeTicks); } else { - html += "0:00"; + html += '0:00'; } } return html; }, getAppSecondaryText: function (session) { - return session.Client + " " + session.ApplicationVersion; + return session.Client + ' ' + session.ApplicationVersion; }, getNowPlayingName: function (session) { - var imgUrl = ""; + var imgUrl = ''; var nowPlayingItem = session.NowPlayingItem; // FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix // how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences if (!nowPlayingItem) { return { - html: globalize.translate("LastSeen", datefns.formatDistanceToNow(Date.parse(session.LastActivityDate), dfnshelper.localeWithSuffix)), + html: globalize.translate('LastSeen', datefns.formatDistanceToNow(Date.parse(session.LastActivityDate), dfnshelper.localeWithSuffix)), image: imgUrl }; } var topText = itemHelper.getDisplayName(nowPlayingItem); - var bottomText = ""; + var bottomText = ''; if (nowPlayingItem.Artists && nowPlayingItem.Artists.length) { bottomText = topText; @@ -490,14 +484,14 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa tag: nowPlayingItem.ImageTags.Logo, maxHeight: 24, maxWidth: 130, - type: "Logo" + type: 'Logo' }); } else if (nowPlayingItem.ParentLogoImageTag) { imgUrl = ApiClient.getScaledImageUrl(nowPlayingItem.ParentLogoItemId, { tag: nowPlayingItem.ParentLogoImageTag, maxHeight: 24, maxWidth: 130, - type: "Logo" + type: 'Logo' }); } @@ -506,7 +500,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } return { - html: bottomText ? topText + "
" + bottomText : topText, + html: bottomText ? topText + '
' + bottomText : topText, image: imgUrl }; }, @@ -521,106 +515,104 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa html.push(session.AdditionalUsers[i].UserName); } - return html.join(", "); + return html.join(', '); }, getUserImage: function (session) { if (session.UserId && session.UserPrimaryImageTag) { return ApiClient.getUserImageUrl(session.UserId, { tag: session.UserPrimaryImageTag, - type: "Primary" + type: 'Primary' }); } return null; }, updateSession: function (row, session) { - row.classList.remove("deadSession"); + row.classList.remove('deadSession'); var nowPlayingItem = session.NowPlayingItem; if (nowPlayingItem) { - row.classList.add("playingSession"); + row.classList.add('playingSession'); } else { - row.classList.remove("playingSession"); + row.classList.remove('playingSession'); } - if (session.ServerId && -1 !== session.SupportedCommands.indexOf("DisplayMessage") && session.DeviceId !== connectionManager.deviceId()) { - row.querySelector(".btnSessionSendMessage").classList.remove("hide"); + if (session.ServerId && -1 !== session.SupportedCommands.indexOf('DisplayMessage') && session.DeviceId !== connectionManager.deviceId()) { + row.querySelector('.btnSessionSendMessage').classList.remove('hide'); } else { - row.querySelector(".btnSessionSendMessage").classList.add("hide"); + row.querySelector('.btnSessionSendMessage').classList.add('hide'); } if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons.length) { - row.querySelector(".btnSessionInfo").classList.remove("hide"); + row.querySelector('.btnSessionInfo').classList.remove('hide'); } else { - row.querySelector(".btnSessionInfo").classList.add("hide"); + row.querySelector('.btnSessionInfo').classList.add('hide'); } - var btnSessionPlayPause = row.querySelector(".btnSessionPlayPause"); + var btnSessionPlayPause = row.querySelector('.btnSessionPlayPause'); if (session.ServerId && nowPlayingItem && session.SupportsRemoteControl && session.DeviceId !== connectionManager.deviceId()) { - btnSessionPlayPause.classList.remove("hide"); - row.querySelector(".btnSessionStop").classList.remove("hide"); + btnSessionPlayPause.classList.remove('hide'); + row.querySelector('.btnSessionStop').classList.remove('hide'); } else { - btnSessionPlayPause.classList.add("hide"); - row.querySelector(".btnSessionStop").classList.add("hide"); + btnSessionPlayPause.classList.add('hide'); + row.querySelector('.btnSessionStop').classList.add('hide'); } - if (session.PlayState && session.PlayState.IsPaused) { - btnSessionPlayPause.querySelector("i").innerHTML = ""; - } else { - btnSessionPlayPause.querySelector("i").innerHTML = "pause"; - } + const btnSessionPlayPauseIcon = btnSessionPlayPause.querySelector('.material-icons'); + btnSessionPlayPauseIcon.classList.remove('play_arrow', 'pause'); + btnSessionPlayPauseIcon.classList.add(session.PlayState && session.PlayState.IsPaused ? 'play_arrow' : 'pause'); - row.querySelector(".sessionNowPlayingStreamInfo").innerHTML = DashboardPage.getSessionNowPlayingStreamInfo(session); - row.querySelector(".sessionNowPlayingTime").innerHTML = DashboardPage.getSessionNowPlayingTime(session); - row.querySelector(".sessionUserName").innerHTML = DashboardPage.getUsersHtml(session); - row.querySelector(".sessionAppSecondaryText").innerHTML = DashboardPage.getAppSecondaryText(session); - row.querySelector(".sessionTranscodingFramerate").innerHTML = session.TranscodingInfo && session.TranscodingInfo.Framerate ? session.TranscodingInfo.Framerate + " fps" : ""; + row.querySelector('.sessionNowPlayingStreamInfo').innerHTML = DashboardPage.getSessionNowPlayingStreamInfo(session); + row.querySelector('.sessionNowPlayingTime').innerHTML = DashboardPage.getSessionNowPlayingTime(session); + row.querySelector('.sessionUserName').innerHTML = DashboardPage.getUsersHtml(session); + row.querySelector('.sessionAppSecondaryText').innerHTML = DashboardPage.getAppSecondaryText(session); + row.querySelector('.sessionTranscodingFramerate').innerHTML = session.TranscodingInfo && session.TranscodingInfo.Framerate ? session.TranscodingInfo.Framerate + ' fps' : ''; var nowPlayingName = DashboardPage.getNowPlayingName(session); - var nowPlayingInfoElem = row.querySelector(".sessionNowPlayingInfo"); + var nowPlayingInfoElem = row.querySelector('.sessionNowPlayingInfo'); - if (!(nowPlayingName.image && nowPlayingName.image == nowPlayingInfoElem.getAttribute("data-imgsrc"))) { + if (!(nowPlayingName.image && nowPlayingName.image == nowPlayingInfoElem.getAttribute('data-imgsrc'))) { nowPlayingInfoElem.innerHTML = nowPlayingName.html; - nowPlayingInfoElem.setAttribute("data-imgsrc", nowPlayingName.image || ""); + nowPlayingInfoElem.setAttribute('data-imgsrc', nowPlayingName.image || ''); } - var playbackProgressElem = row.querySelector(".playbackProgress"); + var playbackProgressElem = row.querySelector('.playbackProgress'); if (nowPlayingItem && nowPlayingItem.RunTimeTicks) { var percent = 100 * (session.PlayState.PositionTicks || 0) / nowPlayingItem.RunTimeTicks; playbackProgressElem.outerHTML = indicators.getProgressHtml(percent, { - containerClass: "playbackProgress" + containerClass: 'playbackProgress' }); } else { playbackProgressElem.outerHTML = indicators.getProgressHtml(0, { - containerClass: "playbackProgress hide" + containerClass: 'playbackProgress hide' }); } - var transcodingProgress = row.querySelector(".transcodingProgress"); + var transcodingProgress = row.querySelector('.transcodingProgress'); if (session.TranscodingInfo && session.TranscodingInfo.CompletionPercentage) { var percent = session.TranscodingInfo.CompletionPercentage.toFixed(1); transcodingProgress.outerHTML = indicators.getProgressHtml(percent, { - containerClass: "transcodingProgress" + containerClass: 'transcodingProgress' }); } else { transcodingProgress.outerHTML = indicators.getProgressHtml(0, { - containerClass: "transcodingProgress hide" + containerClass: 'transcodingProgress hide' }); } - var imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem) || ""; - var imgElem = row.querySelector(".sessionNowPlayingContent"); + var imgUrl = DashboardPage.getNowPlayingImageUrl(nowPlayingItem) || ''; + var imgElem = row.querySelector('.sessionNowPlayingContent'); - if (imgUrl != imgElem.getAttribute("data-src")) { - imgElem.style.backgroundImage = imgUrl ? "url('" + imgUrl + "')" : ""; - imgElem.setAttribute("data-src", imgUrl); + if (imgUrl != imgElem.getAttribute('data-src')) { + imgElem.style.backgroundImage = imgUrl ? "url('" + imgUrl + "')" : ''; + imgElem.setAttribute('data-src', imgUrl); if (imgUrl) { - imgElem.classList.add("sessionNowPlayingContent-withbackground"); + imgElem.classList.add('sessionNowPlayingContent-withbackground'); } else { - imgElem.classList.remove("sessionNowPlayingContent-withbackground"); + imgElem.classList.remove('sessionNowPlayingContent-withbackground'); } } }, @@ -634,7 +626,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (item && item.BackdropImageTags && item.BackdropImageTags.length) { return ApiClient.getScaledImageUrl(item.Id, { maxWidth: Math.round(dom.getScreenWidth() * 0.20), - type: "Backdrop", + type: 'Backdrop', tag: item.BackdropImageTags[0] }); } @@ -642,7 +634,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (item && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) { return ApiClient.getScaledImageUrl(item.ParentBackdropItemId, { maxWidth: Math.round(dom.getScreenWidth() * 0.20), - type: "Backdrop", + type: 'Backdrop', tag: item.ParentBackdropImageTags[0] }); } @@ -650,7 +642,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (item && item.BackdropImageTag) { return ApiClient.getScaledImageUrl(item.BackdropItemId, { maxWidth: Math.round(dom.getScreenWidth() * 0.20), - type: "Backdrop", + type: 'Backdrop', tag: item.BackdropImageTag }); } @@ -660,7 +652,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (item && imageTags.Thumb) { return ApiClient.getScaledImageUrl(item.Id, { maxWidth: Math.round(dom.getScreenWidth() * 0.20), - type: "Thumb", + type: 'Thumb', tag: imageTags.Thumb }); } @@ -668,7 +660,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (item && item.ParentThumbImageTag) { return ApiClient.getScaledImageUrl(item.ParentThumbItemId, { maxWidth: Math.round(dom.getScreenWidth() * 0.20), - type: "Thumb", + type: 'Thumb', tag: item.ParentThumbImageTag }); } @@ -676,7 +668,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (item && item.ThumbImageTag) { return ApiClient.getScaledImageUrl(item.ThumbItemId, { maxWidth: Math.round(dom.getScreenWidth() * 0.20), - type: "Thumb", + type: 'Thumb', tag: item.ThumbImageTag }); } @@ -684,7 +676,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (item && imageTags.Primary) { return ApiClient.getScaledImageUrl(item.Id, { maxWidth: Math.round(dom.getScreenWidth() * 0.20), - type: "Primary", + type: 'Primary', tag: imageTags.Primary }); } @@ -692,7 +684,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (item && item.PrimaryImageTag) { return ApiClient.getScaledImageUrl(item.PrimaryImageItemId, { maxWidth: Math.round(dom.getScreenWidth() * 0.20), - type: "Primary", + type: 'Primary', tag: item.PrimaryImageTag }); } @@ -700,46 +692,46 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa if (item && item.AlbumPrimaryImageTag) { return ApiClient.getScaledImageUrl(item.AlbumId, { maxWidth: Math.round(dom.getScreenWidth() * 0.20), - type: "Primary", + type: 'Primary', tag: item.AlbumPrimaryImageTag }); } return null; }, - systemUpdateTaskKey: "SystemUpdateTask", + systemUpdateTaskKey: 'SystemUpdateTask', stopTask: function (btn, id) { - var page = dom.parentWithClass(btn, "page"); + var page = dom.parentWithClass(btn, 'page'); ApiClient.stopScheduledTask(id).then(function () { pollForInfo(page, ApiClient); }); }, restart: function (btn) { - require(["confirm"], function (confirm) { + require(['confirm'], function (confirm) { confirm({ - title: globalize.translate("HeaderRestart"), - text: globalize.translate("MessageConfirmRestart"), - confirmText: globalize.translate("ButtonRestart"), - primary: "delete" + title: globalize.translate('HeaderRestart'), + text: globalize.translate('MessageConfirmRestart'), + confirmText: globalize.translate('ButtonRestart'), + primary: 'delete' }).then(function () { - var page = dom.parentWithClass(btn, "page"); - page.querySelector("#btnRestartServer").disabled = true; - page.querySelector("#btnShutdown").disabled = true; + var page = dom.parentWithClass(btn, 'page'); + page.querySelector('#btnRestartServer').disabled = true; + page.querySelector('#btnShutdown').disabled = true; ApiClient.restartServer(); }); }); }, shutdown: function (btn) { - require(["confirm"], function (confirm) { + require(['confirm'], function (confirm) { confirm({ - title: globalize.translate("HeaderShutdown"), - text: globalize.translate("MessageConfirmShutdown"), - confirmText: globalize.translate("ButtonShutdown"), - primary: "delete" + title: globalize.translate('HeaderShutdown'), + text: globalize.translate('MessageConfirmShutdown'), + confirmText: globalize.translate('ButtonShutdown'), + primary: 'delete' }).then(function () { - var page = dom.parentWithClass(btn, "page"); - page.querySelector("#btnRestartServer").disabled = true; - page.querySelector("#btnShutdown").disabled = true; + var page = dom.parentWithClass(btn, 'page'); + page.querySelector('#btnRestartServer').disabled = true; + page.querySelector('#btnShutdown').disabled = true; ApiClient.shutdownServer(); }); }); @@ -785,8 +777,8 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa } var serverId = ApiClient.serverId(); - view.querySelector(".activeDevices").addEventListener("click", onActiveDevicesClick); - view.addEventListener("viewshow", function () { + view.querySelector('.activeDevices').addEventListener('click', onActiveDevicesClick); + view.addEventListener('viewshow', function () { var page = this; var apiClient = ApiClient; @@ -794,28 +786,28 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa loading.show(); pollForInfo(page, apiClient); DashboardPage.startInterval(apiClient); - events.on(serverNotifications, "RestartRequired", onRestartRequired); - events.on(serverNotifications, "ServerShuttingDown", onServerShuttingDown); - events.on(serverNotifications, "ServerRestarting", onServerRestarting); - events.on(serverNotifications, "PackageInstalling", onPackageInstalling); - events.on(serverNotifications, "PackageInstallationCompleted", onPackageInstallationCompleted); - events.on(serverNotifications, "Sessions", onSessionsUpdate); - events.on(serverNotifications, "ScheduledTasksInfo", onScheduledTasksUpdate); + events.on(serverNotifications, 'RestartRequired', onRestartRequired); + events.on(serverNotifications, 'ServerShuttingDown', onServerShuttingDown); + events.on(serverNotifications, 'ServerRestarting', onServerRestarting); + events.on(serverNotifications, 'PackageInstalling', onPackageInstalling); + events.on(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted); + events.on(serverNotifications, 'Sessions', onSessionsUpdate); + events.on(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); DashboardPage.lastAppUpdateCheck = null; reloadSystemInfo(page, ApiClient); if (!page.userActivityLog) { page.userActivityLog = new ActivityLog({ serverId: ApiClient.serverId(), - element: page.querySelector(".userActivityItems") + element: page.querySelector('.userActivityItems') }); } - if (ApiClient.isMinServerVersion("3.4.1.25")) { + if (ApiClient.isMinServerVersion('3.4.1.25')) { if (!page.serverActivityLog) { page.serverActivityLog = new ActivityLog({ serverId: ApiClient.serverId(), - element: page.querySelector(".serverActivityItems") + element: page.querySelector('.serverActivityItems') }); } } @@ -824,21 +816,21 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa loading.hide(); } }); - view.addEventListener("viewbeforehide", function () { + view.addEventListener('viewbeforehide', function () { var apiClient = ApiClient; - events.off(serverNotifications, "RestartRequired", onRestartRequired); - events.off(serverNotifications, "ServerShuttingDown", onServerShuttingDown); - events.off(serverNotifications, "ServerRestarting", onServerRestarting); - events.off(serverNotifications, "PackageInstalling", onPackageInstalling); - events.off(serverNotifications, "PackageInstallationCompleted", onPackageInstallationCompleted); - events.off(serverNotifications, "Sessions", onSessionsUpdate); - events.off(serverNotifications, "ScheduledTasksInfo", onScheduledTasksUpdate); + events.off(serverNotifications, 'RestartRequired', onRestartRequired); + events.off(serverNotifications, 'ServerShuttingDown', onServerShuttingDown); + events.off(serverNotifications, 'ServerRestarting', onServerRestarting); + events.off(serverNotifications, 'PackageInstalling', onPackageInstalling); + events.off(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted); + events.off(serverNotifications, 'Sessions', onSessionsUpdate); + events.off(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); if (apiClient) { DashboardPage.stopInterval(apiClient); } }); - view.addEventListener("viewdestroy", function () { + view.addEventListener('viewdestroy', function () { var page = this; var userActivityLog = page.userActivityLog; diff --git a/src/controllers/dashboard/devices/device.js b/src/controllers/dashboard/devices/device.js new file mode 100644 index 0000000000..9ff3f5902e --- /dev/null +++ b/src/controllers/dashboard/devices/device.js @@ -0,0 +1,50 @@ +define(['loading', 'libraryMenu', 'dom', 'emby-input', 'emby-button'], function (loading, libraryMenu, dom) { + 'use strict'; + + function load(page, device, deviceOptions) { + page.querySelector('#txtCustomName', page).value = deviceOptions.CustomName || ''; + page.querySelector('.reportedName', page).innerHTML = device.Name || ''; + } + + function loadData() { + var page = this; + loading.show(); + var id = getParameterByName('id'); + var promise1 = ApiClient.getJSON(ApiClient.getUrl('Devices/Info', { + Id: id + })); + var promise2 = ApiClient.getJSON(ApiClient.getUrl('Devices/Options', { + Id: id + })); + Promise.all([promise1, promise2]).then(function (responses) { + load(page, responses[0], responses[1]); + loading.hide(); + }); + } + + function save(page) { + var id = getParameterByName('id'); + ApiClient.ajax({ + url: ApiClient.getUrl('Devices/Options', { + Id: id + }), + type: 'POST', + data: JSON.stringify({ + CustomName: page.querySelector('#txtCustomName').value + }), + contentType: 'application/json' + }).then(Dashboard.processServerConfigurationUpdateResult); + } + + function onSubmit(e) { + var form = this; + save(dom.parentWithClass(form, 'page')); + e.preventDefault(); + return false; + } + + return function (view, params) { + view.querySelector('form').addEventListener('submit', onSubmit); + view.addEventListener('viewshow', loadData); + }; +}); diff --git a/src/controllers/devices.js b/src/controllers/dashboard/devices/devices.js similarity index 56% rename from src/controllers/devices.js rename to src/controllers/dashboard/devices/devices.js index 8dd665f7fa..0dea81c756 100644 --- a/src/controllers/devices.js +++ b/src/controllers/dashboard/devices/devices.js @@ -1,24 +1,24 @@ -define(["loading", "dom", "libraryMenu", "globalize", "scripts/imagehelper", "date-fns", "dfnshelper", "emby-button", "emby-itemscontainer", "cardStyle"], function (loading, dom, libraryMenu, globalize, imageHelper, datefns, dfnshelper) { - "use strict"; +define(['loading', 'dom', 'libraryMenu', 'globalize', 'scripts/imagehelper', 'date-fns', 'dfnshelper', 'emby-button', 'emby-itemscontainer', 'cardStyle'], function (loading, dom, libraryMenu, globalize, imageHelper, datefns, dfnshelper) { + 'use strict'; function canDelete(deviceId) { return deviceId !== ApiClient.deviceId(); } function deleteDevice(page, id) { - var msg = globalize.translate("DeleteDeviceConfirmation"); + var msg = globalize.translate('DeleteDeviceConfirmation'); - require(["confirm"], function (confirm) { + require(['confirm'], function (confirm) { confirm({ text: msg, - title: globalize.translate("HeaderDeleteDevice"), - confirmText: globalize.translate("ButtonDelete"), - primary: "delete" + title: globalize.translate('HeaderDeleteDevice'), + confirmText: globalize.translate('ButtonDelete'), + primary: 'delete' }).then(function () { loading.show(); ApiClient.ajax({ - type: "DELETE", - url: ApiClient.getUrl("Devices", { + type: 'DELETE', + url: ApiClient.getUrl('Devices', { Id: id }) }).then(function () { @@ -33,31 +33,31 @@ define(["loading", "dom", "libraryMenu", "globalize", "scripts/imagehelper", "da if (canEdit) { menuItems.push({ - name: globalize.translate("Edit"), - id: "open", - icon: "mode_edit" + name: globalize.translate('Edit'), + id: 'open', + icon: 'mode_edit' }); } if (canDelete(deviceId)) { menuItems.push({ - name: globalize.translate("Delete"), - id: "delete", - icon: "delete" + name: globalize.translate('Delete'), + id: 'delete', + icon: 'delete' }); } - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, positionTo: btn, callback: function (id) { switch (id) { - case "open": - Dashboard.navigate("device.html?id=" + deviceId); + case 'open': + Dashboard.navigate('device.html?id=' + deviceId); break; - case "delete": + case 'delete': deleteDevice(view, deviceId); } } @@ -66,74 +66,74 @@ define(["loading", "dom", "libraryMenu", "globalize", "scripts/imagehelper", "da } function load(page, devices) { - var html = ""; + var html = ''; html += devices.map(function (device) { - var deviceHtml = ""; + var deviceHtml = ''; deviceHtml += "'; deviceHtml += '
'; if (canEdit || canDelete(device.Id)) { deviceHtml += '
'; - deviceHtml += ''; - deviceHtml += "
"; + deviceHtml += ''; + deviceHtml += '
'; } deviceHtml += "
"; deviceHtml += device.Name; - deviceHtml += "
"; + deviceHtml += ''; deviceHtml += "
"; - deviceHtml += device.AppName + " " + device.AppVersion; - deviceHtml += "
"; + deviceHtml += device.AppName + ' ' + device.AppVersion; + deviceHtml += ''; deviceHtml += "
"; if (device.LastUserName) { deviceHtml += device.LastUserName; - deviceHtml += ", " + datefns.formatDistanceToNow(Date.parse(device.DateLastActivity), dfnshelper.localeWithSuffix); + deviceHtml += ', ' + datefns.formatDistanceToNow(Date.parse(device.DateLastActivity), dfnshelper.localeWithSuffix); } - deviceHtml += " "; - deviceHtml += "
"; - deviceHtml += ""; - deviceHtml += ""; - deviceHtml += ""; + deviceHtml += ' '; + deviceHtml += ''; + deviceHtml += ''; + deviceHtml += ''; + deviceHtml += ''; return deviceHtml; - }).join(""); - page.querySelector(".devicesList").innerHTML = html; + }).join(''); + page.querySelector('.devicesList').innerHTML = html; } function loadData(page) { loading.show(); - ApiClient.getJSON(ApiClient.getUrl("Devices")).then(function (result) { + ApiClient.getJSON(ApiClient.getUrl('Devices')).then(function (result) { load(page, result.Items); loading.hide(); }); } - var canEdit = ApiClient.isMinServerVersion("3.4.1.31"); + var canEdit = ApiClient.isMinServerVersion('3.4.1.31'); return function (view, params) { - view.querySelector(".devicesList").addEventListener("click", function (e) { - var btnDeviceMenu = dom.parentWithClass(e.target, "btnDeviceMenu"); + view.querySelector('.devicesList').addEventListener('click', function (e) { + var btnDeviceMenu = dom.parentWithClass(e.target, 'btnDeviceMenu'); if (btnDeviceMenu) { - showDeviceMenu(view, btnDeviceMenu, btnDeviceMenu.getAttribute("data-id")); + showDeviceMenu(view, btnDeviceMenu, btnDeviceMenu.getAttribute('data-id')); } }); - view.addEventListener("viewshow", function () { + view.addEventListener('viewshow', function () { loadData(this); }); }; diff --git a/src/controllers/dashboard/dlna/profile.js b/src/controllers/dashboard/dlna/profile.js new file mode 100644 index 0000000000..81eb099f50 --- /dev/null +++ b/src/controllers/dashboard/dlna/profile.js @@ -0,0 +1,829 @@ +define(['jQuery', 'loading', 'globalize', 'emby-select', 'emby-button', 'emby-input', 'emby-checkbox', 'listViewStyle', 'emby-button'], function ($, loading, globalize) { + 'use strict'; + + function loadProfile(page) { + loading.show(); + var promise1 = getProfile(); + var promise2 = ApiClient.getUsers(); + Promise.all([promise1, promise2]).then(function (responses) { + currentProfile = responses[0]; + renderProfile(page, currentProfile, responses[1]); + loading.hide(); + }); + } + + function getProfile() { + var id = getParameterByName('id'); + var url = id ? 'Dlna/Profiles/' + id : 'Dlna/Profiles/Default'; + return ApiClient.getJSON(ApiClient.getUrl(url)); + } + + function renderProfile(page, profile, users) { + $('#txtName', page).val(profile.Name); + $('.chkMediaType', page).each(function () { + this.checked = -1 != (profile.SupportedMediaTypes || '').split(',').indexOf(this.getAttribute('data-value')); + }); + $('#chkEnableAlbumArtInDidl', page).checked = profile.EnableAlbumArtInDidl; + $('#chkEnableSingleImageLimit', page).checked = profile.EnableSingleAlbumArtLimit; + renderXmlDocumentAttributes(page, profile.XmlRootAttributes || []); + var idInfo = profile.Identification || {}; + renderIdentificationHeaders(page, idInfo.Headers || []); + renderSubtitleProfiles(page, profile.SubtitleProfiles || []); + $('#txtInfoFriendlyName', page).val(profile.FriendlyName || ''); + $('#txtInfoModelName', page).val(profile.ModelName || ''); + $('#txtInfoModelNumber', page).val(profile.ModelNumber || ''); + $('#txtInfoModelDescription', page).val(profile.ModelDescription || ''); + $('#txtInfoModelUrl', page).val(profile.ModelUrl || ''); + $('#txtInfoManufacturer', page).val(profile.Manufacturer || ''); + $('#txtInfoManufacturerUrl', page).val(profile.ManufacturerUrl || ''); + $('#txtInfoSerialNumber', page).val(profile.SerialNumber || ''); + $('#txtIdFriendlyName', page).val(idInfo.FriendlyName || ''); + $('#txtIdModelName', page).val(idInfo.ModelName || ''); + $('#txtIdModelNumber', page).val(idInfo.ModelNumber || ''); + $('#txtIdModelDescription', page).val(idInfo.ModelDescription || ''); + $('#txtIdModelUrl', page).val(idInfo.ModelUrl || ''); + $('#txtIdManufacturer', page).val(idInfo.Manufacturer || ''); + $('#txtIdManufacturerUrl', page).val(idInfo.ManufacturerUrl || ''); + $('#txtIdSerialNumber', page).val(idInfo.SerialNumber || ''); + $('#txtIdDeviceDescription', page).val(idInfo.DeviceDescription || ''); + $('#txtAlbumArtPn', page).val(profile.AlbumArtPn || ''); + $('#txtAlbumArtMaxWidth', page).val(profile.MaxAlbumArtWidth || ''); + $('#txtAlbumArtMaxHeight', page).val(profile.MaxAlbumArtHeight || ''); + $('#txtIconMaxWidth', page).val(profile.MaxIconWidth || ''); + $('#txtIconMaxHeight', page).val(profile.MaxIconHeight || ''); + $('#chkIgnoreTranscodeByteRangeRequests', page).checked = profile.IgnoreTranscodeByteRangeRequests; + $('#txtMaxAllowedBitrate', page).val(profile.MaxStreamingBitrate || ''); + $('#txtMusicStreamingTranscodingBitrate', page).val(profile.MusicStreamingTranscodingBitrate || ''); + $('#chkRequiresPlainFolders', page).checked = profile.RequiresPlainFolders; + $('#chkRequiresPlainVideoItems', page).checked = profile.RequiresPlainVideoItems; + $('#txtProtocolInfo', page).val(profile.ProtocolInfo || ''); + $('#txtXDlnaCap', page).val(profile.XDlnaCap || ''); + $('#txtXDlnaDoc', page).val(profile.XDlnaDoc || ''); + $('#txtSonyAggregationFlags', page).val(profile.SonyAggregationFlags || ''); + profile.DirectPlayProfiles = profile.DirectPlayProfiles || []; + profile.TranscodingProfiles = profile.TranscodingProfiles || []; + profile.ContainerProfiles = profile.ContainerProfiles || []; + profile.CodecProfiles = profile.CodecProfiles || []; + profile.ResponseProfiles = profile.ResponseProfiles || []; + var usersHtml = '' + users.map(function (u) { + return ''; + }).join(''); + $('#selectUser', page).html(usersHtml).val(profile.UserId || ''); + renderSubProfiles(page, profile); + } + + function renderIdentificationHeaders(page, headers) { + var index = 0; + var html = '
' + headers.map(function (h) { + var li = '
'; + li += ''; + li += '
'; + li += '

' + h.Name + ': ' + (h.Value || '') + '

'; + li += '
' + (h.Match || '') + '
'; + li += '
'; + li += ''; + li += '
'; + index++; + return li; + }).join('') + '
'; + var elem = $('.httpHeaderIdentificationList', page).html(html).trigger('create'); + $('.btnDeleteIdentificationHeader', elem).on('click', function () { + var itemIndex = parseInt(this.getAttribute('data-index')); + currentProfile.Identification.Headers.splice(itemIndex, 1); + renderIdentificationHeaders(page, currentProfile.Identification.Headers); + }); + } + + function openPopup(elem) { + elem.classList.remove('hide'); + } + + function closePopup(elem) { + elem.classList.add('hide'); + } + + function editIdentificationHeader(page, header) { + isSubProfileNew = null == header; + header = header || {}; + currentSubProfile = header; + var popup = $('#identificationHeaderPopup', page); + $('#txtIdentificationHeaderName', popup).val(header.Name || ''); + $('#txtIdentificationHeaderValue', popup).val(header.Value || ''); + $('#selectMatchType', popup).val(header.Match || 'Equals'); + openPopup(popup[0]); + } + + function saveIdentificationHeader(page) { + currentSubProfile.Name = $('#txtIdentificationHeaderName', page).val(); + currentSubProfile.Value = $('#txtIdentificationHeaderValue', page).val(); + currentSubProfile.Match = $('#selectMatchType', page).val(); + + if (isSubProfileNew) { + currentProfile.Identification = currentProfile.Identification || {}; + currentProfile.Identification.Headers = currentProfile.Identification.Headers || []; + currentProfile.Identification.Headers.push(currentSubProfile); + } + + renderIdentificationHeaders(page, currentProfile.Identification.Headers); + currentSubProfile = null; + closePopup($('#identificationHeaderPopup', page)[0]); + } + + function renderXmlDocumentAttributes(page, attribute) { + var html = '
' + attribute.map(function (h) { + var li = '
'; + li += ''; + li += '
'; + li += '

' + h.Name + ' = ' + (h.Value || '') + '

'; + li += '
'; + li += ''; + return li += '
'; + }).join('') + '
'; + var elem = $('.xmlDocumentAttributeList', page).html(html).trigger('create'); + $('.btnDeleteXmlAttribute', elem).on('click', function () { + var itemIndex = parseInt(this.getAttribute('data-index')); + currentProfile.XmlRootAttributes.splice(itemIndex, 1); + renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes); + }); + } + + function editXmlDocumentAttribute(page, attribute) { + isSubProfileNew = null == attribute; + attribute = attribute || {}; + currentSubProfile = attribute; + var popup = $('#xmlAttributePopup', page); + $('#txtXmlAttributeName', popup).val(attribute.Name || ''); + $('#txtXmlAttributeValue', popup).val(attribute.Value || ''); + openPopup(popup[0]); + } + + function saveXmlDocumentAttribute(page) { + currentSubProfile.Name = $('#txtXmlAttributeName', page).val(); + currentSubProfile.Value = $('#txtXmlAttributeValue', page).val(); + + if (isSubProfileNew) { + currentProfile.XmlRootAttributes.push(currentSubProfile); + } + + renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes); + currentSubProfile = null; + closePopup($('#xmlAttributePopup', page)[0]); + } + + function renderSubtitleProfiles(page, profiles) { + var index = 0; + var html = '
' + profiles.map(function (h) { + var li = '
'; + li += ''; + li += '
'; + li += '

' + (h.Format || '') + '

'; + li += '
'; + li += ''; + li += '
'; + index++; + return li; + }).join('') + '
'; + var elem = $('.subtitleProfileList', page).html(html).trigger('create'); + $('.btnDeleteProfile', elem).on('click', function () { + var itemIndex = parseInt(this.getAttribute('data-index')); + currentProfile.SubtitleProfiles.splice(itemIndex, 1); + renderSubtitleProfiles(page, currentProfile.SubtitleProfiles); + }); + $('.lnkEditSubProfile', elem).on('click', function () { + var itemIndex = parseInt(this.getAttribute('data-index')); + editSubtitleProfile(page, currentProfile.SubtitleProfiles[itemIndex]); + }); + } + + function editSubtitleProfile(page, profile) { + isSubProfileNew = null == profile; + profile = profile || {}; + currentSubProfile = profile; + var popup = $('#subtitleProfilePopup', page); + $('#txtSubtitleProfileFormat', popup).val(profile.Format || ''); + $('#selectSubtitleProfileMethod', popup).val(profile.Method || ''); + $('#selectSubtitleProfileDidlMode', popup).val(profile.DidlMode || ''); + openPopup(popup[0]); + } + + function saveSubtitleProfile(page) { + currentSubProfile.Format = $('#txtSubtitleProfileFormat', page).val(); + currentSubProfile.Method = $('#selectSubtitleProfileMethod', page).val(); + currentSubProfile.DidlMode = $('#selectSubtitleProfileDidlMode', page).val(); + + if (isSubProfileNew) { + currentProfile.SubtitleProfiles.push(currentSubProfile); + } + + renderSubtitleProfiles(page, currentProfile.SubtitleProfiles); + currentSubProfile = null; + closePopup($('#subtitleProfilePopup', page)[0]); + } + + function renderSubProfiles(page, profile) { + renderDirectPlayProfiles(page, profile.DirectPlayProfiles); + renderTranscodingProfiles(page, profile.TranscodingProfiles); + renderContainerProfiles(page, profile.ContainerProfiles); + renderCodecProfiles(page, profile.CodecProfiles); + renderResponseProfiles(page, profile.ResponseProfiles); + } + + function saveDirectPlayProfile(page) { + currentSubProfile.Type = $('#selectDirectPlayProfileType', page).val(); + currentSubProfile.Container = $('#txtDirectPlayContainer', page).val(); + currentSubProfile.AudioCodec = $('#txtDirectPlayAudioCodec', page).val(); + currentSubProfile.VideoCodec = $('#txtDirectPlayVideoCodec', page).val(); + + if (isSubProfileNew) { + currentProfile.DirectPlayProfiles.push(currentSubProfile); + } + + renderSubProfiles(page, currentProfile); + currentSubProfile = null; + closePopup($('#popupEditDirectPlayProfile', page)[0]); + } + + function renderDirectPlayProfiles(page, profiles) { + var html = ''; + html += ''; + var elem = $('.directPlayProfiles', page).html(html).trigger('create'); + $('.btnDeleteProfile', elem).on('click', function () { + var index = this.getAttribute('data-profileindex'); + deleteDirectPlayProfile(page, index); + }); + $('.lnkEditSubProfile', elem).on('click', function () { + var index = parseInt(this.getAttribute('data-profileindex')); + editDirectPlayProfile(page, currentProfile.DirectPlayProfiles[index]); + }); + } + + function deleteDirectPlayProfile(page, index) { + currentProfile.DirectPlayProfiles.splice(index, 1); + renderDirectPlayProfiles(page, currentProfile.DirectPlayProfiles); + } + + function editDirectPlayProfile(page, directPlayProfile) { + isSubProfileNew = null == directPlayProfile; + directPlayProfile = directPlayProfile || {}; + currentSubProfile = directPlayProfile; + var popup = $('#popupEditDirectPlayProfile', page); + $('#selectDirectPlayProfileType', popup).val(directPlayProfile.Type || 'Video').trigger('change'); + $('#txtDirectPlayContainer', popup).val(directPlayProfile.Container || ''); + $('#txtDirectPlayAudioCodec', popup).val(directPlayProfile.AudioCodec || ''); + $('#txtDirectPlayVideoCodec', popup).val(directPlayProfile.VideoCodec || ''); + openPopup(popup[0]); + } + + function renderTranscodingProfiles(page, profiles) { + var html = ''; + html += ''; + var elem = $('.transcodingProfiles', page).html(html).trigger('create'); + $('.btnDeleteProfile', elem).on('click', function () { + var index = this.getAttribute('data-profileindex'); + deleteTranscodingProfile(page, index); + }); + $('.lnkEditSubProfile', elem).on('click', function () { + var index = parseInt(this.getAttribute('data-profileindex')); + editTranscodingProfile(page, currentProfile.TranscodingProfiles[index]); + }); + } + + function editTranscodingProfile(page, transcodingProfile) { + isSubProfileNew = null == transcodingProfile; + transcodingProfile = transcodingProfile || {}; + currentSubProfile = transcodingProfile; + var popup = $('#transcodingProfilePopup', page); + $('#selectTranscodingProfileType', popup).val(transcodingProfile.Type || 'Video').trigger('change'); + $('#txtTranscodingContainer', popup).val(transcodingProfile.Container || ''); + $('#txtTranscodingAudioCodec', popup).val(transcodingProfile.AudioCodec || ''); + $('#txtTranscodingVideoCodec', popup).val(transcodingProfile.VideoCodec || ''); + $('#selectTranscodingProtocol', popup).val(transcodingProfile.Protocol || 'Http'); + $('#chkEnableMpegtsM2TsMode', popup).checked = transcodingProfile.EnableMpegtsM2TsMode || false; + $('#chkEstimateContentLength', popup).checked = transcodingProfile.EstimateContentLength || false; + $('#chkReportByteRangeRequests', popup).checked = 'Bytes' == transcodingProfile.TranscodeSeekInfo; + $('.radioTabButton:first', popup).trigger('click'); + openPopup(popup[0]); + } + + function deleteTranscodingProfile(page, index) { + currentProfile.TranscodingProfiles.splice(index, 1); + renderTranscodingProfiles(page, currentProfile.TranscodingProfiles); + } + + function saveTranscodingProfile(page) { + currentSubProfile.Type = $('#selectTranscodingProfileType', page).val(); + currentSubProfile.Container = $('#txtTranscodingContainer', page).val(); + currentSubProfile.AudioCodec = $('#txtTranscodingAudioCodec', page).val(); + currentSubProfile.VideoCodec = $('#txtTranscodingVideoCodec', page).val(); + currentSubProfile.Protocol = $('#selectTranscodingProtocol', page).val(); + currentSubProfile.Context = 'Streaming'; + currentSubProfile.EnableMpegtsM2TsMode = $('#chkEnableMpegtsM2TsMode', page).checked; + currentSubProfile.EstimateContentLength = $('#chkEstimateContentLength', page).checked; + currentSubProfile.TranscodeSeekInfo = $('#chkReportByteRangeRequests', page).checked ? 'Bytes' : 'Auto'; + + if (isSubProfileNew) { + currentProfile.TranscodingProfiles.push(currentSubProfile); + } + + renderSubProfiles(page, currentProfile); + currentSubProfile = null; + closePopup($('#transcodingProfilePopup', page)[0]); + } + + function renderContainerProfiles(page, profiles) { + var html = ''; + html += ''; + var elem = $('.containerProfiles', page).html(html).trigger('create'); + $('.btnDeleteProfile', elem).on('click', function () { + var index = this.getAttribute('data-profileindex'); + deleteContainerProfile(page, index); + }); + $('.lnkEditSubProfile', elem).on('click', function () { + var index = parseInt(this.getAttribute('data-profileindex')); + editContainerProfile(page, currentProfile.ContainerProfiles[index]); + }); + } + + function deleteContainerProfile(page, index) { + currentProfile.ContainerProfiles.splice(index, 1); + renderContainerProfiles(page, currentProfile.ContainerProfiles); + } + + function editContainerProfile(page, containerProfile) { + isSubProfileNew = null == containerProfile; + containerProfile = containerProfile || {}; + currentSubProfile = containerProfile; + var popup = $('#containerProfilePopup', page); + $('#selectContainerProfileType', popup).val(containerProfile.Type || 'Video').trigger('change'); + $('#txtContainerProfileContainer', popup).val(containerProfile.Container || ''); + $('.radioTabButton:first', popup).trigger('click'); + openPopup(popup[0]); + } + + function saveContainerProfile(page) { + currentSubProfile.Type = $('#selectContainerProfileType', page).val(); + currentSubProfile.Container = $('#txtContainerProfileContainer', page).val(); + + if (isSubProfileNew) { + currentProfile.ContainerProfiles.push(currentSubProfile); + } + + renderSubProfiles(page, currentProfile); + currentSubProfile = null; + closePopup($('#containerProfilePopup', page)[0]); + } + + function renderCodecProfiles(page, profiles) { + var html = ''; + html += ''; + var elem = $('.codecProfiles', page).html(html).trigger('create'); + $('.btnDeleteProfile', elem).on('click', function () { + var index = this.getAttribute('data-profileindex'); + deleteCodecProfile(page, index); + }); + $('.lnkEditSubProfile', elem).on('click', function () { + var index = parseInt(this.getAttribute('data-profileindex')); + editCodecProfile(page, currentProfile.CodecProfiles[index]); + }); + } + + function deleteCodecProfile(page, index) { + currentProfile.CodecProfiles.splice(index, 1); + renderCodecProfiles(page, currentProfile.CodecProfiles); + } + + function editCodecProfile(page, codecProfile) { + isSubProfileNew = null == codecProfile; + codecProfile = codecProfile || {}; + currentSubProfile = codecProfile; + var popup = $('#codecProfilePopup', page); + $('#selectCodecProfileType', popup).val(codecProfile.Type || 'Video').trigger('change'); + $('#txtCodecProfileCodec', popup).val(codecProfile.Codec || ''); + $('.radioTabButton:first', popup).trigger('click'); + openPopup(popup[0]); + } + + function saveCodecProfile(page) { + currentSubProfile.Type = $('#selectCodecProfileType', page).val(); + currentSubProfile.Codec = $('#txtCodecProfileCodec', page).val(); + + if (isSubProfileNew) { + currentProfile.CodecProfiles.push(currentSubProfile); + } + + renderSubProfiles(page, currentProfile); + currentSubProfile = null; + closePopup($('#codecProfilePopup', page)[0]); + } + + function renderResponseProfiles(page, profiles) { + var html = ''; + html += ''; + var elem = $('.mediaProfiles', page).html(html).trigger('create'); + $('.btnDeleteProfile', elem).on('click', function () { + var index = this.getAttribute('data-profileindex'); + deleteResponseProfile(page, index); + }); + $('.lnkEditSubProfile', elem).on('click', function () { + var index = parseInt(this.getAttribute('data-profileindex')); + editResponseProfile(page, currentProfile.ResponseProfiles[index]); + }); + } + + function deleteResponseProfile(page, index) { + currentProfile.ResponseProfiles.splice(index, 1); + renderResponseProfiles(page, currentProfile.ResponseProfiles); + } + + function editResponseProfile(page, responseProfile) { + isSubProfileNew = null == responseProfile; + responseProfile = responseProfile || {}; + currentSubProfile = responseProfile; + var popup = $('#responseProfilePopup', page); + $('#selectResponseProfileType', popup).val(responseProfile.Type || 'Video').trigger('change'); + $('#txtResponseProfileContainer', popup).val(responseProfile.Container || ''); + $('#txtResponseProfileAudioCodec', popup).val(responseProfile.AudioCodec || ''); + $('#txtResponseProfileVideoCodec', popup).val(responseProfile.VideoCodec || ''); + $('.radioTabButton:first', popup).trigger('click'); + openPopup(popup[0]); + } + + function saveResponseProfile(page) { + currentSubProfile.Type = $('#selectResponseProfileType', page).val(); + currentSubProfile.Container = $('#txtResponseProfileContainer', page).val(); + currentSubProfile.AudioCodec = $('#txtResponseProfileAudioCodec', page).val(); + currentSubProfile.VideoCodec = $('#txtResponseProfileVideoCodec', page).val(); + + if (isSubProfileNew) { + currentProfile.ResponseProfiles.push(currentSubProfile); + } + + renderSubProfiles(page, currentProfile); + currentSubProfile = null; + closePopup($('#responseProfilePopup', page)[0]); + } + + function saveProfile(page, profile) { + updateProfile(page, profile); + var id = getParameterByName('id'); + + if (id) { + ApiClient.ajax({ + type: 'POST', + url: ApiClient.getUrl('Dlna/Profiles/' + id), + data: JSON.stringify(profile), + contentType: 'application/json' + }).then(function () { + require(['toast'], function (toast) { + toast('Settings saved.'); + }); + }, Dashboard.processErrorResponse); + } else { + ApiClient.ajax({ + type: 'POST', + url: ApiClient.getUrl('Dlna/Profiles'), + data: JSON.stringify(profile), + contentType: 'application/json' + }).then(function () { + Dashboard.navigate('dlnaprofiles.html'); + }, Dashboard.processErrorResponse); + } + + loading.hide(); + } + + function updateProfile(page, profile) { + profile.Name = $('#txtName', page).val(); + profile.EnableAlbumArtInDidl = $('#chkEnableAlbumArtInDidl', page).checked; + profile.EnableSingleAlbumArtLimit = $('#chkEnableSingleImageLimit', page).checked; + profile.SupportedMediaTypes = $('.chkMediaType:checked', page).get().map(function (c) { + return c.getAttribute('data-value'); + }).join(','); + profile.Identification = profile.Identification || {}; + profile.FriendlyName = $('#txtInfoFriendlyName', page).val(); + profile.ModelName = $('#txtInfoModelName', page).val(); + profile.ModelNumber = $('#txtInfoModelNumber', page).val(); + profile.ModelDescription = $('#txtInfoModelDescription', page).val(); + profile.ModelUrl = $('#txtInfoModelUrl', page).val(); + profile.Manufacturer = $('#txtInfoManufacturer', page).val(); + profile.ManufacturerUrl = $('#txtInfoManufacturerUrl', page).val(); + profile.SerialNumber = $('#txtInfoSerialNumber', page).val(); + profile.Identification.FriendlyName = $('#txtIdFriendlyName', page).val(); + profile.Identification.ModelName = $('#txtIdModelName', page).val(); + profile.Identification.ModelNumber = $('#txtIdModelNumber', page).val(); + profile.Identification.ModelDescription = $('#txtIdModelDescription', page).val(); + profile.Identification.ModelUrl = $('#txtIdModelUrl', page).val(); + profile.Identification.Manufacturer = $('#txtIdManufacturer', page).val(); + profile.Identification.ManufacturerUrl = $('#txtIdManufacturerUrl', page).val(); + profile.Identification.SerialNumber = $('#txtIdSerialNumber', page).val(); + profile.Identification.DeviceDescription = $('#txtIdDeviceDescription', page).val(); + profile.AlbumArtPn = $('#txtAlbumArtPn', page).val(); + profile.MaxAlbumArtWidth = $('#txtAlbumArtMaxWidth', page).val(); + profile.MaxAlbumArtHeight = $('#txtAlbumArtMaxHeight', page).val(); + profile.MaxIconWidth = $('#txtIconMaxWidth', page).val(); + profile.MaxIconHeight = $('#txtIconMaxHeight', page).val(); + profile.RequiresPlainFolders = $('#chkRequiresPlainFolders', page).checked; + profile.RequiresPlainVideoItems = $('#chkRequiresPlainVideoItems', page).checked; + profile.IgnoreTranscodeByteRangeRequests = $('#chkIgnoreTranscodeByteRangeRequests', page).checked; + profile.MaxStreamingBitrate = $('#txtMaxAllowedBitrate', page).val(); + profile.MusicStreamingTranscodingBitrate = $('#txtMusicStreamingTranscodingBitrate', page).val(); + profile.ProtocolInfo = $('#txtProtocolInfo', page).val(); + profile.XDlnaCap = $('#txtXDlnaCap', page).val(); + profile.XDlnaDoc = $('#txtXDlnaDoc', page).val(); + profile.SonyAggregationFlags = $('#txtSonyAggregationFlags', page).val(); + profile.UserId = $('#selectUser', page).val(); + } + + var currentProfile; + var currentSubProfile; + var isSubProfileNew; + var allText = globalize.translate('LabelAll'); + + $(document).on('pageinit', '#dlnaProfilePage', function () { + var page = this; + $('.radioTabButton', page).on('click', function () { + $(this).siblings().removeClass('ui-btn-active'); + $(this).addClass('ui-btn-active'); + var value = 'A' == this.tagName ? this.getAttribute('data-value') : this.value; + var elem = $('.' + value, page); + elem.siblings('.tabContent').hide(); + elem.show(); + }); + $('#selectDirectPlayProfileType', page).on('change', function () { + if ('Video' == this.value) { + $('#fldDirectPlayVideoCodec', page).show(); + } else { + $('#fldDirectPlayVideoCodec', page).hide(); + } + + if ('Photo' == this.value) { + $('#fldDirectPlayAudioCodec', page).hide(); + } else { + $('#fldDirectPlayAudioCodec', page).show(); + } + }); + $('#selectTranscodingProfileType', page).on('change', function () { + if ('Video' == this.value) { + $('#fldTranscodingVideoCodec', page).show(); + $('#fldTranscodingProtocol', page).show(); + $('#fldEnableMpegtsM2TsMode', page).show(); + } else { + $('#fldTranscodingVideoCodec', page).hide(); + $('#fldTranscodingProtocol', page).hide(); + $('#fldEnableMpegtsM2TsMode', page).hide(); + } + + if ('Photo' == this.value) { + $('#fldTranscodingAudioCodec', page).hide(); + $('#fldEstimateContentLength', page).hide(); + $('#fldReportByteRangeRequests', page).hide(); + } else { + $('#fldTranscodingAudioCodec', page).show(); + $('#fldEstimateContentLength', page).show(); + $('#fldReportByteRangeRequests', page).show(); + } + }); + $('#selectResponseProfileType', page).on('change', function () { + if ('Video' == this.value) { + $('#fldResponseProfileVideoCodec', page).show(); + } else { + $('#fldResponseProfileVideoCodec', page).hide(); + } + + if ('Photo' == this.value) { + $('#fldResponseProfileAudioCodec', page).hide(); + } else { + $('#fldResponseProfileAudioCodec', page).show(); + } + }); + $('.btnAddDirectPlayProfile', page).on('click', function () { + editDirectPlayProfile(page); + }); + $('.btnAddTranscodingProfile', page).on('click', function () { + editTranscodingProfile(page); + }); + $('.btnAddContainerProfile', page).on('click', function () { + editContainerProfile(page); + }); + $('.btnAddCodecProfile', page).on('click', function () { + editCodecProfile(page); + }); + $('.btnAddResponseProfile', page).on('click', function () { + editResponseProfile(page); + }); + $('.btnAddIdentificationHttpHeader', page).on('click', function () { + editIdentificationHeader(page); + }); + $('.btnAddXmlDocumentAttribute', page).on('click', function () { + editXmlDocumentAttribute(page); + }); + $('.btnAddSubtitleProfile', page).on('click', function () { + editSubtitleProfile(page); + }); + $('.dlnaProfileForm').off('submit', DlnaProfilePage.onSubmit).on('submit', DlnaProfilePage.onSubmit); + $('.editDirectPlayProfileForm').off('submit', DlnaProfilePage.onDirectPlayFormSubmit).on('submit', DlnaProfilePage.onDirectPlayFormSubmit); + $('.transcodingProfileForm').off('submit', DlnaProfilePage.onTranscodingProfileFormSubmit).on('submit', DlnaProfilePage.onTranscodingProfileFormSubmit); + $('.containerProfileForm').off('submit', DlnaProfilePage.onContainerProfileFormSubmit).on('submit', DlnaProfilePage.onContainerProfileFormSubmit); + $('.codecProfileForm').off('submit', DlnaProfilePage.onCodecProfileFormSubmit).on('submit', DlnaProfilePage.onCodecProfileFormSubmit); + $('.editResponseProfileForm').off('submit', DlnaProfilePage.onResponseProfileFormSubmit).on('submit', DlnaProfilePage.onResponseProfileFormSubmit); + $('.identificationHeaderForm').off('submit', DlnaProfilePage.onIdentificationHeaderFormSubmit).on('submit', DlnaProfilePage.onIdentificationHeaderFormSubmit); + $('.xmlAttributeForm').off('submit', DlnaProfilePage.onXmlAttributeFormSubmit).on('submit', DlnaProfilePage.onXmlAttributeFormSubmit); + $('.subtitleProfileForm').off('submit', DlnaProfilePage.onSubtitleProfileFormSubmit).on('submit', DlnaProfilePage.onSubtitleProfileFormSubmit); + }).on('pageshow', '#dlnaProfilePage', function () { + var page = this; + $('#radioInfo', page).trigger('click'); + loadProfile(page); + }); + window.DlnaProfilePage = { + onSubmit: function () { + loading.show(); + saveProfile($(this).parents('.page'), currentProfile); + return false; + }, + onDirectPlayFormSubmit: function () { + saveDirectPlayProfile($(this).parents('.page')); + return false; + }, + onTranscodingProfileFormSubmit: function () { + saveTranscodingProfile($(this).parents('.page')); + return false; + }, + onContainerProfileFormSubmit: function () { + saveContainerProfile($(this).parents('.page')); + return false; + }, + onCodecProfileFormSubmit: function () { + saveCodecProfile($(this).parents('.page')); + return false; + }, + onResponseProfileFormSubmit: function () { + saveResponseProfile($(this).parents('.page')); + return false; + }, + onIdentificationHeaderFormSubmit: function () { + saveIdentificationHeader($(this).parents('.page')); + return false; + }, + onXmlAttributeFormSubmit: function () { + saveXmlDocumentAttribute($(this).parents('.page')); + return false; + }, + onSubtitleProfileFormSubmit: function () { + saveSubtitleProfile($(this).parents('.page')); + return false; + } + }; +}); diff --git a/src/controllers/dashboard/dlna/profiles.js b/src/controllers/dashboard/dlna/profiles.js new file mode 100644 index 0000000000..fb4caadeb9 --- /dev/null +++ b/src/controllers/dashboard/dlna/profiles.js @@ -0,0 +1,89 @@ +define(['jQuery', 'globalize', 'loading', 'libraryMenu', 'listViewStyle', 'emby-button'], function ($, globalize, loading, libraryMenu) { + 'use strict'; + + function loadProfiles(page) { + loading.show(); + ApiClient.getJSON(ApiClient.getUrl('Dlna/ProfileInfos')).then(function (result) { + renderUserProfiles(page, result); + renderSystemProfiles(page, result); + loading.hide(); + }); + } + + function renderUserProfiles(page, profiles) { + renderProfiles(page, page.querySelector('.customProfiles'), profiles.filter(function (p) { + return 'User' == p.Type; + })); + } + + function renderSystemProfiles(page, profiles) { + renderProfiles(page, page.querySelector('.systemProfiles'), profiles.filter(function (p) { + return 'System' == p.Type; + })); + } + + function renderProfiles(page, element, profiles) { + var html = ''; + + if (profiles.length) { + html += '
'; + } + + for (var i = 0, length = profiles.length; i < length; i++) { + var profile = profiles[i]; + html += '
'; + html += ''; + html += ''; + + if ('User' == profile.Type) { + html += ''; + } + + html += '
'; + } + + if (profiles.length) { + html += '
'; + } + + element.innerHTML = html; + $('.btnDeleteProfile', element).on('click', function () { + var id = this.getAttribute('data-profileid'); + deleteProfile(page, id); + }); + } + + function deleteProfile(page, id) { + require(['confirm'], function (confirm) { + confirm(globalize.translate('MessageConfirmProfileDeletion'), globalize.translate('HeaderConfirmProfileDeletion')).then(function () { + loading.show(); + ApiClient.ajax({ + type: 'DELETE', + url: ApiClient.getUrl('Dlna/Profiles/' + id) + }).then(function () { + loading.hide(); + loadProfiles(page); + }); + }); + }); + } + + function getTabs() { + return [{ + href: 'dlnasettings.html', + name: globalize.translate('TabSettings') + }, { + href: 'dlnaprofiles.html', + name: globalize.translate('TabProfiles') + }]; + } + + $(document).on('pageshow', '#dlnaProfilesPage', function () { + libraryMenu.setTabs('dlna', 1, getTabs); + loadProfiles(this); + }); +}); diff --git a/src/controllers/dashboard/dlna/settings.js b/src/controllers/dashboard/dlna/settings.js new file mode 100644 index 0000000000..da254c0bd9 --- /dev/null +++ b/src/controllers/dashboard/dlna/settings.js @@ -0,0 +1,56 @@ +define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, libraryMenu, globalize) { + 'use strict'; + + function loadPage(page, config, users) { + page.querySelector('#chkEnablePlayTo').checked = config.EnablePlayTo; + page.querySelector('#chkEnableDlnaDebugLogging').checked = config.EnableDebugLog; + $('#txtClientDiscoveryInterval', page).val(config.ClientDiscoveryIntervalSeconds); + $('#chkEnableServer', page).checked = config.EnableServer; + $('#chkBlastAliveMessages', page).checked = config.BlastAliveMessages; + $('#txtBlastInterval', page).val(config.BlastAliveMessageIntervalSeconds); + var usersHtml = users.map(function (u) { + return ''; + }).join(''); + $('#selectUser', page).html(usersHtml).val(config.DefaultUserId || ''); + loading.hide(); + } + + function onSubmit() { + loading.show(); + var form = this; + ApiClient.getNamedConfiguration('dlna').then(function (config) { + config.EnablePlayTo = form.querySelector('#chkEnablePlayTo').checked; + config.EnableDebugLog = form.querySelector('#chkEnableDlnaDebugLogging').checked; + config.ClientDiscoveryIntervalSeconds = $('#txtClientDiscoveryInterval', form).val(); + config.EnableServer = $('#chkEnableServer', form).checked; + config.BlastAliveMessages = $('#chkBlastAliveMessages', form).checked; + config.BlastAliveMessageIntervalSeconds = $('#txtBlastInterval', form).val(); + config.DefaultUserId = $('#selectUser', form).val(); + ApiClient.updateNamedConfiguration('dlna', config).then(Dashboard.processServerConfigurationUpdateResult); + }); + return false; + } + + function getTabs() { + return [{ + href: 'dlnasettings.html', + name: globalize.translate('TabSettings') + }, { + href: 'dlnaprofiles.html', + name: globalize.translate('TabProfiles') + }]; + } + + $(document).on('pageinit', '#dlnaSettingsPage', function () { + $('.dlnaSettingsForm').off('submit', onSubmit).on('submit', onSubmit); + }).on('pageshow', '#dlnaSettingsPage', function () { + libraryMenu.setTabs('dlna', 0, getTabs); + loading.show(); + var page = this; + var promise1 = ApiClient.getNamedConfiguration('dlna'); + var promise2 = ApiClient.getUsers(); + Promise.all([promise1, promise2]).then(function (responses) { + loadPage(page, responses[0], responses[1]); + }); + }); +}); diff --git a/src/controllers/dashboard/encodingsettings.js b/src/controllers/dashboard/encodingsettings.js new file mode 100644 index 0000000000..c9b41a75e0 --- /dev/null +++ b/src/controllers/dashboard/encodingsettings.js @@ -0,0 +1,194 @@ +define(['jQuery', 'loading', 'globalize', 'dom', 'libraryMenu'], function ($, loading, globalize, dom, libraryMenu) { + 'use strict'; + + function loadPage(page, config, systemInfo) { + Array.prototype.forEach.call(page.querySelectorAll('.chkDecodeCodec'), function (c) { + c.checked = -1 !== (config.HardwareDecodingCodecs || []).indexOf(c.getAttribute('data-codec')); + }); + page.querySelector('#chkDecodingColorDepth10').checked = config.EnableDecodingColorDepth10; + page.querySelector('#chkHardwareEncoding').checked = config.EnableHardwareEncoding; + $('#selectVideoDecoder', page).val(config.HardwareAccelerationType); + $('#selectThreadCount', page).val(config.EncodingThreadCount); + $('#txtDownMixAudioBoost', page).val(config.DownMixAudioBoost); + page.querySelector('.txtEncoderPath').value = config.EncoderAppPathDisplay || ''; + $('#txtTranscodingTempPath', page).val(systemInfo.TranscodingTempPath || ''); + $('#txtVaapiDevice', page).val(config.VaapiDevice || ''); + page.querySelector('#selectEncoderPreset').value = config.EncoderPreset || ''; + page.querySelector('#txtH264Crf').value = config.H264Crf || ''; + page.querySelector('#selectDeinterlaceMethod').value = config.DeinterlaceMethod || ''; + page.querySelector('#chkEnableSubtitleExtraction').checked = config.EnableSubtitleExtraction || false; + page.querySelector('#chkEnableThrottling').checked = config.EnableThrottling || false; + page.querySelector('#selectVideoDecoder').dispatchEvent(new CustomEvent('change', { + bubbles: true + })); + loading.hide(); + } + + function onSaveEncodingPathFailure(response) { + loading.hide(); + var msg = ''; + msg = globalize.translate('FFmpegSavePathNotFound'); + + require(['alert'], function (alert) { + alert(msg); + }); + } + + function updateEncoder(form) { + return ApiClient.getSystemInfo().then(function (systemInfo) { + return ApiClient.ajax({ + url: ApiClient.getUrl('System/MediaEncoder/Path'), + type: 'POST', + data: { + Path: form.querySelector('.txtEncoderPath').value, + PathType: 'Custom' + } + }).then(Dashboard.processServerConfigurationUpdateResult, onSaveEncodingPathFailure); + }); + } + + function onSubmit() { + var form = this; + + var onDecoderConfirmed = function () { + loading.show(); + ApiClient.getNamedConfiguration('encoding').then(function (config) { + config.DownMixAudioBoost = $('#txtDownMixAudioBoost', form).val(); + config.TranscodingTempPath = $('#txtTranscodingTempPath', form).val(); + config.EncodingThreadCount = $('#selectThreadCount', form).val(); + config.HardwareAccelerationType = $('#selectVideoDecoder', form).val(); + config.VaapiDevice = $('#txtVaapiDevice', form).val(); + config.EncoderPreset = form.querySelector('#selectEncoderPreset').value; + config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0'); + config.DeinterlaceMethod = form.querySelector('#selectDeinterlaceMethod').value; + config.EnableSubtitleExtraction = form.querySelector('#chkEnableSubtitleExtraction').checked; + config.EnableThrottling = form.querySelector('#chkEnableThrottling').checked; + config.HardwareDecodingCodecs = Array.prototype.map.call(Array.prototype.filter.call(form.querySelectorAll('.chkDecodeCodec'), function (c) { + return c.checked; + }), function (c) { + return c.getAttribute('data-codec'); + }); + config.EnableDecodingColorDepth10 = form.querySelector('#chkDecodingColorDepth10').checked; + + config.EnableHardwareEncoding = form.querySelector('#chkHardwareEncoding').checked; + ApiClient.updateNamedConfiguration('encoding', config).then(function () { + updateEncoder(form); + }, function () { + require(['alert'], function (alert) { + alert(globalize.translate('DefaultErrorMessage')); + }); + + Dashboard.processServerConfigurationUpdateResult(); + }); + }); + }; + + if ($('#selectVideoDecoder', form).val()) { + require(['alert'], function (alert) { + alert({ + title: globalize.translate('TitleHardwareAcceleration'), + text: globalize.translate('HardwareAccelerationWarning') + }).then(onDecoderConfirmed); + }); + } else { + onDecoderConfirmed(); + } + + return false; + } + + function setDecodingCodecsVisible(context, value) { + value = value || ''; + var any; + Array.prototype.forEach.call(context.querySelectorAll('.chkDecodeCodec'), function (c) { + if (-1 === c.getAttribute('data-types').split(',').indexOf(value)) { + dom.parentWithTag(c, 'LABEL').classList.add('hide'); + } else { + dom.parentWithTag(c, 'LABEL').classList.remove('hide'); + any = true; + } + }); + + if (any) { + context.querySelector('.decodingCodecsList').classList.remove('hide'); + } else { + context.querySelector('.decodingCodecsList').classList.add('hide'); + } + } + + function getTabs() { + return [{ + href: 'encodingsettings.html', + name: globalize.translate('Transcoding') + }, { + href: 'playbackconfiguration.html', + name: globalize.translate('TabResumeSettings') + }, { + href: 'streamingsettings.html', + name: globalize.translate('TabStreaming') + }]; + } + + $(document).on('pageinit', '#encodingSettingsPage', function () { + var page = this; + page.querySelector('#selectVideoDecoder').addEventListener('change', function () { + if ('vaapi' == this.value) { + page.querySelector('.fldVaapiDevice').classList.remove('hide'); + page.querySelector('#txtVaapiDevice').setAttribute('required', 'required'); + } else { + page.querySelector('.fldVaapiDevice').classList.add('hide'); + page.querySelector('#txtVaapiDevice').removeAttribute('required'); + } + + if (this.value) { + page.querySelector('.hardwareAccelerationOptions').classList.remove('hide'); + } else { + page.querySelector('.hardwareAccelerationOptions').classList.add('hide'); + } + + setDecodingCodecsVisible(page, this.value); + }); + $('#btnSelectEncoderPath', page).on('click.selectDirectory', function () { + require(['directorybrowser'], function (directoryBrowser) { + var picker = new directoryBrowser(); + picker.show({ + includeFiles: true, + callback: function (path) { + if (path) { + $('.txtEncoderPath', page).val(path); + } + + picker.close(); + } + }); + }); + }); + $('#btnSelectTranscodingTempPath', page).on('click.selectDirectory', function () { + require(['directorybrowser'], function (directoryBrowser) { + var picker = new directoryBrowser(); + picker.show({ + callback: function (path) { + if (path) { + $('#txtTranscodingTempPath', page).val(path); + } + + picker.close(); + }, + validateWriteable: true, + header: globalize.translate('HeaderSelectTranscodingPath'), + instruction: globalize.translate('HeaderSelectTranscodingPathHelp') + }); + }); + }); + $('.encodingSettingsForm').off('submit', onSubmit).on('submit', onSubmit); + }).on('pageshow', '#encodingSettingsPage', function () { + loading.show(); + libraryMenu.setTabs('playback', 0, getTabs); + var page = this; + ApiClient.getNamedConfiguration('encoding').then(function (config) { + ApiClient.getSystemInfo().then(function (systemInfo) { + loadPage(page, config, systemInfo); + }); + }); + }); +}); diff --git a/src/controllers/dashboard/general.js b/src/controllers/dashboard/general.js index a434e46241..f215ace28b 100644 --- a/src/controllers/dashboard/general.js +++ b/src/controllers/dashboard/general.js @@ -1,28 +1,15 @@ -define(["jQuery", "loading", "fnchecked", "emby-checkbox", "emby-textarea", "emby-input", "emby-select", "emby-button"], function ($, loading) { - "use strict"; +define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-textarea', 'emby-input', 'emby-select', 'emby-button'], function ($, loading, globalize) { + 'use strict'; function loadPage(page, config, languageOptions, systemInfo) { - page.querySelector("#txtServerName").value = systemInfo.ServerName; - $("#chkAutoRunWebApp", page).checked(config.AutoRunWebApp); - - if (systemInfo.CanLaunchWebBrowser) { - page.querySelector("#fldAutoRunWebApp").classList.remove("hide"); - } else { - page.querySelector("#fldAutoRunWebApp").classList.add("hide"); - } - - page.querySelector("#txtCachePath").value = systemInfo.CachePath || ""; - $("#txtMetadataPath", page).val(systemInfo.InternalMetadataPath || ""); - $("#txtMetadataNetworkPath", page).val(systemInfo.MetadataNetworkPath || ""); - $("#selectLocalizationLanguage", page).html(languageOptions.map(function (language) { - return '"; + page.querySelector('#txtServerName').value = systemInfo.ServerName; + page.querySelector('#txtCachePath').value = systemInfo.CachePath || ''; + $('#txtMetadataPath', page).val(systemInfo.InternalMetadataPath || ''); + $('#txtMetadataNetworkPath', page).val(systemInfo.MetadataNetworkPath || ''); + $('#selectLocalizationLanguage', page).html(languageOptions.map(function (language) { + return ''; })).val(config.UICulture); currentLanguage = config.UICulture; - if (systemInfo.CanSelfRestart || systemInfo.CanSelfUpdate) { - $(".autoUpdatesContainer", page).removeClass("hide"); - } else { - $(".autoUpdatesContainer", page).addClass("hide"); - } loading.hide(); } @@ -30,19 +17,18 @@ define(["jQuery", "loading", "fnchecked", "emby-checkbox", "emby-textarea", "emb function onSubmit() { loading.show(); var form = this; - $(form).parents(".page"); + $(form).parents('.page'); ApiClient.getServerConfiguration().then(function (config) { - config.ServerName = $("#txtServerName", form).val(); - config.UICulture = $("#selectLocalizationLanguage", form).val(); - config.CachePath = form.querySelector("#txtCachePath").value; - config.MetadataPath = $("#txtMetadataPath", form).val(); - config.MetadataNetworkPath = $("#txtMetadataNetworkPath", form).val(); + config.ServerName = $('#txtServerName', form).val(); + config.UICulture = $('#selectLocalizationLanguage', form).val(); + config.CachePath = form.querySelector('#txtCachePath').value; + config.MetadataPath = $('#txtMetadataPath', form).val(); + config.MetadataNetworkPath = $('#txtMetadataNetworkPath', form).val(); var requiresReload = config.UICulture !== currentLanguage; - config.AutoRunWebApp = $("#chkAutoRunWebApp", form).checked(); ApiClient.updateServerConfiguration(config).then(function() { ApiClient.getNamedConfiguration(brandingConfigKey).then(function(brandingConfig) { - brandingConfig.LoginDisclaimer = form.querySelector("#txtLoginDisclaimer").value; - brandingConfig.CustomCss = form.querySelector("#txtCustomCss").value; + brandingConfig.LoginDisclaimer = form.querySelector('#txtLoginDisclaimer').value; + brandingConfig.CustomCss = form.querySelector('#txtCustomCss').value; if (currentBrandingOptions && brandingConfig.CustomCss !== currentBrandingOptions.CustomCss) { requiresReload = true; @@ -57,8 +43,8 @@ define(["jQuery", "loading", "fnchecked", "emby-checkbox", "emby-textarea", "emb }); }); }, function () { - require(["alert"], function (alert) { - alert(Globalize.translate("DefaultErrorMessage")); + require(['alert'], function (alert) { + alert(globalize.translate('DefaultErrorMessage')); }); Dashboard.processServerConfigurationUpdateResult(); @@ -69,61 +55,61 @@ define(["jQuery", "loading", "fnchecked", "emby-checkbox", "emby-textarea", "emb var currentBrandingOptions; var currentLanguage; - var brandingConfigKey = "branding"; + var brandingConfigKey = 'branding'; return function (view, params) { - $("#btnSelectCachePath", view).on("click.selectDirectory", function () { - require(["directorybrowser"], function (directoryBrowser) { + $('#btnSelectCachePath', view).on('click.selectDirectory', function () { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ callback: function (path) { if (path) { - view.querySelector("#txtCachePath").value = path; + view.querySelector('#txtCachePath').value = path; } picker.close(); }, validateWriteable: true, - header: Globalize.translate("HeaderSelectServerCachePath"), - instruction: Globalize.translate("HeaderSelectServerCachePathHelp") + header: globalize.translate('HeaderSelectServerCachePath'), + instruction: globalize.translate('HeaderSelectServerCachePathHelp') }); }); }); - $("#btnSelectMetadataPath", view).on("click.selectDirectory", function () { - require(["directorybrowser"], function (directoryBrowser) { + $('#btnSelectMetadataPath', view).on('click.selectDirectory', function () { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ - path: $("#txtMetadataPath", view).val(), - networkSharePath: $("#txtMetadataNetworkPath", view).val(), + path: $('#txtMetadataPath', view).val(), + networkSharePath: $('#txtMetadataNetworkPath', view).val(), callback: function (path, networkPath) { if (path) { - $("#txtMetadataPath", view).val(path); + $('#txtMetadataPath', view).val(path); } if (networkPath) { - $("#txtMetadataNetworkPath", view).val(networkPath); + $('#txtMetadataNetworkPath', view).val(networkPath); } picker.close(); }, validateWriteable: true, - header: Globalize.translate("HeaderSelectMetadataPath"), - instruction: Globalize.translate("HeaderSelectMetadataPathHelp"), + header: globalize.translate('HeaderSelectMetadataPath'), + instruction: globalize.translate('HeaderSelectMetadataPathHelp'), enableNetworkSharePath: true }); }); }); - $(".dashboardGeneralForm", view).off("submit", onSubmit).on("submit", onSubmit); - view.addEventListener("viewshow", function () { + $('.dashboardGeneralForm', view).off('submit', onSubmit).on('submit', onSubmit); + view.addEventListener('viewshow', function () { var promiseConfig = ApiClient.getServerConfiguration(); - var promiseLanguageOptions = ApiClient.getJSON(ApiClient.getUrl("Localization/Options")); + var promiseLanguageOptions = ApiClient.getJSON(ApiClient.getUrl('Localization/Options')); var promiseSystemInfo = ApiClient.getSystemInfo(); Promise.all([promiseConfig, promiseLanguageOptions, promiseSystemInfo]).then(function (responses) { loadPage(view, responses[0], responses[1], responses[2]); }); ApiClient.getNamedConfiguration(brandingConfigKey).then(function (config) { currentBrandingOptions = config; - view.querySelector("#txtLoginDisclaimer").value = config.LoginDisclaimer || ""; - view.querySelector("#txtCustomCss").value = config.CustomCss || ""; + view.querySelector('#txtLoginDisclaimer').value = config.LoginDisclaimer || ''; + view.querySelector('#txtCustomCss').value = config.CustomCss || ''; }); }); }; diff --git a/src/controllers/dashboard/librarydisplay.js b/src/controllers/dashboard/librarydisplay.js new file mode 100644 index 0000000000..a820c37528 --- /dev/null +++ b/src/controllers/dashboard/librarydisplay.js @@ -0,0 +1,67 @@ +define(['globalize', 'loading', 'libraryMenu', 'emby-checkbox', 'emby-button', 'emby-button'], function(globalize, loading, libraryMenu) { + 'use strict'; + + function getTabs() { + return [{ + href: 'library.html', + name: globalize.translate('HeaderLibraries') + }, { + href: 'librarydisplay.html', + name: globalize.translate('TabDisplay') + }, { + href: 'metadataimages.html', + name: globalize.translate('TabMetadata') + }, { + href: 'metadatanfo.html', + name: globalize.translate('TabNfoSettings') + }]; + } + + return function(view, params) { + function loadData() { + ApiClient.getServerConfiguration().then(function(config) { + view.querySelector('.chkFolderView').checked = config.EnableFolderView; + view.querySelector('.chkGroupMoviesIntoCollections').checked = config.EnableGroupingIntoCollections; + view.querySelector('.chkDisplaySpecialsWithinSeasons').checked = config.DisplaySpecialsWithinSeasons; + view.querySelector('.chkExternalContentInSuggestions').checked = config.EnableExternalContentInSuggestions; + view.querySelector('#chkSaveMetadataHidden').checked = config.SaveMetadataHidden; + }); + ApiClient.getNamedConfiguration('metadata').then(function(metadata) { + view.querySelector('#selectDateAdded').selectedIndex = metadata.UseFileCreationTimeForDateAdded ? 1 : 0; + }); + } + + view.querySelector('form').addEventListener('submit', function(e) { + loading.show(); + var form = this; + ApiClient.getServerConfiguration().then(function(config) { + config.EnableFolderView = form.querySelector('.chkFolderView').checked; + config.EnableGroupingIntoCollections = form.querySelector('.chkGroupMoviesIntoCollections').checked; + config.DisplaySpecialsWithinSeasons = form.querySelector('.chkDisplaySpecialsWithinSeasons').checked; + config.EnableExternalContentInSuggestions = form.querySelector('.chkExternalContentInSuggestions').checked; + config.SaveMetadataHidden = form.querySelector('#chkSaveMetadataHidden').checked; + ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult); + }); + ApiClient.getNamedConfiguration('metadata').then(function(config) { + config.UseFileCreationTimeForDateAdded = '1' === $('#selectDateAdded', form).val(); + ApiClient.updateNamedConfiguration('metadata', config); + }); + + e.preventDefault(); + loading.hide(); + return false; + }); + + view.addEventListener('viewshow', function() { + libraryMenu.setTabs('librarysetup', 1, getTabs); + loadData(); + ApiClient.getSystemInfo().then(function(info) { + if ('Windows' === info.OperatingSystem) { + view.querySelector('.fldSaveMetadataHidden').classList.remove('hide'); + } else { + view.querySelector('.fldSaveMetadataHidden').classList.add('hide'); + } + }); + }); + }; +}); diff --git a/src/controllers/dashboard/logs.js b/src/controllers/dashboard/logs.js index 46b5e1ac2e..e0b000a130 100644 --- a/src/controllers/dashboard/logs.js +++ b/src/controllers/dashboard/logs.js @@ -1,31 +1,31 @@ -define(["datetime", "loading", "apphost", "listViewStyle", "emby-button", "flexStyles"], function(datetime, loading, appHost) { - "use strict"; +define(['datetime', 'loading', 'apphost', 'listViewStyle', 'emby-button', 'flexStyles'], function(datetime, loading, appHost) { + 'use strict'; return function(view, params) { - view.addEventListener("viewbeforeshow", function() { + view.addEventListener('viewbeforeshow', function() { loading.show(); var apiClient = ApiClient; - apiClient.getJSON(apiClient.getUrl("System/Logs")).then(function(logs) { - var html = ""; + apiClient.getJSON(apiClient.getUrl('System/Logs')).then(function(logs) { + var html = ''; html += '
'; html += logs.map(function(log) { - var logUrl = apiClient.getUrl("System/Logs/Log", { + var logUrl = apiClient.getUrl('System/Logs/Log', { name: log.Name }); - logUrl += "&api_key=" + apiClient.accessToken(); - var logHtml = ""; + logUrl += '&api_key=' + apiClient.accessToken(); + var logHtml = ''; logHtml += ''; logHtml += '
'; - logHtml += "

" + log.Name + "

"; + logHtml += "

" + log.Name + '

'; var date = datetime.parseISO8601Date(log.DateModified, true); var text = datetime.toLocaleDateString(date); - text += " " + datetime.getDisplayTime(date); - logHtml += '
' + text + "
"; - logHtml += "
"; - logHtml += "
"; + text += ' ' + datetime.getDisplayTime(date); + logHtml += '
' + text + '
'; + logHtml += '
'; + logHtml += ''; return logHtml; - }).join(""); - html += ""; - view.querySelector(".serverLogs").innerHTML = html; + }).join(''); + html += ''; + view.querySelector('.serverLogs').innerHTML = html; loading.hide(); }); }); diff --git a/src/controllers/medialibrarypage.js b/src/controllers/dashboard/mediaLibrary.js similarity index 58% rename from src/controllers/medialibrarypage.js rename to src/controllers/dashboard/mediaLibrary.js index d838c2145e..06eba37cbd 100644 --- a/src/controllers/medialibrarypage.js +++ b/src/controllers/dashboard/mediaLibrary.js @@ -1,8 +1,8 @@ -define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "globalize", "dom", "indicators", "scripts/imagehelper", "cardStyle", "emby-itemrefreshindicator"], function ($, appHost, taskButton, loading, libraryMenu, globalize, dom, indicators, imageHelper) { - "use strict"; +define(['jQuery', 'apphost', 'scripts/taskbutton', 'loading', 'libraryMenu', 'globalize', 'dom', 'indicators', 'scripts/imagehelper', 'cardStyle', 'emby-itemrefreshindicator'], function ($, appHost, taskButton, loading, libraryMenu, globalize, dom, indicators, imageHelper) { + 'use strict'; function addVirtualFolder(page) { - require(["medialibrarycreator"], function (medialibrarycreator) { + require(['medialibrarycreator'], function (medialibrarycreator) { new medialibrarycreator().show({ collectionTypeOptions: getCollectionTypeOptions().filter(function (f) { return !f.hidden; @@ -17,7 +17,7 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl } function editVirtualFolder(page, virtualFolder) { - require(["medialibraryeditor"], function (medialibraryeditor) { + require(['medialibraryeditor'], function (medialibraryeditor) { new medialibraryeditor().show({ refresh: shouldRefreshLibraryAfterChanges(page), library: virtualFolder @@ -30,14 +30,14 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl } function deleteVirtualFolder(page, virtualFolder) { - var msg = globalize.translate("MessageAreYouSureYouWishToRemoveMediaFolder"); + var msg = globalize.translate('MessageAreYouSureYouWishToRemoveMediaFolder'); if (virtualFolder.Locations.length) { - msg += "

" + globalize.translate("MessageTheFollowingLocationWillBeRemovedFromLibrary") + "

"; - msg += virtualFolder.Locations.join("
"); + msg += '

' + globalize.translate('MessageTheFollowingLocationWillBeRemovedFromLibrary') + '

'; + msg += virtualFolder.Locations.join('
'); } - require(["confirm"], function (confirm) { + require(['confirm'], function (confirm) { confirm({ text: msg, @@ -55,20 +55,20 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl } function refreshVirtualFolder(page, virtualFolder) { - require(["refreshDialog"], function (refreshDialog) { + require(['refreshDialog'], function (refreshDialog) { new refreshDialog({ itemIds: [virtualFolder.ItemId], serverId: ApiClient.serverId(), - mode: "scan" + mode: 'scan' }).show(); }); } function renameVirtualFolder(page, virtualFolder) { - require(["prompt"], function (prompt) { + require(['prompt'], function (prompt) { prompt({ - label: globalize.translate("LabelNewName"), - confirmText: globalize.translate("ButtonRename") + label: globalize.translate('LabelNewName'), + confirmText: globalize.translate('ButtonRename') }).then(function (newName) { if (newName && newName != virtualFolder.Name) { var refreshAfterChange = shouldRefreshLibraryAfterChanges(page); @@ -81,59 +81,59 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl } function showCardMenu(page, elem, virtualFolders) { - var card = dom.parentWithClass(elem, "card"); - var index = parseInt(card.getAttribute("data-index")); + var card = dom.parentWithClass(elem, 'card'); + var index = parseInt(card.getAttribute('data-index')); var virtualFolder = virtualFolders[index]; var menuItems = []; menuItems.push({ - name: globalize.translate("ButtonEditImages"), - id: "editimages", - icon: "photo" + name: globalize.translate('ButtonEditImages'), + id: 'editimages', + icon: 'photo' }); menuItems.push({ - name: globalize.translate("ManageLibrary"), - id: "edit", - icon: "folder_open" + name: globalize.translate('ManageLibrary'), + id: 'edit', + icon: 'folder_open' }); menuItems.push({ - name: globalize.translate("ButtonRemove"), - id: "delete", - icon: "delete" + name: globalize.translate('ButtonRemove'), + id: 'delete', + icon: 'delete' }); menuItems.push({ - name: globalize.translate("ButtonRename"), - id: "rename", - icon: "mode_edit" + name: globalize.translate('ButtonRename'), + id: 'rename', + icon: 'mode_edit' }); menuItems.push({ - name: globalize.translate("ScanLibrary"), - id: "refresh", - icon: "refresh" + name: globalize.translate('ScanLibrary'), + id: 'refresh', + icon: 'refresh' }); - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, positionTo: elem, callback: function (resultId) { switch (resultId) { - case "edit": + case 'edit': editVirtualFolder(page, virtualFolder); break; - case "editimages": + case 'editimages': editImages(page, virtualFolder); break; - case "rename": + case 'rename': renameVirtualFolder(page, virtualFolder); break; - case "delete": + case 'delete': deleteVirtualFolder(page, virtualFolder); break; - case "refresh": + case 'refresh': refreshVirtualFolder(page, virtualFolder); } } @@ -149,14 +149,14 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl } function shouldRefreshLibraryAfterChanges(page) { - return "mediaLibraryPage" === page.id; + return 'mediaLibraryPage' === page.id; } function reloadVirtualFolders(page, virtualFolders) { - var html = ""; + var html = ''; virtualFolders.push({ - Name: globalize.translate("ButtonAddMediaLibrary"), - icon: "add_circle", + Name: globalize.translate('ButtonAddMediaLibrary'), + icon: 'add_circle', Locations: [], showType: false, showLocations: false, @@ -169,19 +169,19 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl html += getVirtualFolderHtml(page, virtualFolder, i); } - var divVirtualFolders = page.querySelector("#divVirtualFolders"); + var divVirtualFolders = page.querySelector('#divVirtualFolders'); divVirtualFolders.innerHTML = html; - divVirtualFolders.classList.add("itemsContainer"); - divVirtualFolders.classList.add("vertical-wrap"); - $(".btnCardMenu", divVirtualFolders).on("click", function () { + divVirtualFolders.classList.add('itemsContainer'); + divVirtualFolders.classList.add('vertical-wrap'); + $('.btnCardMenu', divVirtualFolders).on('click', function () { showCardMenu(page, this, virtualFolders); }); - divVirtualFolders.querySelector(".addLibrary").addEventListener("click", function () { + divVirtualFolders.querySelector('.addLibrary').addEventListener('click', function () { addVirtualFolder(page); }); - $(".editLibrary", divVirtualFolders).on("click", function () { - var card = $(this).parents(".card")[0]; - var index = parseInt(card.getAttribute("data-index")); + $('.editLibrary', divVirtualFolders).on('click', function () { + var card = $(this).parents('.card')[0]; + var index = parseInt(card.getAttribute('data-index')); var virtualFolder = virtualFolders[index]; if (virtualFolder.ItemId) { @@ -192,7 +192,7 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl } function editImages(page, virtualFolder) { - require(["imageEditor"], function (imageEditor) { + require(['imageEditor'], function (imageEditor) { imageEditor.show({ itemId: virtualFolder.ItemId, serverId: ApiClient.serverId() @@ -203,48 +203,48 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl } function getLink(text, url) { - return globalize.translate(text, '', ""); + return globalize.translate(text, '', ''); } function getCollectionTypeOptions() { return [{ - name: "", - value: "" + name: '', + value: '' }, { - name: globalize.translate("FolderTypeMovies"), - value: "movies", - message: getLink("MovieLibraryHelp", "https://docs.jellyfin.org/general/server/media/movies.html") + name: globalize.translate('FolderTypeMovies'), + value: 'movies', + message: getLink('MovieLibraryHelp', 'https://docs.jellyfin.org/general/server/media/movies.html') }, { - name: globalize.translate("FolderTypeMusic"), - value: "music", - message: getLink("MusicLibraryHelp", "https://docs.jellyfin.org/general/server/media/music.html") + name: globalize.translate('FolderTypeMusic'), + value: 'music', + message: getLink('MusicLibraryHelp', 'https://docs.jellyfin.org/general/server/media/music.html') }, { - name: globalize.translate("FolderTypeTvShows"), - value: "tvshows", - message: getLink("TvLibraryHelp", "https://docs.jellyfin.org/general/server/media/shows.html") + name: globalize.translate('FolderTypeTvShows'), + value: 'tvshows', + message: getLink('TvLibraryHelp', 'https://docs.jellyfin.org/general/server/media/shows.html') }, { - name: globalize.translate("FolderTypeBooks"), - value: "books", - message: getLink("BookLibraryHelp", "https://docs.jellyfin.org/general/server/media/books.html") + name: globalize.translate('FolderTypeBooks'), + value: 'books', + message: getLink('BookLibraryHelp', 'https://docs.jellyfin.org/general/server/media/books.html') }, { - name: globalize.translate("OptionHomeVideos"), - value: "homevideos" + name: globalize.translate('OptionHomeVideos'), + value: 'homevideos' }, { - name: globalize.translate("FolderTypeMusicVideos"), - value: "musicvideos" + name: globalize.translate('FolderTypeMusicVideos'), + value: 'musicvideos' }, { - name: globalize.translate("FolderTypeUnset"), - value: "mixed", - message: globalize.translate("MessageUnsetContentHelp") + name: globalize.translate('FolderTypeUnset'), + value: 'mixed', + message: globalize.translate('MessageUnsetContentHelp') }]; } function getVirtualFolderHtml(page, virtualFolder, index) { - var html = ""; - var style = ""; + var html = ''; + var style = ''; - if (page.classList.contains("wizardPage")) { - style += "min-width:33.3%;"; + if (page.classList.contains('wizardPage')) { + style += 'min-width:33.3%;'; } html += '
'; @@ -252,12 +252,12 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl html += '
'; html += '
'; html += '
'; - var imgUrl = ""; + var imgUrl = ''; if (virtualFolder.PrimaryImageItemId) { imgUrl = ApiClient.getScaledImageUrl(virtualFolder.PrimaryImageItemId, { maxWidth: Math.round(dom.getScreenWidth() * 0.40), - type: "Primary" + type: 'Primary' }); } @@ -268,124 +268,124 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl hasCardImageContainer = true; } else if (!virtualFolder.showNameWithIcon) { html += '
'; - html += '' + (virtualFolder.icon || imageHelper.getLibraryIcon(virtualFolder.CollectionType)) + ""; + html += ''; hasCardImageContainer = true; } if (hasCardImageContainer) { html += '
'; - html += '
'; - html += "
"; - html += "
"; + html += '
'; + html += '
'; + html += '
'; } if (!imgUrl && virtualFolder.showNameWithIcon) { html += '

'; - html += '' + (virtualFolder.icon || imageHelper.getLibraryIcon(virtualFolder.CollectionType)) + ""; + html += ''; if (virtualFolder.showNameWithIcon) { html += '
'; html += virtualFolder.Name; - html += "
"; + html += '

'; } - html += ""; + html += ''; } - html += ""; - html += ""; + html += ''; + html += ''; html += '
'; // always show menu unless explicitly hidden if (virtualFolder.showMenu !== false) { html += '
'; - html += ''; - html += "
"; + html += ''; + html += '
'; } html += "
"; if (virtualFolder.showNameWithIcon) { - html += " "; + html += ' '; } else { html += virtualFolder.Name; } - html += "
"; + html += ''; var typeName = getCollectionTypeOptions().filter(function (t) { return t.value == virtualFolder.CollectionType; })[0]; - typeName = typeName ? typeName.name : globalize.translate("FolderTypeUnset"); + typeName = typeName ? typeName.name : globalize.translate('FolderTypeUnset'); html += "
"; if (virtualFolder.showType === false) { - html += " "; + html += ' '; } else { html += typeName; } - html += "
"; + html += ''; if (virtualFolder.showLocations === false) { html += "
"; - html += " "; - html += "
"; + html += ' '; + html += ''; } else if (virtualFolder.Locations.length && virtualFolder.Locations.length === 1) { html += "
"; html += virtualFolder.Locations[0]; - html += "
"; + html += ''; } else { html += "
"; - html += globalize.translate("NumLocationsValue", virtualFolder.Locations.length); - html += "
"; + html += globalize.translate('NumLocationsValue', virtualFolder.Locations.length); + html += ''; } - html += ""; - html += ""; - html += ""; + html += ''; + html += ''; + html += ''; return html; } function getTabs() { return [{ - href: "library.html", - name: globalize.translate("HeaderLibraries") + href: 'library.html', + name: globalize.translate('HeaderLibraries') }, { - href: "librarydisplay.html", - name: globalize.translate("TabDisplay") + href: 'librarydisplay.html', + name: globalize.translate('TabDisplay') }, { - href: "metadataimages.html", - name: globalize.translate("TabMetadata") + href: 'metadataimages.html', + name: globalize.translate('TabMetadata') }, { - href: "metadatanfo.html", - name: globalize.translate("TabNfoSettings") + href: 'metadatanfo.html', + name: globalize.translate('TabNfoSettings') }]; } window.WizardLibraryPage = { next: function () { - Dashboard.navigate("wizardsettings.html"); + Dashboard.navigate('wizardsettings.html'); } }; - pageClassOn("pageshow", "mediaLibraryPage", function () { + pageClassOn('pageshow', 'mediaLibraryPage', function () { reloadLibrary(this); }); - pageIdOn("pageshow", "mediaLibraryPage", function () { - libraryMenu.setTabs("librarysetup", 0, getTabs); + pageIdOn('pageshow', 'mediaLibraryPage', function () { + libraryMenu.setTabs('librarysetup', 0, getTabs); var page = this; taskButton({ - mode: "on", - progressElem: page.querySelector(".refreshProgress"), - taskKey: "RefreshLibrary", - button: page.querySelector(".btnRefresh") + mode: 'on', + progressElem: page.querySelector('.refreshProgress'), + taskKey: 'RefreshLibrary', + button: page.querySelector('.btnRefresh') }); }); - pageIdOn("pagebeforehide", "mediaLibraryPage", function () { + pageIdOn('pagebeforehide', 'mediaLibraryPage', function () { var page = this; taskButton({ - mode: "off", - progressElem: page.querySelector(".refreshProgress"), - taskKey: "RefreshLibrary", - button: page.querySelector(".btnRefresh") + mode: 'off', + progressElem: page.querySelector('.refreshProgress'), + taskKey: 'RefreshLibrary', + button: page.querySelector('.btnRefresh') }); }); }); diff --git a/src/controllers/dashboard/metadataImages.js b/src/controllers/dashboard/metadataImages.js new file mode 100644 index 0000000000..3047736a68 --- /dev/null +++ b/src/controllers/dashboard/metadataImages.js @@ -0,0 +1,70 @@ +define(['jQuery', 'dom', 'loading', 'libraryMenu', 'globalize', 'listViewStyle'], function($, dom, loading, libraryMenu, globalize) { + 'use strict'; + + function populateLanguages(select) { + return ApiClient.getCultures().then(function(languages) { + var html = ''; + html += ""; + for (var i = 0, length = languages.length; i < length; i++) { + var culture = languages[i]; + html += "'; + } + select.innerHTML = html; + }); + } + + function populateCountries(select) { + return ApiClient.getCountries().then(function(allCountries) { + var html = ''; + html += ""; + for (var i = 0, length = allCountries.length; i < length; i++) { + var culture = allCountries[i]; + html += "'; + } + select.innerHTML = html; + }); + } + + function loadPage(page) { + var promises = [ApiClient.getServerConfiguration(), populateLanguages(page.querySelector('#selectLanguage')), populateCountries(page.querySelector('#selectCountry'))]; + Promise.all(promises).then(function(responses) { + var config = responses[0]; + page.querySelector('#selectLanguage').value = config.PreferredMetadataLanguage || ''; + page.querySelector('#selectCountry').value = config.MetadataCountryCode || ''; + loading.hide(); + }); + } + + function onSubmit() { + var form = this; + return loading.show(), ApiClient.getServerConfiguration().then(function(config) { + config.PreferredMetadataLanguage = form.querySelector('#selectLanguage').value; + config.MetadataCountryCode = form.querySelector('#selectCountry').value; + ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult); + }), !1; + } + + function getTabs() { + return [{ + href: 'library.html', + name: globalize.translate('HeaderLibraries') + }, { + href: 'librarydisplay.html', + name: globalize.translate('TabDisplay') + }, { + href: 'metadataimages.html', + name: globalize.translate('TabMetadata') + }, { + href: 'metadatanfo.html', + name: globalize.translate('TabNfoSettings') + }]; + } + + $(document).on('pageinit', '#metadataImagesConfigurationPage', function() { + $('.metadataImagesConfigurationForm').off('submit', onSubmit).on('submit', onSubmit); + }).on('pageshow', '#metadataImagesConfigurationPage', function() { + libraryMenu.setTabs('metadata', 2, getTabs); + loading.show(); + loadPage(this); + }); +}); diff --git a/src/controllers/dashboard/metadatanfo.js b/src/controllers/dashboard/metadatanfo.js new file mode 100644 index 0000000000..a936192618 --- /dev/null +++ b/src/controllers/dashboard/metadatanfo.js @@ -0,0 +1,74 @@ +define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, libraryMenu, globalize) { + 'use strict'; + + function loadPage(page, config, users) { + var html = ''; + html += users.map(function (user) { + return ''; + }).join(''); + $('#selectUser', page).html(html).val(config.UserId || ''); + $('#selectReleaseDateFormat', page).val(config.ReleaseDateFormat); + page.querySelector('#chkSaveImagePaths').checked = config.SaveImagePathsInNfo; + page.querySelector('#chkEnablePathSubstitution').checked = config.EnablePathSubstitution; + page.querySelector('#chkEnableExtraThumbs').checked = config.EnableExtraThumbsDuplication; + loading.hide(); + } + + function onSubmit() { + loading.show(); + var form = this; + ApiClient.getNamedConfiguration(metadataKey).then(function (config) { + config.UserId = $('#selectUser', form).val() || null; + config.ReleaseDateFormat = $('#selectReleaseDateFormat', form).val(); + config.SaveImagePathsInNfo = form.querySelector('#chkSaveImagePaths').checked; + config.EnablePathSubstitution = form.querySelector('#chkEnablePathSubstitution').checked; + config.EnableExtraThumbsDuplication = form.querySelector('#chkEnableExtraThumbs').checked; + ApiClient.updateNamedConfiguration(metadataKey, config).then(function () { + Dashboard.processServerConfigurationUpdateResult(); + showConfirmMessage(config); + }); + }); + return false; + } + + function showConfirmMessage(config) { + var msg = []; + msg.push(globalize.translate('MetadataSettingChangeHelp')); + + require(['alert'], function (alert) { + alert({ + text: msg.join('

') + }); + }); + } + + function getTabs() { + return [{ + href: 'library.html', + name: globalize.translate('HeaderLibraries') + }, { + href: 'librarydisplay.html', + name: globalize.translate('TabDisplay') + }, { + href: 'metadataimages.html', + name: globalize.translate('TabMetadata') + }, { + href: 'metadatanfo.html', + name: globalize.translate('TabNfoSettings') + }]; + } + + var metadataKey = 'xbmcmetadata'; + $(document).on('pageinit', '#metadataNfoPage', function () { + $('.metadataNfoForm').off('submit', onSubmit).on('submit', onSubmit); + }).on('pageshow', '#metadataNfoPage', function () { + libraryMenu.setTabs('metadata', 3, getTabs); + loading.show(); + var page = this; + var promise1 = ApiClient.getUsers(); + var promise2 = ApiClient.getNamedConfiguration(metadataKey); + Promise.all([promise1, promise2]).then(function (responses) { + loadPage(page, responses[1], responses[0]); + }); + }); +}); diff --git a/src/controllers/dashboard/networking.js b/src/controllers/dashboard/networking.js index c0e4d76d0d..4ddde7f24c 100644 --- a/src/controllers/dashboard/networking.js +++ b/src/controllers/dashboard/networking.js @@ -1,68 +1,43 @@ -define(["loading", "libraryMenu", "globalize", "emby-checkbox", "emby-select"], function (loading, libraryMenu, globalize) { - "use strict"; +define(['loading', 'libraryMenu', 'globalize', 'emby-checkbox', 'emby-select'], function (loading, libraryMenu, globalize) { + 'use strict'; function onSubmit(e) { var form = this; - var localAddress = form.querySelector("#txtLocalAddress").value; - var enableUpnp = form.querySelector("#chkEnableUpnp").checked; + var localAddress = form.querySelector('#txtLocalAddress').value; + var enableUpnp = form.querySelector('#chkEnableUpnp').checked; confirmSelections(localAddress, enableUpnp, function () { var validationResult = getValidationAlert(form); if (validationResult) { - alertText(validationResult); + showAlertText(validationResult); return; } validateHttps(form).then(function () { loading.show(); ApiClient.getServerConfiguration().then(function (config) { - config.LocalNetworkSubnets = form.querySelector("#txtLanNetworks").value.split(",").map(function (s) { + config.LocalNetworkSubnets = form.querySelector('#txtLanNetworks').value.split(',').map(function (s) { return s.trim(); }).filter(function (s) { return s.length > 0; }); - config.RemoteIPFilter = form.querySelector("#txtExternalAddressFilter").value.split(",").map(function (s) { + config.RemoteIPFilter = form.querySelector('#txtExternalAddressFilter').value.split(',').map(function (s) { return s.trim(); }).filter(function (s) { return s.length > 0; }); - config.IsRemoteIPFilterBlacklist = "blacklist" === form.querySelector("#selectExternalAddressFilterMode").value; - config.PublicPort = form.querySelector("#txtPublicPort").value; - config.PublicHttpsPort = form.querySelector("#txtPublicHttpsPort").value; - var httpsMode = form.querySelector("#selectHttpsMode").value; - - switch (httpsMode) { - case "proxy": - config.EnableHttps = true; - config.RequireHttps = false; - config.IsBehindProxy = true; - break; - - case "required": - config.EnableHttps = true; - config.RequireHttps = true; - config.IsBehindProxy = false; - break; - - case "enabled": - config.EnableHttps = true; - config.RequireHttps = false; - config.IsBehindProxy = false; - break; - - default: - config.EnableHttps = false; - config.RequireHttps = false; - config.IsBehindProxy = false; - } - - config.HttpsPortNumber = form.querySelector("#txtHttpsPort").value; - config.HttpServerPortNumber = form.querySelector("#txtPortNumber").value; + config.IsRemoteIPFilterBlacklist = 'blacklist' === form.querySelector('#selectExternalAddressFilterMode').value; + config.PublicPort = form.querySelector('#txtPublicPort').value; + config.PublicHttpsPort = form.querySelector('#txtPublicHttpsPort').value; + config.HttpServerPortNumber = form.querySelector('#txtPortNumber').value; + config.HttpsPortNumber = form.querySelector('#txtHttpsPort').value; + config.EnableHttps = form.querySelector('#chkEnableHttps').checked; + config.RequireHttps = form.querySelector('#chkRequireHttps').checked; config.EnableUPnP = enableUpnp; - config.BaseUrl = form.querySelector("#txtBaseUrl").value; - config.EnableRemoteAccess = form.querySelector("#chkRemoteAccess").checked; - config.CertificatePath = form.querySelector("#txtCertificatePath").value || null; - config.CertificatePassword = form.querySelector("#txtCertPassword").value || null; + config.BaseUrl = form.querySelector('#txtBaseUrl').value; + config.EnableRemoteAccess = form.querySelector('#chkRemoteAccess').checked; + config.CertificatePath = form.querySelector('#txtCertificatePath').value || null; + config.CertificatePassword = form.querySelector('#txtCertPassword').value || null; config.LocalNetworkAddresses = localAddress ? [localAddress] : []; ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult, Dashboard.processErrorResponse); }); @@ -72,43 +47,40 @@ define(["loading", "libraryMenu", "globalize", "emby-checkbox", "emby-select"], } function triggerChange(select) { - var evt = document.createEvent("HTMLEvents"); - evt.initEvent("change", false, true); + var evt = document.createEvent('HTMLEvents'); + evt.initEvent('change', false, true); select.dispatchEvent(evt); } function getValidationAlert(form) { - if (form.querySelector("#txtPublicPort").value === form.querySelector("#txtPublicHttpsPort").value) { - return "The public http and https ports must be different."; + if (form.querySelector('#txtPublicPort').value === form.querySelector('#txtPublicHttpsPort').value) { + return 'The public http and https ports must be different.'; } - if (form.querySelector("#txtPortNumber").value === form.querySelector("#txtHttpsPort").value) { - return "The http and https ports must be different."; + if (form.querySelector('#txtPortNumber').value === form.querySelector('#txtHttpsPort').value) { + return 'The http and https ports must be different.'; } return null; } function validateHttps(form) { - var remoteAccess = form.querySelector("#chkRemoteAccess").checked; - var certPath = form.querySelector("#txtCertificatePath").value || null; - var httpsMode = form.querySelector("#selectHttpsMode").value; + var certPath = form.querySelector('#txtCertificatePath').value || null; + var httpsEnabled = form.querySelector('#chkEnableHttps').checked; - if (!remoteAccess || ("enabled" !== httpsMode && "required" !== httpsMode || certPath)) { - return Promise.resolve(); + if (httpsEnabled && !certPath) { + return showAlertText({ + title: globalize.translate('TitleHostingSettings'), + text: globalize.translate('HttpsRequiresCert') + }).then(Promise.reject); } - return new Promise(function (resolve, reject) { - return alertText({ - title: globalize.translate("TitleHostingSettings"), - text: globalize.translate("HttpsRequiresCert") - }).then(reject, reject); - }); + return Promise.resolve(); } - function alertText(options) { + function showAlertText(options) { return new Promise(function (resolve, reject) { - require(["alert"], function (alert) { + require(['alert'], function (alert) { alert(options).then(resolve, reject); }); }); @@ -116,9 +88,9 @@ define(["loading", "libraryMenu", "globalize", "emby-checkbox", "emby-select"], function confirmSelections(localAddress, enableUpnp, callback) { if (localAddress || !enableUpnp) { - alertText({ - title: globalize.translate("TitleHostingSettings"), - text: globalize.translate("SettingsWarning") + showAlertText({ + title: globalize.translate('TitleHostingSettings'), + text: globalize.translate('SettingsWarning') }).then(callback); } else { callback(); @@ -127,76 +99,60 @@ define(["loading", "libraryMenu", "globalize", "emby-checkbox", "emby-select"], return function (view, params) { function loadPage(page, config) { - page.querySelector("#txtPortNumber").value = config.HttpServerPortNumber; - page.querySelector("#txtPublicPort").value = config.PublicPort; - page.querySelector("#txtPublicHttpsPort").value = config.PublicHttpsPort; - page.querySelector("#txtLocalAddress").value = config.LocalNetworkAddresses[0] || ""; - page.querySelector("#txtLanNetworks").value = (config.LocalNetworkSubnets || []).join(", "); - page.querySelector("#txtExternalAddressFilter").value = (config.RemoteIPFilter || []).join(", "); - page.querySelector("#selectExternalAddressFilterMode").value = config.IsRemoteIPFilterBlacklist ? "blacklist" : "whitelist"; - page.querySelector("#chkRemoteAccess").checked = null == config.EnableRemoteAccess || config.EnableRemoteAccess; - var selectHttpsMode = page.querySelector("#selectHttpsMode"); - - if (config.IsBehindProxy) { - selectHttpsMode.value = "proxy"; - } else if (config.RequireHttps) { - selectHttpsMode.value = "required"; - } else if (config.EnableHttps) { - selectHttpsMode.value = "enabled"; - } else { - selectHttpsMode.value = "disabled"; - } - - page.querySelector("#txtHttpsPort").value = config.HttpsPortNumber; - page.querySelector("#txtBaseUrl").value = config.BaseUrl || ""; - var txtCertificatePath = page.querySelector("#txtCertificatePath"); - txtCertificatePath.value = config.CertificatePath || ""; - page.querySelector("#txtCertPassword").value = config.CertificatePassword || ""; - page.querySelector("#chkEnableUpnp").checked = config.EnableUPnP; - triggerChange(page.querySelector("#chkRemoteAccess")); + page.querySelector('#txtPortNumber').value = config.HttpServerPortNumber; + page.querySelector('#txtPublicPort').value = config.PublicPort; + page.querySelector('#txtPublicHttpsPort').value = config.PublicHttpsPort; + page.querySelector('#txtLocalAddress').value = config.LocalNetworkAddresses[0] || ''; + page.querySelector('#txtLanNetworks').value = (config.LocalNetworkSubnets || []).join(', '); + page.querySelector('#txtExternalAddressFilter').value = (config.RemoteIPFilter || []).join(', '); + page.querySelector('#selectExternalAddressFilterMode').value = config.IsRemoteIPFilterBlacklist ? 'blacklist' : 'whitelist'; + page.querySelector('#chkRemoteAccess').checked = null == config.EnableRemoteAccess || config.EnableRemoteAccess; + page.querySelector('#txtHttpsPort').value = config.HttpsPortNumber; + page.querySelector('#chkEnableHttps').checked = config.EnableHttps; + page.querySelector('#chkRequireHttps').checked = config.RequireHttps; + page.querySelector('#txtBaseUrl').value = config.BaseUrl || ''; + var txtCertificatePath = page.querySelector('#txtCertificatePath'); + txtCertificatePath.value = config.CertificatePath || ''; + page.querySelector('#txtCertPassword').value = config.CertificatePassword || ''; + page.querySelector('#chkEnableUpnp').checked = config.EnableUPnP; + triggerChange(page.querySelector('#chkRemoteAccess')); loading.hide(); } - view.querySelector("#chkRemoteAccess").addEventListener("change", function () { + view.querySelector('#chkRemoteAccess').addEventListener('change', function () { if (this.checked) { - view.querySelector(".fldExternalAddressFilter").classList.remove("hide"); - view.querySelector(".fldExternalAddressFilterMode").classList.remove("hide"); - view.querySelector(".fldPublicPort").classList.remove("hide"); - view.querySelector(".fldPublicHttpsPort").classList.remove("hide"); - view.querySelector(".fldCertificatePath").classList.remove("hide"); - view.querySelector(".fldCertPassword").classList.remove("hide"); - view.querySelector(".fldHttpsMode").classList.remove("hide"); - view.querySelector(".fldEnableUpnp").classList.remove("hide"); + view.querySelector('.fldExternalAddressFilter').classList.remove('hide'); + view.querySelector('.fldExternalAddressFilterMode').classList.remove('hide'); + view.querySelector('.fldPublicPort').classList.remove('hide'); + view.querySelector('.fldPublicHttpsPort').classList.remove('hide'); + view.querySelector('.fldEnableUpnp').classList.remove('hide'); } else { - view.querySelector(".fldExternalAddressFilter").classList.add("hide"); - view.querySelector(".fldExternalAddressFilterMode").classList.add("hide"); - view.querySelector(".fldPublicPort").classList.add("hide"); - view.querySelector(".fldPublicHttpsPort").classList.add("hide"); - view.querySelector(".fldCertificatePath").classList.add("hide"); - view.querySelector(".fldCertPassword").classList.add("hide"); - view.querySelector(".fldHttpsMode").classList.add("hide"); - view.querySelector(".fldEnableUpnp").classList.add("hide"); + view.querySelector('.fldExternalAddressFilter').classList.add('hide'); + view.querySelector('.fldExternalAddressFilterMode').classList.add('hide'); + view.querySelector('.fldPublicPort').classList.add('hide'); + view.querySelector('.fldPublicHttpsPort').classList.add('hide'); + view.querySelector('.fldEnableUpnp').classList.add('hide'); } }); - view.querySelector("#btnSelectCertPath").addEventListener("click", function () { - require(["directorybrowser"], function (directoryBrowser) { + view.querySelector('#btnSelectCertPath').addEventListener('click', function () { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ includeFiles: true, includeDirectories: true, callback: function (path) { if (path) { - view.querySelector("#txtCertificatePath").value = path; + view.querySelector('#txtCertificatePath').value = path; } picker.close(); }, - header: globalize.translate("HeaderSelectCertificatePath") + header: globalize.translate('HeaderSelectCertificatePath') }); }); }); - view.querySelector(".dashboardHostingForm").addEventListener("submit", onSubmit); - view.addEventListener("viewshow", function (e) { + view.querySelector('.dashboardHostingForm').addEventListener('submit', onSubmit); + view.addEventListener('viewshow', function (e) { loading.show(); ApiClient.getServerConfiguration().then(function (config) { loadPage(view, config); diff --git a/src/controllers/dashboard/notifications/notification.js b/src/controllers/dashboard/notifications/notification.js index 70e6adaf61..370fc6a369 100644 --- a/src/controllers/dashboard/notifications/notification.js +++ b/src/controllers/dashboard/notifications/notification.js @@ -1,23 +1,23 @@ -define(["jQuery", "emby-checkbox", "fnchecked"], function ($) { - "use strict"; +define(['jQuery', 'emby-checkbox'], function ($) { + 'use strict'; function fillItems(elem, items, cssClass, idPrefix, currentList, isEnabledList) { var html = '
'; html += items.map(function (u) { var isChecked = isEnabledList ? currentList.indexOf(u.Id) != -1 : currentList.indexOf(u.Id) == -1; - var checkedHtml = isChecked ? ' checked="checked"' : ""; - return '"; - }).join(""); - html += "
"; - elem.html(html).trigger("create"); + var checkedHtml = isChecked ? ' checked="checked"' : ''; + return ''; + }).join(''); + html += ''; + elem.html(html).trigger('create'); } function reload(page) { - var type = getParameterByName("type"); + var type = getParameterByName('type'); var promise1 = ApiClient.getUsers(); var promise2 = ApiClient.getNamedConfiguration(notificationsConfigurationKey); - var promise3 = ApiClient.getJSON(ApiClient.getUrl("Notifications/Types")); - var promise4 = ApiClient.getJSON(ApiClient.getUrl("Notifications/Services")); + var promise3 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types')); + var promise4 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Services')); Promise.all([promise1, promise2, promise3, promise4]).then(function (responses) { var users = responses[0]; var notificationOptions = responses[1]; @@ -31,37 +31,37 @@ define(["jQuery", "emby-checkbox", "fnchecked"], function ($) { })[0] || {}; if (typeInfo.IsBasedOnUserEvent) { - $(".monitorUsers", page).show(); + $('.monitorUsers', page).show(); } else { - $(".monitorUsers", page).hide(); + $('.monitorUsers', page).hide(); } - $(".notificationType", page).html(typeInfo.Name || "Unknown Notification"); + $('.notificationType', page).html(typeInfo.Name || 'Unknown Notification'); if (!notificationConfig) { notificationConfig = { DisabledMonitorUsers: [], SendToUsers: [], DisabledServices: [], - SendToUserMode: "Admins" + SendToUserMode: 'Admins' }; } - fillItems($(".monitorUsersList", page), users, "chkMonitor", "chkMonitor", notificationConfig.DisabledMonitorUsers); - fillItems($(".sendToUsersList", page), users, "chkSendTo", "chkSendTo", notificationConfig.SendToUsers, true); - fillItems($(".servicesList", page), services, "chkService", "chkService", notificationConfig.DisabledServices); - $("#chkEnabled", page).checked(notificationConfig.Enabled || false); - $("#selectUsers", page).val(notificationConfig.SendToUserMode).trigger("change"); + fillItems($('.monitorUsersList', page), users, 'chkMonitor', 'chkMonitor', notificationConfig.DisabledMonitorUsers); + fillItems($('.sendToUsersList', page), users, 'chkSendTo', 'chkSendTo', notificationConfig.SendToUsers, true); + fillItems($('.servicesList', page), services, 'chkService', 'chkService', notificationConfig.DisabledServices); + $('#chkEnabled', page).checked = notificationConfig.Enabled || false; + $('#selectUsers', page).val(notificationConfig.SendToUserMode).trigger('change'); }); } function save(page) { - var type = getParameterByName("type"); + var type = getParameterByName('type'); var promise1 = ApiClient.getNamedConfiguration(notificationsConfigurationKey); - var promise2 = ApiClient.getJSON(ApiClient.getUrl("Notifications/Types")); + // TODO: Check if this promise is really needed, as it's unused. + var promise2 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types')); Promise.all([promise1, promise2]).then(function (responses) { var notificationOptions = responses[0]; - var types = responses[1]; var notificationConfig = notificationOptions.Options.filter(function (n) { return n.Type == type; })[0]; @@ -73,50 +73,47 @@ define(["jQuery", "emby-checkbox", "fnchecked"], function ($) { notificationOptions.Options.push(notificationConfig); } - types.filter(function (n) { - return n.Type == type; - })[0]; - notificationConfig.Enabled = $("#chkEnabled", page).checked(); - notificationConfig.SendToUserMode = $("#selectUsers", page).val(); - notificationConfig.DisabledMonitorUsers = $(".chkMonitor", page).get().filter(function (c) { + notificationConfig.Enabled = $('#chkEnabled', page).checked; + notificationConfig.SendToUserMode = $('#selectUsers', page).val(); + notificationConfig.DisabledMonitorUsers = $('.chkMonitor', page).get().filter(function (c) { return !c.checked; }).map(function (c) { - return c.getAttribute("data-itemid"); + return c.getAttribute('data-itemid'); }); - notificationConfig.SendToUsers = $(".chkSendTo", page).get().filter(function (c) { + notificationConfig.SendToUsers = $('.chkSendTo', page).get().filter(function (c) { return c.checked; }).map(function (c) { - return c.getAttribute("data-itemid"); + return c.getAttribute('data-itemid'); }); - notificationConfig.DisabledServices = $(".chkService", page).get().filter(function (c) { + notificationConfig.DisabledServices = $('.chkService', page).get().filter(function (c) { return !c.checked; }).map(function (c) { - return c.getAttribute("data-itemid"); + return c.getAttribute('data-itemid'); }); ApiClient.updateNamedConfiguration(notificationsConfigurationKey, notificationOptions).then(function (r) { Dashboard.processServerConfigurationUpdateResult(); - Dashboard.navigate("notificationsettings.html"); + Dashboard.navigate('notificationsettings.html'); }); }); } function onSubmit() { - save($(this).parents(".page")); + save($(this).parents('.page')); return false; } - var notificationsConfigurationKey = "notifications"; - $(document).on("pageinit", "#notificationSettingPage", function () { + var notificationsConfigurationKey = 'notifications'; + $(document).on('pageinit', '#notificationSettingPage', function () { var page = this; - $("#selectUsers", page).on("change", function () { - if ("Custom" == this.value) { - $(".selectCustomUsers", page).show(); + $('#selectUsers', page).on('change', function () { + if ('Custom' == this.value) { + $('.selectCustomUsers', page).show(); } else { - $(".selectCustomUsers", page).hide(); + $('.selectCustomUsers', page).hide(); } }); - $(".notificationSettingForm").off("submit", onSubmit).on("submit", onSubmit); - }).on("pageshow", "#notificationSettingPage", function () { + $('.notificationSettingForm').off('submit', onSubmit).on('submit', onSubmit); + }).on('pageshow', '#notificationSettingPage', function () { reload(this); }); }); diff --git a/src/controllers/dashboard/notifications/notifications.js b/src/controllers/dashboard/notifications/notifications.js index 3c0b5e9b36..4e049bc106 100644 --- a/src/controllers/dashboard/notifications/notifications.js +++ b/src/controllers/dashboard/notifications/notifications.js @@ -1,59 +1,59 @@ -define(["loading", "libraryMenu", "globalize", "listViewStyle", "emby-button"], function(loading, libraryMenu, globalize) { - "use strict"; +define(['loading', 'libraryMenu', 'globalize', 'listViewStyle', 'emby-button'], function(loading, libraryMenu, globalize) { + 'use strict'; function reload(page) { loading.show(); - ApiClient.getJSON(ApiClient.getUrl("Notifications/Types")).then(function(list) { - var html = ""; - var lastCategory = ""; + ApiClient.getJSON(ApiClient.getUrl('Notifications/Types')).then(function(list) { + var html = ''; + var lastCategory = ''; var showHelp = true; html += list.map(function(notification) { - var itemHtml = ""; + var itemHtml = ''; if (notification.Category !== lastCategory) { lastCategory = notification.Category; if (lastCategory) { - itemHtml += ""; - itemHtml += ""; + itemHtml += ''; + itemHtml += ''; } itemHtml += '
'; itemHtml += '
'; itemHtml += '

'; itemHtml += notification.Category; - itemHtml += "

"; + itemHtml += ''; if (showHelp) { showHelp = false; itemHtml += ''; - itemHtml += globalize.translate("Help"); - itemHtml += ""; + itemHtml += globalize.translate('Help'); + itemHtml += ''; } - itemHtml += "
"; + itemHtml += '
'; itemHtml += ''; + itemHtml += ''; + itemHtml += ''; return itemHtml; - }).join(""); + }).join(''); if (list.length) { - html += ""; - html += ""; + html += ''; + html += ''; } - page.querySelector(".notificationList").innerHTML = html; + page.querySelector('.notificationList').innerHTML = html; loading.hide(); }); } return function(view, params) { - view.addEventListener("viewshow", function() { + view.addEventListener('viewshow', function() { reload(view); }); }; diff --git a/src/controllers/playbackconfiguration.js b/src/controllers/dashboard/playback.js similarity index 50% rename from src/controllers/playbackconfiguration.js rename to src/controllers/dashboard/playback.js index cd2136cea9..d5b67a2961 100644 --- a/src/controllers/playbackconfiguration.js +++ b/src/controllers/dashboard/playback.js @@ -1,10 +1,10 @@ -define(["jQuery", "loading", "libraryMenu"], function ($, loading, libraryMenu) { - "use strict"; +define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, libraryMenu, globalize) { + 'use strict'; function loadPage(page, config) { - $("#txtMinResumePct", page).val(config.MinResumePct); - $("#txtMaxResumePct", page).val(config.MaxResumePct); - $("#txtMinResumeDuration", page).val(config.MinResumeDurationSeconds); + $('#txtMinResumePct', page).val(config.MinResumePct); + $('#txtMaxResumePct', page).val(config.MaxResumePct); + $('#txtMinResumeDuration', page).val(config.MinResumeDurationSeconds); loading.hide(); } @@ -24,22 +24,22 @@ define(["jQuery", "loading", "libraryMenu"], function ($, loading, libraryMenu) function getTabs() { return [{ - href: "encodingsettings.html", - name: Globalize.translate("Transcoding") + href: 'encodingsettings.html', + name: globalize.translate('Transcoding') }, { - href: "playbackconfiguration.html", - name: Globalize.translate("TabResumeSettings") + href: 'playbackconfiguration.html', + name: globalize.translate('TabResumeSettings') }, { - href: "streamingsettings.html", - name: Globalize.translate("TabStreaming") + href: 'streamingsettings.html', + name: globalize.translate('TabStreaming') }]; } - $(document).on("pageinit", "#playbackConfigurationPage", function () { - $(".playbackConfigurationForm").off("submit", onSubmit).on("submit", onSubmit); - }).on("pageshow", "#playbackConfigurationPage", function () { + $(document).on('pageinit', '#playbackConfigurationPage', function () { + $('.playbackConfigurationForm').off('submit', onSubmit).on('submit', onSubmit); + }).on('pageshow', '#playbackConfigurationPage', function () { loading.show(); - libraryMenu.setTabs("playback", 1, getTabs); + libraryMenu.setTabs('playback', 1, getTabs); var page = this; ApiClient.getServerConfiguration().then(function (config) { loadPage(page, config); diff --git a/src/controllers/dashboard/plugins/add.js b/src/controllers/dashboard/plugins/add.js index a05cac461b..8b1deb804d 100644 --- a/src/controllers/dashboard/plugins/add.js +++ b/src/controllers/dashboard/plugins/add.js @@ -1,43 +1,36 @@ -define(["jQuery", "loading", "libraryMenu", "globalize", "connectionManager", "emby-button"], function ($, loading, libraryMenu, globalize, connectionManager) { - "use strict"; +define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'connectionManager', 'emby-button'], function ($, loading, libraryMenu, globalize, connectionManager) { + 'use strict'; function populateHistory(packageInfo, page) { - var html = ""; + var html = ''; var length = Math.min(packageInfo.versions.length, 10); for (var i = 0; i < length; i++) { var version = packageInfo.versions[i]; - html += '

' + version.versionStr + " (" + version.classification + ")

"; - html += '
' + version.description + "
"; + html += '

' + version.version + '

'; + html += '
' + version.changelog + '
'; } - $("#revisionHistory", page).html(html); + $('#revisionHistory', page).html(html); } function populateVersions(packageInfo, page, installedPlugin) { - var html = ""; + var html = ''; for (var i = 0; i < packageInfo.versions.length; i++) { var version = packageInfo.versions[i]; - html += '"; + html += ''; } - var selectmenu = $("#selectVersion", page).html(html); + var selectmenu = $('#selectVersion', page).html(html); if (!installedPlugin) { - $("#pCurrentVersion", page).hide().html(""); + $('#pCurrentVersion', page).hide().html(''); } - var packageVersion = packageInfo.versions.filter(function (current) { - return "Release" == current.classification; - })[0]; - packageVersion = packageVersion || packageInfo.versions.filter(function (current) { - return "Beta" == current.classification; - })[0]; - + var packageVersion = packageInfo.versions[0]; if (packageVersion) { - var val = packageVersion.versionStr + "|" + packageVersion.classification; - selectmenu.val(val); + selectmenu.val(packageVersion.version); } } @@ -45,81 +38,60 @@ define(["jQuery", "loading", "libraryMenu", "globalize", "connectionManager", "e var installedPlugin = installedPlugins.filter(function (ip) { return ip.Name == pkg.name; })[0]; + populateVersions(pkg, page, installedPlugin); populateHistory(pkg, page); - $(".pluginName", page).html(pkg.name); - if ("Server" == pkg.targetSystem) { - $("#btnInstallDiv", page).removeClass("hide"); - $("#nonServerMsg", page).hide(); - $("#pSelectVersion", page).removeClass("hide"); + $('.pluginName', page).html(pkg.name); + $('#btnInstallDiv', page).removeClass('hide'); + $('#pSelectVersion', page).removeClass('hide'); + + if (pkg.overview) { + $('#overview', page).show().html(pkg.overview); } else { - $("#btnInstallDiv", page).addClass("hide"); - $("#pSelectVersion", page).addClass("hide"); - var msg = globalize.translate("MessageInstallPluginFromApp"); - $("#nonServerMsg", page).html(msg).show(); + $('#overview', page).hide(); } - if (pkg.shortDescription) { - $("#tagline", page).show().html(pkg.shortDescription); - } else { - $("#tagline", page).hide(); - } - - $("#overview", page).html(pkg.overview || ""); - $("#developer", page).html(pkg.owner); - - if (pkg.richDescUrl) { - $("#pViewWebsite", page).show(); - $("#pViewWebsite a", page).attr("href", pkg.richDescUrl); - } else { - $("#pViewWebsite", page).hide(); - } - - if (pkg.previewImage || pkg.thumbImage) { - var img = pkg.previewImage ? pkg.previewImage : pkg.thumbImage; - $("#pPreviewImage", page).show().html(""); - } else { - $("#pPreviewImage", page).hide().html(""); - } + $('#description', page).html(pkg.description); + $('#developer', page).html(pkg.owner); if (installedPlugin) { - var currentVersionText = globalize.translate("MessageYouHaveVersionInstalled", "" + installedPlugin.Version + ""); - $("#pCurrentVersion", page).show().html(currentVersionText); + var currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '' + installedPlugin.Version + ''); + $('#pCurrentVersion', page).show().html(currentVersionText); } else { - $("#pCurrentVersion", page).hide().html(""); + $('#pCurrentVersion', page).hide().html(''); } loading.hide(); } function alertText(options) { - require(["alert"], function (alert) { + require(['alert'], function (alert) { alert(options); }); } - function performInstallation(page, packageName, guid, updateClass, version) { - var developer = $("#developer", page).html().toLowerCase(); + function performInstallation(page, name, guid, version) { + var developer = $('#developer', page).html().toLowerCase(); var alertCallback = function () { loading.show(); - page.querySelector("#btnInstall").disabled = true; - ApiClient.installPlugin(packageName, guid, updateClass, version).then(function () { + page.querySelector('#btnInstall').disabled = true; + ApiClient.installPlugin(name, guid, version).then(function () { loading.hide(); - alertText(globalize.translate("PluginInstalledMessage")); + alertText(globalize.translate('PluginInstalledMessage')); }); }; if (developer !== 'jellyfin') { loading.hide(); - var msg = globalize.translate("MessagePluginInstallDisclaimer"); - msg += "
"; - msg += "
"; - msg += globalize.translate("PleaseConfirmPluginInstallation"); + var msg = globalize.translate('MessagePluginInstallDisclaimer'); + msg += '
'; + msg += '
'; + msg += globalize.translate('PleaseConfirmPluginInstallation'); - require(["confirm"], function (confirm) { - confirm(msg, globalize.translate("HeaderConfirmPluginInstallation")).then(function () { + require(['confirm'], function (confirm) { + confirm(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () { alertCallback(); }, function () { console.debug('plugin not installed'); @@ -131,33 +103,32 @@ define(["jQuery", "loading", "libraryMenu", "globalize", "connectionManager", "e } return function (view, params) { - $(".addPluginForm", view).on("submit", function () { + $('.addPluginForm', view).on('submit', function () { loading.show(); - var page = $(this).parents("#addPluginPage")[0]; + var page = $(this).parents('#addPluginPage')[0]; var name = params.name; var guid = params.guid; ApiClient.getInstalledPlugins().then(function (plugins) { var installedPlugin = plugins.filter(function (plugin) { return plugin.Name == name; })[0]; - var vals = $("#selectVersion", page).val().split("|"); - var version = vals[0]; + var version = $('#selectVersion', page).val(); if (installedPlugin) { if (installedPlugin.Version === version) { loading.hide(); Dashboard.alert({ - message: globalize.translate("MessageAlreadyInstalled"), - title: globalize.translate("HeaderPluginInstallation") + message: globalize.translate('MessageAlreadyInstalled'), + title: globalize.translate('HeaderPluginInstallation') }); } } else { - performInstallation(page, name, guid, vals[1], version); + performInstallation(page, name, guid, version); } }); return false; }); - view.addEventListener("viewshow", function () { + view.addEventListener('viewshow', function () { var page = this; loading.show(); var name = params.name; diff --git a/src/controllers/dashboard/plugins/available.js b/src/controllers/dashboard/plugins/available.js index adccfa3935..82fea00b58 100644 --- a/src/controllers/dashboard/plugins/available.js +++ b/src/controllers/dashboard/plugins/available.js @@ -1,5 +1,5 @@ -define(["loading", "libraryMenu", "globalize", "cardStyle", "emby-button", "emby-checkbox", "emby-select"], function (loading, libraryMenu, globalize) { - "use strict"; +define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby-checkbox', 'emby-select'], function (loading, libraryMenu, globalize) { + 'use strict'; function reloadList(page) { loading.show(); @@ -7,8 +7,8 @@ define(["loading", "libraryMenu", "globalize", "cardStyle", "emby-button", "emby var promise2 = ApiClient.getInstalledPlugins(); Promise.all([promise1, promise2]).then(function (responses) { populateList({ - catalogElement: page.querySelector("#pluginTiles"), - noItemsElement: page.querySelector("#noPlugins"), + catalogElement: page.querySelector('#pluginTiles'), + noItemsElement: page.querySelector('#noPlugins'), availablePlugins: responses[0], installedPlugins: responses[1] }); @@ -16,15 +16,15 @@ define(["loading", "libraryMenu", "globalize", "cardStyle", "emby-button", "emby } function getHeaderText(category) { - category = category.replace(" ", ""); - if ("Channel" === category) { - category = "Channels"; - } else if ("Theme" === category) { - category = "Themes"; - } else if ("LiveTV" === category) { - category = "HeaderLiveTV"; - } else if ("ScreenSaver" === category) { - category = "HeaderScreenSavers"; + category = category.replace(' ', ''); + if ('Channel' === category) { + category = 'Channels'; + } else if ('Theme' === category) { + category = 'Themes'; + } else if ('LiveTV' === category) { + category = 'HeaderLiveTV'; + } else if ('ScreenSaver' === category) { + category = 'HeaderScreenSavers'; } return globalize.translate(category); @@ -56,28 +56,28 @@ define(["loading", "libraryMenu", "globalize", "cardStyle", "emby-button", "emby }); var currentCategory = null; - var html = ""; + var html = ''; for (var i = 0; i < availablePlugins.length; i++) { var plugin = availablePlugins[i]; var category = plugin.categoryDisplayName; if (category != currentCategory) { if (currentCategory) { - html += ""; - html += ""; + html += ''; + html += ''; } html += '
'; - html += '

' + category + "

"; + html += '

' + category + '

'; html += '
'; currentCategory = category; } html += getPluginHtml(plugin, options, installedPlugins); } - html += "
"; - html += "
"; + html += ''; + html += ''; if (!availablePlugins.length && options.noItemsElement) { - options.noItemsElement.classList.remove("hide"); + options.noItemsElement.classList.remove('hide'); } options.catalogElement.innerHTML = html; @@ -85,51 +85,44 @@ define(["loading", "libraryMenu", "globalize", "cardStyle", "emby-button", "emby } function getPluginHtml(plugin, options, installedPlugins) { - var html = ""; - var href = plugin.externalUrl ? plugin.externalUrl : "addplugin.html?name=" + encodeURIComponent(plugin.name) + "&guid=" + plugin.guid; + var html = ''; + var href = plugin.externalUrl ? plugin.externalUrl : 'addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid; if (options.context) { - href += "&context=" + options.context; + href += '&context=' + options.context; } - var target = plugin.externalUrl ? ' target="_blank"' : ""; + var target = plugin.externalUrl ? ' target="_blank"' : ''; html += "
"; html += ''; html += '
'; html += "
"; html += plugin.name; - html += "
"; - var installedPlugin = plugin.isApp ? null : installedPlugins.filter(function (ip) { + html += '
'; + var installedPlugin = installedPlugins.filter(function (ip) { return ip.Id == plugin.guid; })[0]; html += "
"; - html += installedPlugin ? globalize.translate("LabelVersionInstalled", installedPlugin.Version) : " "; - html += "
"; - html += "
"; - html += ""; - return html += ""; + html += installedPlugin ? globalize.translate('LabelVersionInstalled', installedPlugin.Version) : ' '; + html += ''; + html += ''; + html += ''; + return html += ''; } function getTabs() { return [{ - href: "installedplugins.html", - name: globalize.translate("TabMyPlugins") + href: 'installedplugins.html', + name: globalize.translate('TabMyPlugins') }, { - href: "availableplugins.html", - name: globalize.translate("TabCatalog") + href: 'availableplugins.html', + name: globalize.translate('TabCatalog') }]; } @@ -138,8 +131,8 @@ define(["loading", "libraryMenu", "globalize", "cardStyle", "emby-button", "emby }; return function (view, params) { - view.addEventListener("viewshow", function () { - libraryMenu.setTabs("plugins", 1, getTabs); + view.addEventListener('viewshow', function () { + libraryMenu.setTabs('plugins', 1, getTabs); reloadList(this); }); }; diff --git a/src/controllers/dashboard/plugins/installed.js b/src/controllers/dashboard/plugins/installed.js index c381b2409e..87e9428cc6 100644 --- a/src/controllers/dashboard/plugins/installed.js +++ b/src/controllers/dashboard/plugins/installed.js @@ -1,15 +1,15 @@ -define(["loading", "libraryMenu", "dom", "globalize", "cardStyle", "emby-button"], function (loading, libraryMenu, dom, globalize) { - "use strict"; +define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'], function (loading, libraryMenu, dom, globalize) { + 'use strict'; function deletePlugin(page, uniqueid, name) { - var msg = globalize.translate("UninstallPluginConfirmation", name); + var msg = globalize.translate('UninstallPluginConfirmation', name); - require(["confirm"], function (confirm) { + require(['confirm'], function (confirm) { confirm({ - title: globalize.translate("UninstallPluginHeader"), + title: globalize.translate('UninstallPluginHeader'), text: msg, - primary: "delete", - confirmText: globalize.translate("UninstallPluginHeader") + primary: 'delete', + confirmText: globalize.translate('UninstallPluginHeader') }).then(function () { loading.show(); ApiClient.uninstallPlugin(uniqueid).then(function () { @@ -21,13 +21,13 @@ define(["loading", "libraryMenu", "dom", "globalize", "cardStyle", "emby-button" function showNoConfigurationMessage() { Dashboard.alert({ - message: globalize.translate("NoPluginConfigurationMessage") + message: globalize.translate('NoPluginConfigurationMessage') }); } function showConnectMessage() { Dashboard.alert({ - message: globalize.translate("MessagePluginConfigurationRequiresLocalAccess") + message: globalize.translate('MessagePluginConfigurationRequiresLocalAccess') }); } @@ -36,40 +36,33 @@ define(["loading", "libraryMenu", "dom", "globalize", "cardStyle", "emby-button" return pluginConfigurationPage.PluginId == plugin.Id; })[0]; var configPageUrl = configPage ? Dashboard.getConfigurationPageUrl(configPage.Name) : null; - var html = ""; + var html = ''; html += "
"; html += '
'; html += '"; + html += ''; + html += configPageUrl ? '' : '
'; + html += '
'; html += '
'; html += '
'; - html += ''; - html += "
"; + html += ''; + html += '
'; html += "
"; - html += configPage ? configPage.DisplayName || plugin.Name : plugin.Name; - html += "
"; + html += configPage && configPage.DisplayName ? configPage.DisplayName : plugin.Name; + html += ''; html += "
"; html += plugin.Version; - html += "
"; - html += ""; - html += ""; - html += ""; + html += ''; + html += ''; + html += ''; + html += ''; return html; } function renderPlugins(page, plugins) { - ApiClient.getJSON(ApiClient.getUrl("web/configurationpages") + "?pageType=PluginConfiguration").then(function (configPages) { + ApiClient.getJSON(ApiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration').then(function (configPages) { populateList(page, plugins, configPages); }); } @@ -85,22 +78,22 @@ define(["loading", "libraryMenu", "dom", "globalize", "cardStyle", "emby-button" var html = plugins.map(function (p) { return getPluginCardHtml(p, pluginConfigurationPages); - }).join(""); + }).join(''); - var installedPluginsElement = page.querySelector(".installedPlugins"); - installedPluginsElement.removeEventListener("click", onInstalledPluginsClick); - installedPluginsElement.addEventListener("click", onInstalledPluginsClick); + var installedPluginsElement = page.querySelector('.installedPlugins'); + installedPluginsElement.removeEventListener('click', onInstalledPluginsClick); + installedPluginsElement.addEventListener('click', onInstalledPluginsClick); if (plugins.length) { - installedPluginsElement.classList.add("itemsContainer"); - installedPluginsElement.classList.add("vertical-wrap"); + installedPluginsElement.classList.add('itemsContainer'); + installedPluginsElement.classList.add('vertical-wrap'); } else { html += '
'; - html += "

" + globalize.translate("MessageNoPluginsInstalled") + "

"; + html += '

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

'; html += '

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

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

'; + html += ''; } installedPluginsElement.innerHTML = html; @@ -108,36 +101,36 @@ define(["loading", "libraryMenu", "dom", "globalize", "cardStyle", "emby-button" } function showPluginMenu(page, elem) { - var card = dom.parentWithClass(elem, "card"); - var id = card.getAttribute("data-id"); - var name = card.getAttribute("data-name"); - var configHref = card.querySelector(".cardContent").getAttribute("href"); + var card = dom.parentWithClass(elem, 'card'); + var id = card.getAttribute('data-id'); + var name = card.getAttribute('data-name'); + var configHref = card.querySelector('.cardContent').getAttribute('href'); var menuItems = []; if (configHref) { menuItems.push({ - name: globalize.translate("ButtonSettings"), - id: "open", - icon: "mode_edit" + name: globalize.translate('ButtonSettings'), + id: 'open', + icon: 'mode_edit' }); } menuItems.push({ - name: globalize.translate("ButtonUninstall"), - id: "delete", - icon: "delete" + name: globalize.translate('ButtonUninstall'), + id: 'delete', + icon: 'delete' }); - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, positionTo: elem, callback: function (resultId) { switch (resultId) { - case "open": + case 'open': Dashboard.navigate(configHref); break; - case "delete": + case 'delete': deletePlugin(page, id, name); break; } @@ -155,29 +148,29 @@ define(["loading", "libraryMenu", "dom", "globalize", "cardStyle", "emby-button" function getTabs() { return [{ - href: "installedplugins.html", - name: globalize.translate("TabMyPlugins") + href: 'installedplugins.html', + name: globalize.translate('TabMyPlugins') }, { - href: "availableplugins.html", - name: globalize.translate("TabCatalog") + href: 'availableplugins.html', + name: globalize.translate('TabCatalog') }]; } function onInstalledPluginsClick(e) { - if (dom.parentWithClass(e.target, "noConfigPluginCard")) { + if (dom.parentWithClass(e.target, 'noConfigPluginCard')) { showNoConfigurationMessage(); - } else if (dom.parentWithClass(e.target, "connectModePluginCard")) { + } else if (dom.parentWithClass(e.target, 'connectModePluginCard')) { showConnectMessage(); } else { - var btnCardMenu = dom.parentWithClass(e.target, "btnCardMenu"); + var btnCardMenu = dom.parentWithClass(e.target, 'btnCardMenu'); if (btnCardMenu) { - showPluginMenu(dom.parentWithClass(btnCardMenu, "page"), btnCardMenu); + showPluginMenu(dom.parentWithClass(btnCardMenu, 'page'), btnCardMenu); } } } - pageIdOn("pageshow", "pluginsPage", function () { - libraryMenu.setTabs("plugins", 0, getTabs); + pageIdOn('pageshow', 'pluginsPage', function () { + libraryMenu.setTabs('plugins', 0, getTabs); reloadList(this); }); diff --git a/src/controllers/dashboard/scheduledtasks/scheduledtask.js b/src/controllers/dashboard/scheduledtasks/scheduledtask.js index 8a3cdf5d69..52050d1f80 100644 --- a/src/controllers/dashboard/scheduledtasks/scheduledtask.js +++ b/src/controllers/dashboard/scheduledtasks/scheduledtask.js @@ -1,5 +1,5 @@ -define(["jQuery", "loading", "datetime", "dom", "globalize", "emby-input", "emby-button", "emby-select"], function ($, loading, datetime, dom, globalize) { - "use strict"; +define(['jQuery', 'loading', 'datetime', 'dom', 'globalize', 'emby-input', 'emby-button', 'emby-select'], function ($, loading, datetime, dom, globalize) { + 'use strict'; function fillTimeOfDay(select) { @@ -14,7 +14,7 @@ define(["jQuery", "loading", "datetime", "dom", "globalize", "emby-input", "emby select.innerHTML = options.map(function (o) { return ''; - }).join(""); + }).join(''); } Array.prototype.remove = function (from, to) { @@ -26,92 +26,92 @@ define(["jQuery", "loading", "datetime", "dom", "globalize", "emby-input", "emby var ScheduledTaskPage = { refreshScheduledTask: function (view) { loading.show(); - var id = getParameterByName("id"); + var id = getParameterByName('id'); ApiClient.getScheduledTask(id).then(function (task) { ScheduledTaskPage.loadScheduledTask(view, task); }); }, loadScheduledTask: function (view, task) { - $(".taskName", view).html(task.Name); - $("#pTaskDescription", view).html(task.Description); + $('.taskName', view).html(task.Name); + $('#pTaskDescription', view).html(task.Description); - require(["listViewStyle"], function () { + require(['listViewStyle'], function () { ScheduledTaskPage.loadTaskTriggers(view, task); }); loading.hide(); }, loadTaskTriggers: function (context, task) { - var html = ""; + var html = ''; html += '
'; for (var i = 0, length = task.Triggers.length; i < length; i++) { var trigger = task.Triggers[i]; html += '
'; - html += 'schedule'; + html += ''; if (trigger.MaxRuntimeMs) { html += '
'; } else { html += '
'; } - html += "
" + ScheduledTaskPage.getTriggerFriendlyName(trigger) + "
"; + html += "
" + ScheduledTaskPage.getTriggerFriendlyName(trigger) + '
'; if (trigger.MaxRuntimeMs) { html += '
'; var hours = trigger.MaxRuntimeTicks / 36e9; if (hours == 1) { - html += globalize.translate("ValueTimeLimitSingleHour"); + html += globalize.translate('ValueTimeLimitSingleHour'); } else { - html += globalize.translate("ValueTimeLimitMultiHour", hours); + html += globalize.translate('ValueTimeLimitMultiHour', hours); } - html += "
"; + html += '
'; } - html += "
"; - html += ''; - html += "
"; + html += '
'; + html += ''; + html += ''; } - html += ""; - context.querySelector(".taskTriggers").innerHTML = html; + html += ''; + context.querySelector('.taskTriggers').innerHTML = html; }, // TODO: Replace this mess with date-fns and remove datetime completely getTriggerFriendlyName: function (trigger) { - if ("DailyTrigger" == trigger.Type) { - return globalize.translate("DailyAt", ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks)); + if ('DailyTrigger' == trigger.Type) { + return globalize.translate('DailyAt', ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks)); } - if ("WeeklyTrigger" == trigger.Type) { + if ('WeeklyTrigger' == trigger.Type) { // TODO: The day of week isn't localised as well - return globalize.translate("WeeklyAt", trigger.DayOfWeek, ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks)); + return globalize.translate('WeeklyAt', trigger.DayOfWeek, ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks)); } - if ("SystemEventTrigger" == trigger.Type && "WakeFromSleep" == trigger.SystemEvent) { - return globalize.translate("OnWakeFromSleep"); + if ('SystemEventTrigger' == trigger.Type && 'WakeFromSleep' == trigger.SystemEvent) { + return globalize.translate('OnWakeFromSleep'); } - if (trigger.Type == "IntervalTrigger") { + if (trigger.Type == 'IntervalTrigger') { var hours = trigger.IntervalTicks / 36e9; if (hours == 0.25) { - return globalize.translate("EveryXMinutes", "15"); + return globalize.translate('EveryXMinutes', '15'); } if (hours == 0.5) { - return globalize.translate("EveryXMinutes", "30"); + return globalize.translate('EveryXMinutes', '30'); } if (hours == 0.75) { - return globalize.translate("EveryXMinutes", "45"); + return globalize.translate('EveryXMinutes', '45'); } if (hours == 1) { - return globalize.translate("EveryHour"); + return globalize.translate('EveryHour'); } - return globalize.translate("EveryXHours", hours); + return globalize.translate('EveryXHours', hours); } - if (trigger.Type == "StartupTrigger") { - return globalize.translate("OnApplicationStartup"); + if (trigger.Type == 'StartupTrigger') { + return globalize.translate('OnApplicationStartup'); } return trigger.Type; @@ -124,20 +124,20 @@ define(["jQuery", "loading", "datetime", "dom", "globalize", "emby-input", "emby return datetime.getDisplayTime(now); }, showAddTriggerPopup: function (view) { - $("#selectTriggerType", view).val("DailyTrigger"); - view.querySelector("#selectTriggerType").dispatchEvent(new CustomEvent("change", {})); - $("#popupAddTrigger", view).removeClass("hide"); + $('#selectTriggerType', view).val('DailyTrigger'); + view.querySelector('#selectTriggerType').dispatchEvent(new CustomEvent('change', {})); + $('#popupAddTrigger', view).removeClass('hide'); }, confirmDeleteTrigger: function (view, index) { - require(["confirm"], function (confirm) { - confirm(globalize.translate("MessageDeleteTaskTrigger"), globalize.translate("HeaderDeleteTaskTrigger")).then(function () { + require(['confirm'], function (confirm) { + confirm(globalize.translate('MessageDeleteTaskTrigger'), globalize.translate('HeaderDeleteTaskTrigger')).then(function () { ScheduledTaskPage.deleteTrigger(view, index); }); }); }, deleteTrigger: function (view, index) { loading.show(); - var id = getParameterByName("id"); + var id = getParameterByName('id'); ApiClient.getScheduledTask(id).then(function (task) { task.Triggers.remove(index); ApiClient.updateScheduledTaskTriggers(task.Id, task.Triggers).then(function () { @@ -146,55 +146,55 @@ define(["jQuery", "loading", "datetime", "dom", "globalize", "emby-input", "emby }); }, refreshTriggerFields: function (page, triggerType) { - if (triggerType == "DailyTrigger") { - $("#fldTimeOfDay", page).show(); - $("#fldDayOfWeek", page).hide(); - $("#fldSelectSystemEvent", page).hide(); - $("#fldSelectInterval", page).hide(); - $("#selectTimeOfDay", page).attr("required", "required"); - } else if (triggerType == "WeeklyTrigger") { - $("#fldTimeOfDay", page).show(); - $("#fldDayOfWeek", page).show(); - $("#fldSelectSystemEvent", page).hide(); - $("#fldSelectInterval", page).hide(); - $("#selectTimeOfDay", page).attr("required", "required"); - } else if (triggerType == "SystemEventTrigger") { - $("#fldTimeOfDay", page).hide(); - $("#fldDayOfWeek", page).hide(); - $("#fldSelectSystemEvent", page).show(); - $("#fldSelectInterval", page).hide(); - $("#selectTimeOfDay", page).removeAttr("required"); - } else if (triggerType == "IntervalTrigger") { - $("#fldTimeOfDay", page).hide(); - $("#fldDayOfWeek", page).hide(); - $("#fldSelectSystemEvent", page).hide(); - $("#fldSelectInterval", page).show(); - $("#selectTimeOfDay", page).removeAttr("required"); - } else if (triggerType == "StartupTrigger") { - $("#fldTimeOfDay", page).hide(); - $("#fldDayOfWeek", page).hide(); - $("#fldSelectSystemEvent", page).hide(); - $("#fldSelectInterval", page).hide(); - $("#selectTimeOfDay", page).removeAttr("required"); + if (triggerType == 'DailyTrigger') { + $('#fldTimeOfDay', page).show(); + $('#fldDayOfWeek', page).hide(); + $('#fldSelectSystemEvent', page).hide(); + $('#fldSelectInterval', page).hide(); + $('#selectTimeOfDay', page).attr('required', 'required'); + } else if (triggerType == 'WeeklyTrigger') { + $('#fldTimeOfDay', page).show(); + $('#fldDayOfWeek', page).show(); + $('#fldSelectSystemEvent', page).hide(); + $('#fldSelectInterval', page).hide(); + $('#selectTimeOfDay', page).attr('required', 'required'); + } else if (triggerType == 'SystemEventTrigger') { + $('#fldTimeOfDay', page).hide(); + $('#fldDayOfWeek', page).hide(); + $('#fldSelectSystemEvent', page).show(); + $('#fldSelectInterval', page).hide(); + $('#selectTimeOfDay', page).removeAttr('required'); + } else if (triggerType == 'IntervalTrigger') { + $('#fldTimeOfDay', page).hide(); + $('#fldDayOfWeek', page).hide(); + $('#fldSelectSystemEvent', page).hide(); + $('#fldSelectInterval', page).show(); + $('#selectTimeOfDay', page).removeAttr('required'); + } else if (triggerType == 'StartupTrigger') { + $('#fldTimeOfDay', page).hide(); + $('#fldDayOfWeek', page).hide(); + $('#fldSelectSystemEvent', page).hide(); + $('#fldSelectInterval', page).hide(); + $('#selectTimeOfDay', page).removeAttr('required'); } }, getTriggerToAdd: function (page) { var trigger = { - Type: $("#selectTriggerType", page).val() + Type: $('#selectTriggerType', page).val() }; - if (trigger.Type == "DailyTrigger") { - trigger.TimeOfDayTicks = $("#selectTimeOfDay", page).val(); - } else if (trigger.Type == "WeeklyTrigger") { - trigger.DayOfWeek = $("#selectDayOfWeek", page).val(); - trigger.TimeOfDayTicks = $("#selectTimeOfDay", page).val(); - } else if (trigger.Type == "SystemEventTrigger") { - trigger.SystemEvent = $("#selectSystemEvent", page).val(); - } else if (trigger.Type == "IntervalTrigger") { - trigger.IntervalTicks = $("#selectInterval", page).val(); + if (trigger.Type == 'DailyTrigger') { + trigger.TimeOfDayTicks = $('#selectTimeOfDay', page).val(); + } else if (trigger.Type == 'WeeklyTrigger') { + trigger.DayOfWeek = $('#selectDayOfWeek', page).val(); + trigger.TimeOfDayTicks = $('#selectTimeOfDay', page).val(); + } else if (trigger.Type == 'SystemEventTrigger') { + trigger.SystemEvent = $('#selectSystemEvent', page).val(); + } else if (trigger.Type == 'IntervalTrigger') { + trigger.IntervalTicks = $('#selectInterval', page).val(); } - var timeLimit = $("#txtTimeLimit", page).val() || "0"; + var timeLimit = $('#txtTimeLimit', page).val() || '0'; timeLimit = parseFloat(timeLimit) * 3600000; trigger.MaxRuntimeMs = timeLimit || null; @@ -205,34 +205,34 @@ define(["jQuery", "loading", "datetime", "dom", "globalize", "emby-input", "emby return function (view, params) { function onSubmit(e) { loading.show(); - var id = getParameterByName("id"); + var id = getParameterByName('id'); ApiClient.getScheduledTask(id).then(function (task) { task.Triggers.push(ScheduledTaskPage.getTriggerToAdd(view)); ApiClient.updateScheduledTaskTriggers(task.Id, task.Triggers).then(function () { - $("#popupAddTrigger").addClass("hide"); + $('#popupAddTrigger').addClass('hide'); ScheduledTaskPage.refreshScheduledTask(view); }); }); e.preventDefault(); } - view.querySelector(".addTriggerForm").addEventListener("submit", onSubmit); - fillTimeOfDay(view.querySelector("#selectTimeOfDay")); - $(view.querySelector("#popupAddTrigger").parentNode).trigger("create"); - view.querySelector(".selectTriggerType").addEventListener("change", function () { + view.querySelector('.addTriggerForm').addEventListener('submit', onSubmit); + fillTimeOfDay(view.querySelector('#selectTimeOfDay')); + $(view.querySelector('#popupAddTrigger').parentNode).trigger('create'); + view.querySelector('.selectTriggerType').addEventListener('change', function () { ScheduledTaskPage.refreshTriggerFields(view, this.value); }); - view.querySelector(".btnAddTrigger").addEventListener("click", function () { + view.querySelector('.btnAddTrigger').addEventListener('click', function () { ScheduledTaskPage.showAddTriggerPopup(view); }); - view.addEventListener("click", function (e) { - var btnDeleteTrigger = dom.parentWithClass(e.target, "btnDeleteTrigger"); + view.addEventListener('click', function (e) { + var btnDeleteTrigger = dom.parentWithClass(e.target, 'btnDeleteTrigger'); if (btnDeleteTrigger) { - ScheduledTaskPage.confirmDeleteTrigger(view, parseInt(btnDeleteTrigger.getAttribute("data-index"))); + ScheduledTaskPage.confirmDeleteTrigger(view, parseInt(btnDeleteTrigger.getAttribute('data-index'))); } }); - view.addEventListener("viewshow", function () { + view.addEventListener('viewshow', function () { ScheduledTaskPage.refreshScheduledTask(view); }); }; diff --git a/src/controllers/dashboard/scheduledtasks/scheduledtasks.js b/src/controllers/dashboard/scheduledtasks/scheduledtasks.js index b9c7ba413c..5ce53cf6fe 100644 --- a/src/controllers/dashboard/scheduledtasks/scheduledtasks.js +++ b/src/controllers/dashboard/scheduledtasks/scheduledtasks.js @@ -1,5 +1,5 @@ -define(["jQuery", "loading", "events", "globalize", "serverNotifications", "date-fns", "dfnshelper", "listViewStyle", "emby-button"], function ($, loading, events, globalize, serverNotifications, datefns, dfnshelper) { - "use strict"; +define(['jQuery', 'loading', 'events', 'globalize', 'serverNotifications', 'date-fns', 'dfnshelper', 'listViewStyle', 'emby-button'], function ($, loading, events, globalize, serverNotifications, datefns, dfnshelper) { + 'use strict'; function reloadList(page) { ApiClient.getScheduledTasks({ @@ -12,114 +12,114 @@ define(["jQuery", "loading", "events", "globalize", "serverNotifications", "date function populateList(page, tasks) { tasks = tasks.sort(function(a, b) { - a = a.Category + " " + a.Name; - b = b.Category + " " + b.Name; + a = a.Category + ' ' + a.Name; + b = b.Category + ' ' + b.Name; return a == b ? 0 : a < b ? -1 : 1; }); var currentCategory; - var html = ""; + var html = ''; for (var i = 0; i < tasks.length; i++) { var task = tasks[i]; if (task.Category != currentCategory) { currentCategory = task.Category; if (currentCategory) { - html += ""; - html += ""; + html += ''; + html += ''; } html += '
'; html += '
'; html += '

'; html += currentCategory; - html += "

"; + html += ''; if (i === 0) { - html += '' + globalize.translate("Help") + ""; + html += '' + globalize.translate('Help') + ''; } - html += "
"; + html += '
'; html += '
'; } html += '
'; html += ""; - html += 'schedule'; - html += ""; + html += ''; + html += ''; html += '"; - if (task.State === "Running") { - html += ''; - } else if (task.State === "Idle") { - html += ''; + html += "

" + task.Name + '

'; + html += "
" + getTaskProgressHtml(task) + '
'; + html += ''; + html += '
'; + if (task.State === 'Running') { + html += ''; + } else if (task.State === 'Idle') { + html += ''; } - html += "
"; + html += ''; } if (tasks.length) { - html += ""; - html += ""; + html += ''; + html += ''; } - page.querySelector(".divScheduledTasks").innerHTML = html; + page.querySelector('.divScheduledTasks').innerHTML = html; } function getTaskProgressHtml(task) { - var html = ""; - if (task.State === "Idle") { + var html = ''; + if (task.State === 'Idle') { if (task.LastExecutionResult) { var endtime = Date.parse(task.LastExecutionResult.EndTimeUtc); var starttime = Date.parse(task.LastExecutionResult.StartTimeUtc); - html += globalize.translate("LabelScheduledTaskLastRan", datefns.formatDistanceToNow(endtime, dfnshelper.localeWithSuffix), + html += globalize.translate('LabelScheduledTaskLastRan', datefns.formatDistanceToNow(endtime, dfnshelper.localeWithSuffix), datefns.formatDistance(starttime, endtime, { locale: dfnshelper.getLocale() })); - if (task.LastExecutionResult.Status === "Failed") { - html += " (" + globalize.translate("LabelFailed") + ")"; - } else if (task.LastExecutionResult.Status === "Cancelled") { - html += " (" + globalize.translate("LabelCancelled") + ")"; - } else if (task.LastExecutionResult.Status === "Aborted") { - html += " " + globalize.translate("LabelAbortedByServerShutdown") + ""; + if (task.LastExecutionResult.Status === 'Failed') { + html += " (" + globalize.translate('LabelFailed') + ')'; + } else if (task.LastExecutionResult.Status === 'Cancelled') { + html += " (" + globalize.translate('LabelCancelled') + ')'; + } else if (task.LastExecutionResult.Status === 'Aborted') { + html += " " + globalize.translate('LabelAbortedByServerShutdown') + ''; } } - } else if (task.State === "Running") { + } else if (task.State === 'Running') { var progress = (task.CurrentProgressPercentage || 0).toFixed(1); html += '
'; html += '
'; html += '
'; - html += "
"; - html += "
"; - html += "" + progress + "%"; - html += "
"; + html += ''; + html += ''; + html += "" + progress + '%'; + html += ''; } else { - html += "" + globalize.translate("LabelStopping") + ""; + html += "" + globalize.translate('LabelStopping') + ''; } return html; } function setTaskButtonIcon(button, icon) { - var inner = button.querySelector("i"); - inner.classList.remove("stop", "play_arrow"); + var inner = button.querySelector('.material-icons'); + inner.classList.remove('stop', 'play_arrow'); inner.classList.add(icon); } function updateTaskButton(elem, state) { - if (state === "Running") { - elem.classList.remove("btnStartTask"); - elem.classList.add("btnStopTask"); - setTaskButtonIcon(elem, "stop"); - elem.title = globalize.translate("ButtonStop"); - } else if (state === "Idle") { - elem.classList.add("btnStartTask"); - elem.classList.remove("btnStopTask"); - setTaskButtonIcon(elem, "play_arrow"); - elem.title = globalize.translate("ButtonStart"); + if (state === 'Running') { + elem.classList.remove('btnStartTask'); + elem.classList.add('btnStopTask'); + setTaskButtonIcon(elem, 'stop'); + elem.title = globalize.translate('ButtonStop'); + } else if (state === 'Idle') { + elem.classList.add('btnStartTask'); + elem.classList.remove('btnStopTask'); + setTaskButtonIcon(elem, 'play_arrow'); + elem.title = globalize.translate('ButtonStart'); } - $(elem).parents(".listItem")[0].setAttribute("data-status", state); + $(elem).parents('.listItem')[0].setAttribute('data-status', state); } return function(view, params) { function updateTasks(tasks) { for (var i = 0; i < tasks.length; i++) { var task = tasks[i]; - view.querySelector("#taskProgress" + task.Id).innerHTML = getTaskProgressHtml(task); - updateTaskButton(view.querySelector("#btnTask" + task.Id), task.State); + view.querySelector('#taskProgress' + task.Id).innerHTML = getTaskProgressHtml(task); + updateTaskButton(view.querySelector('#btnTask' + task.Id), task.State); } } @@ -136,47 +136,47 @@ define(["jQuery", "loading", "events", "globalize", "serverNotifications", "date } function startInterval() { - ApiClient.sendMessage("ScheduledTasksInfoStart", "1000,1000"); + ApiClient.sendMessage('ScheduledTasksInfoStart', '1000,1000'); pollInterval && clearInterval(pollInterval); pollInterval = setInterval(onPollIntervalFired, 1e4); } function stopInterval() { - ApiClient.sendMessage("ScheduledTasksInfoStop"); + ApiClient.sendMessage('ScheduledTasksInfoStop'); pollInterval && clearInterval(pollInterval); } var pollInterval; var serverId = ApiClient.serverId(); - $(".divScheduledTasks", view).on("click", ".btnStartTask", function() { + $('.divScheduledTasks', view).on('click', '.btnStartTask', function() { var button = this; - var id = button.getAttribute("data-taskid"); + var id = button.getAttribute('data-taskid'); ApiClient.startScheduledTask(id).then(function() { - updateTaskButton(button, "Running"); + updateTaskButton(button, 'Running'); reloadList(view); }); }); - $(".divScheduledTasks", view).on("click", ".btnStopTask", function() { + $('.divScheduledTasks', view).on('click', '.btnStopTask', function() { var button = this; - var id = button.getAttribute("data-taskid"); + var id = button.getAttribute('data-taskid'); ApiClient.stopScheduledTask(id).then(function() { - updateTaskButton(button, ""); + updateTaskButton(button, ''); reloadList(view); }); }); - view.addEventListener("viewbeforehide", function() { - events.off(serverNotifications, "ScheduledTasksInfo", onScheduledTasksUpdate); + view.addEventListener('viewbeforehide', function() { + events.off(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); stopInterval(); }); - view.addEventListener("viewshow", function() { + view.addEventListener('viewshow', function() { loading.show(); startInterval(); reloadList(view); - events.on(serverNotifications, "ScheduledTasksInfo", onScheduledTasksUpdate); + events.on(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); }); }; }); diff --git a/src/controllers/dashboard/serveractivity.js b/src/controllers/dashboard/serveractivity.js new file mode 100644 index 0000000000..c48a2903ae --- /dev/null +++ b/src/controllers/dashboard/serveractivity.js @@ -0,0 +1,31 @@ +define(['components/activitylog', 'globalize'], function (ActivityLog, globalize) { + 'use strict'; + + return function (view, params) { + var activityLog; + + if (params.useractivity !== 'false') { + view.querySelector('.activityItems').setAttribute('data-useractivity', 'true'); + view.querySelector('.sectionTitle').innerHTML = globalize.translate('HeaderActivity'); + } else { + view.querySelector('.activityItems').setAttribute('data-useractivity', 'false'); + view.querySelector('.sectionTitle').innerHTML = globalize.translate('Alerts'); + } + + view.addEventListener('viewshow', function () { + if (!activityLog) { + activityLog = new ActivityLog({ + serverId: ApiClient.serverId(), + element: view.querySelector('.activityItems') + }); + } + }); + view.addEventListener('viewdestroy', function () { + if (activityLog) { + activityLog.destroy(); + } + + activityLog = null; + }); + }; +}); diff --git a/src/controllers/dashboard/streaming.js b/src/controllers/dashboard/streaming.js new file mode 100644 index 0000000000..37afe5a054 --- /dev/null +++ b/src/controllers/dashboard/streaming.js @@ -0,0 +1,43 @@ +define(['jQuery', 'libraryMenu', 'loading', 'globalize'], function ($, libraryMenu, loading, globalize) { + 'use strict'; + + function loadPage(page, config) { + $('#txtRemoteClientBitrateLimit', page).val(config.RemoteClientBitrateLimit / 1e6 || ''); + loading.hide(); + } + + function onSubmit() { + loading.show(); + var form = this; + ApiClient.getServerConfiguration().then(function (config) { + config.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', form).val() || '0')); + ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult); + }); + + return false; + } + + function getTabs() { + return [{ + href: 'encodingsettings.html', + name: globalize.translate('Transcoding') + }, { + href: 'playbackconfiguration.html', + name: globalize.translate('TabResumeSettings') + }, { + href: 'streamingsettings.html', + name: globalize.translate('TabStreaming') + }]; + } + + $(document).on('pageinit', '#streamingSettingsPage', function () { + $('.streamingSettingsForm').off('submit', onSubmit).on('submit', onSubmit); + }).on('pageshow', '#streamingSettingsPage', function () { + loading.show(); + libraryMenu.setTabs('playback', 2, getTabs); + var page = this; + ApiClient.getServerConfiguration().then(function (config) { + loadPage(page, config); + }); + }); +}); diff --git a/src/controllers/dashboard/users/useredit.js b/src/controllers/dashboard/users/useredit.js new file mode 100644 index 0000000000..c71c81d9e0 --- /dev/null +++ b/src/controllers/dashboard/users/useredit.js @@ -0,0 +1,200 @@ +define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, libraryMenu, globalize) { + 'use strict'; + + function loadDeleteFolders(page, user, mediaFolders) { + ApiClient.getJSON(ApiClient.getUrl('Channels', { + SupportsMediaDeletion: true + })).then(function (channelsResult) { + var i; + var length; + var folder; + var isChecked; + var checkedAttribute; + var html = ''; + + for (i = 0, length = mediaFolders.length; i < length; i++) { + folder = mediaFolders[i]; + isChecked = user.Policy.EnableContentDeletion || -1 != user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id); + checkedAttribute = isChecked ? ' checked="checked"' : ''; + html += ''; + } + + for (i = 0, length = channelsResult.Items.length; i < length; i++) { + folder = channelsResult.Items[i]; + isChecked = user.Policy.EnableContentDeletion || -1 != user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id); + checkedAttribute = isChecked ? ' checked="checked"' : ''; + html += ''; + } + + $('.deleteAccess', page).html(html).trigger('create'); + $('#chkEnableDeleteAllFolders', page).checked = user.Policy.EnableContentDeletion; + }); + } + + function loadAuthProviders(page, user, providers) { + if (providers.length > 1) { + page.querySelector('.fldSelectLoginProvider').classList.remove('hide'); + } else { + page.querySelector('.fldSelectLoginProvider').classList.add('hide'); + } + + var currentProviderId = user.Policy.AuthenticationProviderId; + page.querySelector('.selectLoginProvider').innerHTML = providers.map(function (provider) { + var selected = provider.Id === currentProviderId || providers.length < 2 ? ' selected' : ''; + return ''; + }); + } + + function loadPasswordResetProviders(page, user, providers) { + if (providers.length > 1) { + page.querySelector('.fldSelectPasswordResetProvider').classList.remove('hide'); + } else { + page.querySelector('.fldSelectPasswordResetProvider').classList.add('hide'); + } + + var currentProviderId = user.Policy.PasswordResetProviderId; + page.querySelector('.selectPasswordResetProvider').innerHTML = providers.map(function (provider) { + var selected = provider.Id === currentProviderId || providers.length < 2 ? ' selected' : ''; + return ''; + }); + } + + function loadUser(page, user) { + currentUser = user; + ApiClient.getJSON(ApiClient.getUrl('Auth/Providers')).then(function (providers) { + loadAuthProviders(page, user, providers); + }); + ApiClient.getJSON(ApiClient.getUrl('Auth/PasswordResetProviders')).then(function (providers) { + loadPasswordResetProviders(page, user, providers); + }); + ApiClient.getJSON(ApiClient.getUrl('Library/MediaFolders', { + IsHidden: false + })).then(function (folders) { + loadDeleteFolders(page, user, folders.Items); + }); + + if (user.Policy.IsDisabled) { + $('.disabledUserBanner', page).show(); + } else { + $('.disabledUserBanner', page).hide(); + } + + $('#txtUserName', page).prop('disabled', '').removeAttr('disabled'); + $('#fldConnectInfo', page).show(); + $('.lnkEditUserPreferences', page).attr('href', 'mypreferencesmenu.html?userId=' + user.Id); + libraryMenu.setTitle(user.Name); + page.querySelector('.username').innerHTML = user.Name; + $('#txtUserName', page).val(user.Name); + $('#chkIsAdmin', page).checked = user.Policy.IsAdministrator; + $('#chkDisabled', page).checked = user.Policy.IsDisabled; + $('#chkIsHidden', page).checked = user.Policy.IsHidden; + $('#chkRemoteControlSharedDevices', page).checked = user.Policy.EnableSharedDeviceControl; + $('#chkEnableRemoteControlOtherUsers', page).checked = user.Policy.EnableRemoteControlOfOtherUsers; + $('#chkEnableDownloading', page).checked = user.Policy.EnableContentDownloading; + $('#chkManageLiveTv', page).checked = user.Policy.EnableLiveTvManagement; + $('#chkEnableLiveTvAccess', page).checked = user.Policy.EnableLiveTvAccess; + $('#chkEnableMediaPlayback', page).checked = user.Policy.EnableMediaPlayback; + $('#chkEnableAudioPlaybackTranscoding', page).checked = user.Policy.EnableAudioPlaybackTranscoding; + $('#chkEnableVideoPlaybackTranscoding', page).checked = user.Policy.EnableVideoPlaybackTranscoding; + $('#chkEnableVideoPlaybackRemuxing', page).checked = user.Policy.EnablePlaybackRemuxing; + $('#chkForceRemoteSourceTranscoding', page).checked = user.Policy.ForceRemoteSourceTranscoding; + $('#chkRemoteAccess', page).checked = null == user.Policy.EnableRemoteAccess || user.Policy.EnableRemoteAccess; + $('#chkEnableSyncTranscoding', page).checked = user.Policy.EnableSyncTranscoding; + $('#chkEnableConversion', page).checked = user.Policy.EnableMediaConversion || false; + $('#chkEnableSharing', page).checked = user.Policy.EnablePublicSharing; + $('#txtRemoteClientBitrateLimit', page).val(user.Policy.RemoteClientBitrateLimit / 1e6 || ''); + $('#txtLoginAttemptsBeforeLockout', page).val(user.Policy.LoginAttemptsBeforeLockout || '0'); + $('#selectSyncPlayAccess').val(user.Policy.SyncPlayAccess); + loading.hide(); + } + + function onSaveComplete(page, user) { + Dashboard.navigate('userprofiles.html'); + loading.hide(); + + require(['toast'], function (toast) { + toast(globalize.translate('SettingsSaved')); + }); + } + + function saveUser(user, page) { + user.Name = $('#txtUserName', page).val(); + user.Policy.IsAdministrator = $('#chkIsAdmin', page).checked; + user.Policy.IsHidden = $('#chkIsHidden', page).checked; + user.Policy.IsDisabled = $('#chkDisabled', page).checked; + user.Policy.EnableRemoteControlOfOtherUsers = $('#chkEnableRemoteControlOtherUsers', page).checked; + user.Policy.EnableLiveTvManagement = $('#chkManageLiveTv', page).checked; + user.Policy.EnableLiveTvAccess = $('#chkEnableLiveTvAccess', page).checked; + user.Policy.EnableSharedDeviceControl = $('#chkRemoteControlSharedDevices', page).checked; + user.Policy.EnableMediaPlayback = $('#chkEnableMediaPlayback', page).checked; + user.Policy.EnableAudioPlaybackTranscoding = $('#chkEnableAudioPlaybackTranscoding', page).checked; + user.Policy.EnableVideoPlaybackTranscoding = $('#chkEnableVideoPlaybackTranscoding', page).checked; + user.Policy.EnablePlaybackRemuxing = $('#chkEnableVideoPlaybackRemuxing', page).checked; + user.Policy.ForceRemoteSourceTranscoding = $('#chkForceRemoteSourceTranscoding', page).checked; + user.Policy.EnableContentDownloading = $('#chkEnableDownloading', page).checked; + user.Policy.EnableSyncTranscoding = $('#chkEnableSyncTranscoding', page).checked; + user.Policy.EnableMediaConversion = $('#chkEnableConversion', page).checked; + user.Policy.EnablePublicSharing = $('#chkEnableSharing', page).checked; + user.Policy.EnableRemoteAccess = $('#chkRemoteAccess', page).checked; + user.Policy.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', page).val() || '0')); + user.Policy.LoginAttemptsBeforeLockout = parseInt($('#txtLoginAttemptsBeforeLockout', page).val() || '0'); + user.Policy.AuthenticationProviderId = page.querySelector('.selectLoginProvider').value; + user.Policy.PasswordResetProviderId = page.querySelector('.selectPasswordResetProvider').value; + user.Policy.EnableContentDeletion = $('#chkEnableDeleteAllFolders', page).checked; + user.Policy.EnableContentDeletionFromFolders = user.Policy.EnableContentDeletion ? [] : $('.chkFolder', page).get().filter(function (c) { + return c.checked; + }).map(function (c) { + return c.getAttribute('data-id'); + }); + user.Policy.SyncPlayAccess = page.querySelector('#selectSyncPlayAccess').value; + ApiClient.updateUser(user).then(function () { + ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () { + onSaveComplete(page, user); + }); + }); + } + + function onSubmit() { + var page = $(this).parents('.page')[0]; + loading.show(); + getUser().then(function (result) { + saveUser(result, page); + }); + return false; + } + + function getUser() { + var userId = getParameterByName('userId'); + return ApiClient.getUser(userId); + } + + function loadData(page) { + loading.show(); + getUser().then(function (user) { + loadUser(page, user); + }); + } + + var currentUser; + $(document).on('pageinit', '#editUserPage', function () { + $('.editUserProfileForm').off('submit', onSubmit).on('submit', onSubmit); + this.querySelector('.sharingHelp').innerHTML = globalize.translate('OptionAllowLinkSharingHelp', 30); + var page = this; + $('#chkEnableDeleteAllFolders', this).on('change', function () { + if (this.checked) { + $('.deleteAccess', page).hide(); + } else { + $('.deleteAccess', page).show(); + } + }); + ApiClient.getServerConfiguration().then(function (config) { + if (config.EnableRemoteAccess) { + page.querySelector('.fldRemoteAccess').classList.remove('hide'); + } else { + page.querySelector('.fldRemoteAccess').classList.add('hide'); + } + }); + }).on('pagebeforeshow', '#editUserPage', function () { + loadData(this); + }); +}); diff --git a/src/controllers/userlibraryaccess.js b/src/controllers/dashboard/users/userlibraryaccess.js similarity index 53% rename from src/controllers/userlibraryaccess.js rename to src/controllers/dashboard/users/userlibraryaccess.js index 38418f5190..1fdb6cb599 100644 --- a/src/controllers/userlibraryaccess.js +++ b/src/controllers/dashboard/users/userlibraryaccess.js @@ -1,79 +1,79 @@ -define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, libraryMenu) { - "use strict"; +define(['jQuery', 'loading', 'libraryMenu', 'globalize'], function ($, loading, libraryMenu, globalize) { + 'use strict'; function triggerChange(select) { - var evt = document.createEvent("HTMLEvents"); - evt.initEvent("change", false, true); + var evt = document.createEvent('HTMLEvents'); + evt.initEvent('change', false, true); select.dispatchEvent(evt); } function loadMediaFolders(page, user, mediaFolders) { - var html = ""; - html += '

' + Globalize.translate("HeaderLibraries") + "

"; + var html = ''; + html += '

' + globalize.translate('HeaderLibraries') + '

'; html += '
'; for (var i = 0, length = mediaFolders.length; i < length; i++) { var folder = mediaFolders[i]; var isChecked = user.Policy.EnableAllFolders || -1 != user.Policy.EnabledFolders.indexOf(folder.Id); - var checkedAttribute = isChecked ? ' checked="checked"' : ""; - html += '"; + var checkedAttribute = isChecked ? ' checked="checked"' : ''; + html += ''; } - html += "
"; - page.querySelector(".folderAccess").innerHTML = html; - var chkEnableAllFolders = page.querySelector("#chkEnableAllFolders"); + html += ''; + page.querySelector('.folderAccess').innerHTML = html; + var chkEnableAllFolders = page.querySelector('#chkEnableAllFolders'); chkEnableAllFolders.checked = user.Policy.EnableAllFolders; triggerChange(chkEnableAllFolders); } function loadChannels(page, user, channels) { - var html = ""; - html += '

' + Globalize.translate("HeaderChannels") + "

"; + var html = ''; + html += '

' + globalize.translate('HeaderChannels') + '

'; html += '
'; for (var i = 0, length = channels.length; i < length; i++) { var folder = channels[i]; var isChecked = user.Policy.EnableAllChannels || -1 != user.Policy.EnabledChannels.indexOf(folder.Id); - var checkedAttribute = isChecked ? ' checked="checked"' : ""; - html += '"; + var checkedAttribute = isChecked ? ' checked="checked"' : ''; + html += ''; } - html += "
"; - $(".channelAccess", page).show().html(html); + html += ''; + $('.channelAccess', page).show().html(html); if (channels.length) { - $(".channelAccessContainer", page).show(); + $('.channelAccessContainer', page).show(); } else { - $(".channelAccessContainer", page).hide(); + $('.channelAccessContainer', page).hide(); } - $("#chkEnableAllChannels", page).checked(user.Policy.EnableAllChannels).trigger("change"); + $('#chkEnableAllChannels', page).checked = user.Policy.EnableAllChannels; } function loadDevices(page, user, devices) { - var html = ""; - html += '

' + Globalize.translate("HeaderDevices") + "

"; + var html = ''; + html += '

' + globalize.translate('HeaderDevices') + '

'; html += '
'; for (var i = 0, length = devices.length; i < length; i++) { var device = devices[i]; - var checkedAttribute = user.Policy.EnableAllDevices || -1 != user.Policy.EnabledDevices.indexOf(device.Id) ? ' checked="checked"' : ""; - html += '"; + var checkedAttribute = user.Policy.EnableAllDevices || -1 != user.Policy.EnabledDevices.indexOf(device.Id) ? ' checked="checked"' : ''; + html += ''; } - html += "
"; - $(".deviceAccess", page).show().html(html); - $("#chkEnableAllDevices", page).checked(user.Policy.EnableAllDevices).trigger("change"); + html += ''; + $('.deviceAccess', page).show().html(html); + $('#chkEnableAllDevices', page).checked = user.Policy.EnableAllDevices; if (user.Policy.IsAdministrator) { - page.querySelector(".deviceAccessContainer").classList.add("hide"); + page.querySelector('.deviceAccessContainer').classList.add('hide'); } else { - page.querySelector(".deviceAccessContainer").classList.remove("hide"); + page.querySelector('.deviceAccessContainer').classList.remove('hide'); } } function loadUser(page, user, loggedInUser, mediaFolders, channels, devices) { - page.querySelector(".username").innerHTML = user.Name; + page.querySelector('.username').innerHTML = user.Name; libraryMenu.setTitle(user.Name); loadChannels(page, user, channels); loadMediaFolders(page, user, mediaFolders); @@ -84,29 +84,29 @@ define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, function onSaveComplete(page) { loading.hide(); - require(["toast"], function (toast) { - toast(Globalize.translate("SettingsSaved")); + require(['toast'], function (toast) { + toast(globalize.translate('SettingsSaved')); }); } function saveUser(user, page) { - user.Policy.EnableAllFolders = $("#chkEnableAllFolders", page).checked(); - user.Policy.EnabledFolders = user.Policy.EnableAllFolders ? [] : $(".chkFolder", page).get().filter(function (c) { + user.Policy.EnableAllFolders = $('#chkEnableAllFolders', page).checked; + user.Policy.EnabledFolders = user.Policy.EnableAllFolders ? [] : $('.chkFolder', page).get().filter(function (c) { return c.checked; }).map(function (c) { - return c.getAttribute("data-id"); + return c.getAttribute('data-id'); }); - user.Policy.EnableAllChannels = $("#chkEnableAllChannels", page).checked(); - user.Policy.EnabledChannels = user.Policy.EnableAllChannels ? [] : $(".chkChannel", page).get().filter(function (c) { + user.Policy.EnableAllChannels = $('#chkEnableAllChannels', page).checked; + user.Policy.EnabledChannels = user.Policy.EnableAllChannels ? [] : $('.chkChannel', page).get().filter(function (c) { return c.checked; }).map(function (c) { - return c.getAttribute("data-id"); + return c.getAttribute('data-id'); }); - user.Policy.EnableAllDevices = $("#chkEnableAllDevices", page).checked(); - user.Policy.EnabledDevices = user.Policy.EnableAllDevices ? [] : $(".chkDevice", page).get().filter(function (c) { + user.Policy.EnableAllDevices = $('#chkEnableAllDevices', page).checked; + user.Policy.EnabledDevices = user.Policy.EnableAllDevices ? [] : $('.chkDevice', page).get().filter(function (c) { return c.checked; }).map(function (c) { - return c.getAttribute("data-id"); + return c.getAttribute('data-id'); }); user.Policy.BlockedChannels = null; user.Policy.BlockedMediaFolders = null; @@ -116,44 +116,44 @@ define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, } function onSubmit() { - var page = $(this).parents(".page"); + var page = $(this).parents('.page'); loading.show(); - var userId = getParameterByName("userId"); + var userId = getParameterByName('userId'); ApiClient.getUser(userId).then(function (result) { saveUser(result, page); }); return false; } - $(document).on("pageinit", "#userLibraryAccessPage", function () { + $(document).on('pageinit', '#userLibraryAccessPage', function () { var page = this; - $("#chkEnableAllDevices", page).on("change", function () { + $('#chkEnableAllDevices', page).on('change', function () { if (this.checked) { - $(".deviceAccessListContainer", page).hide(); + $('.deviceAccessListContainer', page).hide(); } else { - $(".deviceAccessListContainer", page).show(); + $('.deviceAccessListContainer', page).show(); } }); - $("#chkEnableAllChannels", page).on("change", function () { + $('#chkEnableAllChannels', page).on('change', function () { if (this.checked) { - $(".channelAccessListContainer", page).hide(); + $('.channelAccessListContainer', page).hide(); } else { - $(".channelAccessListContainer", page).show(); + $('.channelAccessListContainer', page).show(); } }); - page.querySelector("#chkEnableAllFolders").addEventListener("change", function () { + page.querySelector('#chkEnableAllFolders').addEventListener('change', function () { if (this.checked) { - page.querySelector(".folderAccessListContainer").classList.add("hide"); + page.querySelector('.folderAccessListContainer').classList.add('hide'); } else { - page.querySelector(".folderAccessListContainer").classList.remove("hide"); + page.querySelector('.folderAccessListContainer').classList.remove('hide'); } }); - $(".userLibraryAccessForm").off("submit", onSubmit).on("submit", onSubmit); - }).on("pageshow", "#userLibraryAccessPage", function () { + $('.userLibraryAccessForm').off('submit', onSubmit).on('submit', onSubmit); + }).on('pageshow', '#userLibraryAccessPage', function () { var page = this; loading.show(); var promise1; - var userId = getParameterByName("userId"); + var userId = getParameterByName('userId'); if (userId) { promise1 = ApiClient.getUser(userId); @@ -166,11 +166,11 @@ define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, } var promise2 = Dashboard.getCurrentUser(); - var promise4 = ApiClient.getJSON(ApiClient.getUrl("Library/MediaFolders", { + var promise4 = ApiClient.getJSON(ApiClient.getUrl('Library/MediaFolders', { IsHidden: false })); - var promise5 = ApiClient.getJSON(ApiClient.getUrl("Channels")); - var promise6 = ApiClient.getJSON(ApiClient.getUrl("Devices")); + var promise5 = ApiClient.getJSON(ApiClient.getUrl('Channels')); + var promise6 = ApiClient.getJSON(ApiClient.getUrl('Devices')); Promise.all([promise1, promise2, promise4, promise5, promise6]).then(function (responses) { loadUser(page, responses[0], responses[1], responses[2].Items, responses[3].Items, responses[4].Items); }); diff --git a/src/controllers/dashboard/users/usernew.js b/src/controllers/dashboard/users/usernew.js new file mode 100644 index 0000000000..1e4e2bbee2 --- /dev/null +++ b/src/controllers/dashboard/users/usernew.js @@ -0,0 +1,126 @@ +define(['jQuery', 'loading', 'globalize', 'emby-checkbox'], function ($, loading, globalize) { + 'use strict'; + + function loadMediaFolders(page, mediaFolders) { + var html = ''; + html += '

' + globalize.translate('HeaderLibraries') + '

'; + html += '
'; + + for (var i = 0; i < mediaFolders.length; i++) { + var folder = mediaFolders[i]; + html += ''; + } + + html += '
'; + $('.folderAccess', page).html(html).trigger('create'); + $('#chkEnableAllFolders', page).checked = false; + } + + function loadChannels(page, channels) { + var html = ''; + html += '

' + globalize.translate('HeaderChannels') + '

'; + html += '
'; + + for (var i = 0; i < channels.length; i++) { + var folder = channels[i]; + html += ''; + } + + html += '
'; + $('.channelAccess', page).show().html(html).trigger('create'); + + if (channels.length) { + $('.channelAccessContainer', page).show(); + } else { + $('.channelAccessContainer', page).hide(); + } + + $('#chkEnableAllChannels', page).checked = false; + } + + function loadUser(page) { + $('#txtUsername', page).val(''); + $('#txtPassword', page).val(''); + loading.show(); + var promiseFolders = ApiClient.getJSON(ApiClient.getUrl('Library/MediaFolders', { + IsHidden: false + })); + var promiseChannels = ApiClient.getJSON(ApiClient.getUrl('Channels')); + Promise.all([promiseFolders, promiseChannels]).then(function (responses) { + loadMediaFolders(page, responses[0].Items); + loadChannels(page, responses[1].Items); + loading.hide(); + }); + } + + function saveUser(page) { + var user = {}; + user.Name = $('#txtUsername', page).val(); + user.Password = $('#txtPassword', page).val(); + ApiClient.createUser(user).then(function (user) { + user.Policy.EnableAllFolders = $('#chkEnableAllFolders', page).checked; + user.Policy.EnabledFolders = []; + + if (!user.Policy.EnableAllFolders) { + user.Policy.EnabledFolders = $('.chkFolder', page).get().filter(function (i) { + return i.checked; + }).map(function (i) { + return i.getAttribute('data-id'); + }); + } + + user.Policy.EnableAllChannels = $('#chkEnableAllChannels', page).checked; + user.Policy.EnabledChannels = []; + + if (!user.Policy.EnableAllChannels) { + user.Policy.EnabledChannels = $('.chkChannel', page).get().filter(function (i) { + return i.checked; + }).map(function (i) { + return i.getAttribute('data-id'); + }); + } + + ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () { + Dashboard.navigate('useredit.html?userId=' + user.Id); + }); + }, function (response) { + require(['toast'], function (toast) { + toast(globalize.translate('DefaultErrorMessage')); + }); + + loading.hide(); + }); + } + + function onSubmit() { + var page = $(this).parents('.page')[0]; + loading.show(); + saveUser(page); + return false; + } + + function loadData(page) { + loadUser(page); + } + + $(document).on('pageinit', '#newUserPage', function () { + var page = this; + $('#chkEnableAllChannels', page).on('change', function () { + if (this.checked) { + $('.channelAccessListContainer', page).hide(); + } else { + $('.channelAccessListContainer', page).show(); + } + }); + $('#chkEnableAllFolders', page).on('change', function () { + if (this.checked) { + $('.folderAccessListContainer', page).hide(); + } else { + $('.folderAccessListContainer', page).show(); + } + }); + $('.newUserProfileForm').off('submit', onSubmit).on('submit', onSubmit); + }).on('pageshow', '#newUserPage', function () { + loadData(this); + }); +}); diff --git a/src/controllers/userparentalcontrol.js b/src/controllers/dashboard/users/userparentalcontrol.js similarity index 60% rename from src/controllers/userparentalcontrol.js rename to src/controllers/dashboard/users/userparentalcontrol.js index 333c09f266..e8255512d6 100644 --- a/src/controllers/userparentalcontrol.js +++ b/src/controllers/dashboard/users/userparentalcontrol.js @@ -1,8 +1,8 @@ -define(["jQuery", "datetime", "loading", "libraryMenu", "listViewStyle", "paper-icon-button-light"], function ($, datetime, loading, libraryMenu) { - "use strict"; +define(['jQuery', 'datetime', 'loading', 'libraryMenu', 'globalize', 'listViewStyle', 'paper-icon-button-light'], function ($, datetime, loading, libraryMenu, globalize) { + 'use strict'; function populateRatings(allParentalRatings, page) { - var html = ""; + var html = ''; html += ""; var i; var length; @@ -14,7 +14,7 @@ define(["jQuery", "datetime", "loading", "libraryMenu", "listViewStyle", "paper- var lastRating = ratings[ratings.length - 1]; if (lastRating.Value === rating.Value) { - lastRating.Name += "/" + rating.Name; + lastRating.Name += '/' + rating.Name; continue; } } @@ -27,56 +27,56 @@ define(["jQuery", "datetime", "loading", "libraryMenu", "listViewStyle", "paper- for (i = 0, length = ratings.length; i < length; i++) { rating = ratings[i]; - html += ""; + html += "'; } - $("#selectMaxParentalRating", page).html(html); + $('#selectMaxParentalRating', page).html(html); } function loadUnratedItems(page, user) { var items = [{ - name: Globalize.translate("OptionBlockBooks"), - value: "Book" + name: globalize.translate('OptionBlockBooks'), + value: 'Book' }, { - name: Globalize.translate("OptionBlockChannelContent"), - value: "ChannelContent" + name: globalize.translate('OptionBlockChannelContent'), + value: 'ChannelContent' }, { - name: Globalize.translate("OptionBlockLiveTvChannels"), - value: "LiveTvChannel" + name: globalize.translate('OptionBlockLiveTvChannels'), + value: 'LiveTvChannel' }, { - name: Globalize.translate("OptionBlockMovies"), - value: "Movie" + name: globalize.translate('OptionBlockMovies'), + value: 'Movie' }, { - name: Globalize.translate("OptionBlockMusic"), - value: "Music" + name: globalize.translate('OptionBlockMusic'), + value: 'Music' }, { - name: Globalize.translate("OptionBlockTrailers"), - value: "Trailer" + name: globalize.translate('OptionBlockTrailers'), + value: 'Trailer' }, { - name: Globalize.translate("OptionBlockTvShows"), - value: "Series" + name: globalize.translate('OptionBlockTvShows'), + value: 'Series' }]; - var html = ""; - html += '

' + Globalize.translate("HeaderBlockItemsWithNoRating") + "

"; + var html = ''; + html += '

' + globalize.translate('HeaderBlockItemsWithNoRating') + '

'; html += '
'; for (var i = 0, length = items.length; i < length; i++) { var item = items[i]; - var checkedAttribute = -1 != user.Policy.BlockUnratedItems.indexOf(item.value) ? ' checked="checked"' : ""; - html += '"; + var checkedAttribute = -1 != user.Policy.BlockUnratedItems.indexOf(item.value) ? ' checked="checked"' : ''; + html += ''; } - html += "
"; - $(".blockUnratedItems", page).html(html).trigger("create"); + html += ''; + $('.blockUnratedItems', page).html(html).trigger('create'); } function loadUser(page, user, allParentalRatings) { - page.querySelector(".username").innerHTML = user.Name; + page.querySelector('.username').innerHTML = user.Name; libraryMenu.setTitle(user.Name); loadUnratedItems(page, user); loadBlockedTags(page, user.Policy.BlockedTags); populateRatings(allParentalRatings, page); - var ratingValue = ""; + var ratingValue = ''; if (user.Policy.MaxParentalRating) { for (var i = 0, length = allParentalRatings.length; i < length; i++) { @@ -88,12 +88,12 @@ define(["jQuery", "datetime", "loading", "libraryMenu", "listViewStyle", "paper- } } - $("#selectMaxParentalRating", page).val(ratingValue); + $('#selectMaxParentalRating', page).val(ratingValue); if (user.Policy.IsAdministrator) { - $(".accessScheduleSection", page).hide(); + $('.accessScheduleSection', page).hide(); } else { - $(".accessScheduleSection", page).show(); + $('.accessScheduleSection', page).show(); } renderAccessSchedule(page, user.Policy.AccessSchedules || []); @@ -106,19 +106,19 @@ define(["jQuery", "datetime", "loading", "libraryMenu", "listViewStyle", "paper- li += '
'; li += '

'; li += h; - li += "

"; - li += "
"; - li += ''; - return li += ""; - }).join(""); + li += ''; + li += ''; + li += ''; + return li += ''; + }).join(''); if (html) { - html = '
' + html + "
"; + html = '
' + html + '
'; } - var elem = $(".blockedTags", page).html(html).trigger("create"); - $(".btnDeleteTag", elem).on("click", function () { - var tag = this.getAttribute("data-tag"); + var elem = $('.blockedTags', page).html(html).trigger('create'); + $('.btnDeleteTag', elem).on('click', function () { + var tag = this.getAttribute('data-tag'); var newTags = tags.filter(function (t) { return t != tag; }); @@ -132,43 +132,43 @@ define(["jQuery", "datetime", "loading", "libraryMenu", "listViewStyle", "paper- } function renderAccessSchedule(page, schedules) { - var html = ""; + var html = ''; var index = 0; html += schedules.map(function (a) { - var itemHtml = ""; + var itemHtml = ''; itemHtml += '
'; itemHtml += '
'; itemHtml += '

'; - itemHtml += Globalize.translate("Option" + a.DayOfWeek); - itemHtml += "

"; - itemHtml += '
' + getDisplayTime(a.StartHour) + " - " + getDisplayTime(a.EndHour) + "
"; - itemHtml += "
"; - itemHtml += ''; - itemHtml += "
"; + itemHtml += globalize.translate('Option' + a.DayOfWeek); + itemHtml += ''; + itemHtml += '
' + getDisplayTime(a.StartHour) + ' - ' + getDisplayTime(a.EndHour) + '
'; + itemHtml += ''; + itemHtml += ''; + itemHtml += ''; index++; return itemHtml; - }).join(""); - var accessScheduleList = page.querySelector(".accessScheduleList"); + }).join(''); + var accessScheduleList = page.querySelector('.accessScheduleList'); accessScheduleList.innerHTML = html; - $(".btnDelete", accessScheduleList).on("click", function () { - deleteAccessSchedule(page, schedules, parseInt(this.getAttribute("data-index"))); + $('.btnDelete', accessScheduleList).on('click', function () { + deleteAccessSchedule(page, schedules, parseInt(this.getAttribute('data-index'))); }); } function onSaveComplete(page) { loading.hide(); - require(["toast"], function (toast) { - toast(Globalize.translate("SettingsSaved")); + require(['toast'], function (toast) { + toast(globalize.translate('SettingsSaved')); }); } function saveUser(user, page) { - user.Policy.MaxParentalRating = $("#selectMaxParentalRating", page).val() || null; - user.Policy.BlockUnratedItems = $(".chkUnratedItem", page).get().filter(function (i) { + user.Policy.MaxParentalRating = $('#selectMaxParentalRating', page).val() || null; + user.Policy.BlockUnratedItems = $('.chkUnratedItem', page).get().filter(function (i) { return i.checked; }).map(function (i) { - return i.getAttribute("data-itemtype"); + return i.getAttribute('data-itemtype'); }); user.Policy.AccessSchedules = getSchedulesFromPage(page); user.Policy.BlockedTags = getBlockedTagsFromPage(page); @@ -191,7 +191,7 @@ define(["jQuery", "datetime", "loading", "libraryMenu", "listViewStyle", "paper- function showSchedulePopup(page, schedule, index) { schedule = schedule || {}; - require(["components/accessschedule/accessschedule"], function (accessschedule) { + require(['components/accessSchedule/accessSchedule'], function (accessschedule) { accessschedule.show({ schedule: schedule }).then(function (updatedSchedule) { @@ -208,25 +208,25 @@ define(["jQuery", "datetime", "loading", "libraryMenu", "listViewStyle", "paper- } function getSchedulesFromPage(page) { - return $(".liSchedule", page).map(function () { + return $('.liSchedule', page).map(function () { return { - DayOfWeek: this.getAttribute("data-day"), - StartHour: this.getAttribute("data-start"), - EndHour: this.getAttribute("data-end") + DayOfWeek: this.getAttribute('data-day'), + StartHour: this.getAttribute('data-start'), + EndHour: this.getAttribute('data-end') }; }).get(); } function getBlockedTagsFromPage(page) { - return $(".blockedTag", page).map(function () { - return this.getAttribute("data-tag"); + return $('.blockedTag', page).map(function () { + return this.getAttribute('data-tag'); }).get(); } function showBlockedTagPopup(page) { - require(["prompt"], function (prompt) { + require(['prompt'], function (prompt) { prompt({ - label: Globalize.translate("LabelTag") + label: globalize.translate('LabelTag') }).then(function (value) { var tags = getBlockedTagsFromPage(page); @@ -240,28 +240,28 @@ define(["jQuery", "datetime", "loading", "libraryMenu", "listViewStyle", "paper- window.UserParentalControlPage = { onSubmit: function () { - var page = $(this).parents(".page"); + var page = $(this).parents('.page'); loading.show(); - var userId = getParameterByName("userId"); + var userId = getParameterByName('userId'); ApiClient.getUser(userId).then(function (result) { saveUser(result, page); }); return false; } }; - $(document).on("pageinit", "#userParentalControlPage", function () { + $(document).on('pageinit', '#userParentalControlPage', function () { var page = this; - $(".btnAddSchedule", page).on("click", function () { + $('.btnAddSchedule', page).on('click', function () { showSchedulePopup(page, {}, -1); }); - $(".btnAddBlockedTag", page).on("click", function () { + $('.btnAddBlockedTag', page).on('click', function () { showBlockedTagPopup(page); }); - $(".userParentalControlForm").off("submit", UserParentalControlPage.onSubmit).on("submit", UserParentalControlPage.onSubmit); - }).on("pageshow", "#userParentalControlPage", function () { + $('.userParentalControlForm').off('submit', UserParentalControlPage.onSubmit).on('submit', UserParentalControlPage.onSubmit); + }).on('pageshow', '#userParentalControlPage', function () { var page = this; loading.show(); - var userId = getParameterByName("userId"); + var userId = getParameterByName('userId'); var promise1 = ApiClient.getUser(userId); var promise2 = ApiClient.getParentalRatings(); Promise.all([promise1, promise2]).then(function (responses) { diff --git a/src/controllers/userpasswordpage.js b/src/controllers/dashboard/users/userpasswordpage.js similarity index 50% rename from src/controllers/userpasswordpage.js rename to src/controllers/dashboard/users/userpasswordpage.js index eeb9b25e3e..186e39b151 100644 --- a/src/controllers/userpasswordpage.js +++ b/src/controllers/dashboard/users/userpasswordpage.js @@ -1,67 +1,67 @@ -define(["loading", "libraryMenu", "emby-button"], function (loading, libraryMenu) { - "use strict"; +define(['loading', 'libraryMenu', 'globalize', 'emby-button'], function (loading, libraryMenu, globalize) { + 'use strict'; function loadUser(page, params) { var userid = params.userId; ApiClient.getUser(userid).then(function (user) { Dashboard.getCurrentUser().then(function (loggedInUser) { libraryMenu.setTitle(user.Name); - page.querySelector(".username").innerHTML = user.Name; + page.querySelector('.username').innerHTML = user.Name; var showPasswordSection = true; var showLocalAccessSection = false; - if ("Guest" == user.ConnectLinkType) { - page.querySelector(".localAccessSection").classList.add("hide"); + if ('Guest' == user.ConnectLinkType) { + page.querySelector('.localAccessSection').classList.add('hide'); showPasswordSection = false; } else if (user.HasConfiguredPassword) { - page.querySelector("#btnResetPassword").classList.remove("hide"); - page.querySelector("#fldCurrentPassword").classList.remove("hide"); + page.querySelector('#btnResetPassword').classList.remove('hide'); + page.querySelector('#fldCurrentPassword').classList.remove('hide'); showLocalAccessSection = true; } else { - page.querySelector("#btnResetPassword").classList.add("hide"); - page.querySelector("#fldCurrentPassword").classList.add("hide"); + page.querySelector('#btnResetPassword').classList.add('hide'); + page.querySelector('#fldCurrentPassword').classList.add('hide'); } if (showPasswordSection && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) { - page.querySelector(".passwordSection").classList.remove("hide"); + page.querySelector('.passwordSection').classList.remove('hide'); } else { - page.querySelector(".passwordSection").classList.add("hide"); + page.querySelector('.passwordSection').classList.add('hide'); } if (showLocalAccessSection && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) { - page.querySelector(".localAccessSection").classList.remove("hide"); + page.querySelector('.localAccessSection').classList.remove('hide'); } else { - page.querySelector(".localAccessSection").classList.add("hide"); + page.querySelector('.localAccessSection').classList.add('hide'); } - var txtEasyPassword = page.querySelector("#txtEasyPassword"); - txtEasyPassword.value = ""; + var txtEasyPassword = page.querySelector('#txtEasyPassword'); + txtEasyPassword.value = ''; if (user.HasConfiguredEasyPassword) { - txtEasyPassword.placeholder = "******"; - page.querySelector("#btnResetEasyPassword").classList.remove("hide"); + txtEasyPassword.placeholder = '******'; + page.querySelector('#btnResetEasyPassword').classList.remove('hide'); } else { - txtEasyPassword.removeAttribute("placeholder"); - txtEasyPassword.placeholder = ""; - page.querySelector("#btnResetEasyPassword").classList.add("hide"); + txtEasyPassword.removeAttribute('placeholder'); + txtEasyPassword.placeholder = ''; + page.querySelector('#btnResetEasyPassword').classList.add('hide'); } - page.querySelector(".chkEnableLocalEasyPassword").checked = user.Configuration.EnableLocalPassword; + page.querySelector('.chkEnableLocalEasyPassword').checked = user.Configuration.EnableLocalPassword; - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); }); }); - page.querySelector("#txtCurrentPassword").value = ""; - page.querySelector("#txtNewPassword").value = ""; - page.querySelector("#txtNewPasswordConfirm").value = ""; + page.querySelector('#txtCurrentPassword').value = ''; + page.querySelector('#txtNewPassword').value = ''; + page.querySelector('#txtNewPasswordConfirm').value = ''; } return function (view, params) { function saveEasyPassword() { var userId = params.userId; - var easyPassword = view.querySelector("#txtEasyPassword").value; + var easyPassword = view.querySelector('#txtEasyPassword').value; if (easyPassword) { ApiClient.updateEasyPassword(userId, easyPassword).then(function () { @@ -74,12 +74,12 @@ define(["loading", "libraryMenu", "emby-button"], function (loading, libraryMenu function onEasyPasswordSaved(userId) { ApiClient.getUser(userId).then(function (user) { - user.Configuration.EnableLocalPassword = view.querySelector(".chkEnableLocalEasyPassword").checked; + user.Configuration.EnableLocalPassword = view.querySelector('.chkEnableLocalEasyPassword').checked; ApiClient.updateUserConfiguration(user.Id, user.Configuration).then(function () { loading.hide(); - require(["toast"], function (toast) { - toast(Globalize.translate("MessageSettingsSaved")); + require(['toast'], function (toast) { + toast(globalize.translate('MessageSettingsSaved')); }); loadUser(view, params); @@ -89,28 +89,28 @@ define(["loading", "libraryMenu", "emby-button"], function (loading, libraryMenu function savePassword() { var userId = params.userId; - var currentPassword = view.querySelector("#txtCurrentPassword").value; - var newPassword = view.querySelector("#txtNewPassword").value; + var currentPassword = view.querySelector('#txtCurrentPassword').value; + var newPassword = view.querySelector('#txtNewPassword').value; - if (view.querySelector("#fldCurrentPassword").classList.contains("hide")) { + if (view.querySelector('#fldCurrentPassword').classList.contains('hide')) { // Firefox does not respect autocomplete=off, so clear it if the field is supposed to be hidden (and blank) // This should only happen when user.HasConfiguredPassword is false, but this information is not passed on - currentPassword = ""; + currentPassword = ''; } ApiClient.updateUserPassword(userId, currentPassword, newPassword).then(function () { loading.hide(); - require(["toast"], function (toast) { - toast(Globalize.translate("PasswordSaved")); + require(['toast'], function (toast) { + toast(globalize.translate('PasswordSaved')); }); loadUser(view, params); }, function () { loading.hide(); Dashboard.alert({ - title: Globalize.translate("HeaderLoginFailure"), - message: Globalize.translate("MessageInvalidUser") + title: globalize.translate('HeaderLoginFailure'), + message: globalize.translate('MessageInvalidUser') }); }); } @@ -118,9 +118,9 @@ define(["loading", "libraryMenu", "emby-button"], function (loading, libraryMenu function onSubmit(e) { var form = this; - if (form.querySelector("#txtNewPassword").value != form.querySelector("#txtNewPasswordConfirm").value) { - require(["toast"], function (toast) { - toast(Globalize.translate("PasswordMatchError")); + if (form.querySelector('#txtNewPassword').value != form.querySelector('#txtNewPasswordConfirm').value) { + require(['toast'], function (toast) { + toast(globalize.translate('PasswordMatchError')); }); } else { loading.show(); @@ -139,17 +139,17 @@ define(["loading", "libraryMenu", "emby-button"], function (loading, libraryMenu } function resetPassword() { - var msg = Globalize.translate("PasswordResetConfirmation"); + var msg = globalize.translate('PasswordResetConfirmation'); - require(["confirm"], function (confirm) { - confirm(msg, Globalize.translate("PasswordResetHeader")).then(function () { + require(['confirm'], function (confirm) { + confirm(msg, globalize.translate('PasswordResetHeader')).then(function () { var userId = params.userId; loading.show(); ApiClient.resetUserPassword(userId).then(function () { loading.hide(); Dashboard.alert({ - message: Globalize.translate("PasswordResetComplete"), - title: Globalize.translate("PasswordResetHeader") + message: globalize.translate('PasswordResetComplete'), + title: globalize.translate('PasswordResetHeader') }); loadUser(view, params); }); @@ -158,17 +158,17 @@ define(["loading", "libraryMenu", "emby-button"], function (loading, libraryMenu } function resetEasyPassword() { - var msg = Globalize.translate("PinCodeResetConfirmation"); + var msg = globalize.translate('PinCodeResetConfirmation'); - require(["confirm"], function (confirm) { - confirm(msg, Globalize.translate("HeaderPinCodeReset")).then(function () { + require(['confirm'], function (confirm) { + confirm(msg, globalize.translate('HeaderPinCodeReset')).then(function () { var userId = params.userId; loading.show(); ApiClient.resetEasyPassword(userId).then(function () { loading.hide(); Dashboard.alert({ - message: Globalize.translate("PinCodeResetComplete"), - title: Globalize.translate("HeaderPinCodeReset") + message: globalize.translate('PinCodeResetComplete'), + title: globalize.translate('HeaderPinCodeReset') }); loadUser(view, params); }); @@ -176,11 +176,11 @@ define(["loading", "libraryMenu", "emby-button"], function (loading, libraryMenu }); } - view.querySelector(".updatePasswordForm").addEventListener("submit", onSubmit); - view.querySelector(".localAccessForm").addEventListener("submit", onLocalAccessSubmit); - view.querySelector("#btnResetEasyPassword").addEventListener("click", resetEasyPassword); - view.querySelector("#btnResetPassword").addEventListener("click", resetPassword); - view.addEventListener("viewshow", function () { + view.querySelector('.updatePasswordForm').addEventListener('submit', onSubmit); + view.querySelector('.localAccessForm').addEventListener('submit', onLocalAccessSubmit); + view.querySelector('#btnResetEasyPassword').addEventListener('click', resetEasyPassword); + view.querySelector('#btnResetPassword').addEventListener('click', resetPassword); + view.addEventListener('viewshow', function () { loadUser(view, params); }); }; diff --git a/src/controllers/userprofilespage.js b/src/controllers/dashboard/users/userprofilespage.js similarity index 57% rename from src/controllers/userprofilespage.js rename to src/controllers/dashboard/users/userprofilespage.js index 180d0e62ae..c691c665f6 100644 --- a/src/controllers/userprofilespage.js +++ b/src/controllers/dashboard/users/userprofilespage.js @@ -1,15 +1,15 @@ -define(["loading", "dom", "globalize", "date-fns", "dfnshelper", "paper-icon-button-light", "cardStyle", "emby-button", "indicators", "flexStyles"], function (loading, dom, globalize, datefns, dfnshelper) { - "use strict"; +define(['loading', 'dom', 'globalize', 'date-fns', 'dfnshelper', 'paper-icon-button-light', 'cardStyle', 'emby-button', 'indicators', 'flexStyles'], function (loading, dom, globalize, datefns, dfnshelper) { + 'use strict'; function deleteUser(page, id) { - var msg = globalize.translate("DeleteUserConfirmation"); + var msg = globalize.translate('DeleteUserConfirmation'); - require(["confirm"], function (confirm) { + require(['confirm'], function (confirm) { confirm({ - title: globalize.translate("DeleteUser"), + title: globalize.translate('DeleteUser'), text: msg, - confirmText: globalize.translate("ButtonDelete"), - primary: "delete" + confirmText: globalize.translate('ButtonDelete'), + primary: 'delete' }).then(function () { loading.show(); ApiClient.deleteUser(id).then(function () { @@ -20,50 +20,50 @@ define(["loading", "dom", "globalize", "date-fns", "dfnshelper", "paper-icon-but } function showUserMenu(elem) { - var card = dom.parentWithClass(elem, "card"); - var page = dom.parentWithClass(card, "page"); - var userId = card.getAttribute("data-userid"); + var card = dom.parentWithClass(elem, 'card'); + var page = dom.parentWithClass(card, 'page'); + var userId = card.getAttribute('data-userid'); var menuItems = []; menuItems.push({ - name: globalize.translate("ButtonOpen"), - id: "open", - icon: "mode_edit" + name: globalize.translate('ButtonOpen'), + id: 'open', + icon: 'mode_edit' }); menuItems.push({ - name: globalize.translate("ButtonLibraryAccess"), - id: "access", - icon: "lock" + name: globalize.translate('ButtonLibraryAccess'), + id: 'access', + icon: 'lock' }); menuItems.push({ - name: globalize.translate("ButtonParentalControl"), - id: "parentalcontrol", - icon: "person" + name: globalize.translate('ButtonParentalControl'), + id: 'parentalcontrol', + icon: 'person' }); menuItems.push({ - name: globalize.translate("ButtonDelete"), - id: "delete", - icon: "delete" + name: globalize.translate('ButtonDelete'), + id: 'delete', + icon: 'delete' }); - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, positionTo: card, callback: function (id) { switch (id) { - case "open": - Dashboard.navigate("useredit.html?userId=" + userId); + case 'open': + Dashboard.navigate('useredit.html?userId=' + userId); break; - case "access": - Dashboard.navigate("userlibraryaccess.html?userId=" + userId); + case 'access': + Dashboard.navigate('userlibraryaccess.html?userId=' + userId); break; - case "parentalcontrol": - Dashboard.navigate("userparentalcontrol.html?userId=" + userId); + case 'parentalcontrol': + Dashboard.navigate('userparentalcontrol.html?userId=' + userId); break; - case "delete": + case 'delete': deleteUser(page, userId); } } @@ -72,11 +72,11 @@ define(["loading", "dom", "globalize", "date-fns", "dfnshelper", "paper-icon-but } function getUserHtml(user, addConnectIndicator) { - var html = ""; - var cssClass = "card squareCard scalableCard squareCard-scalable"; + var html = ''; + var cssClass = 'card squareCard scalableCard squareCard-scalable'; if (user.Policy.IsDisabled) { - cssClass += " grayscale"; + cssClass += ' grayscale'; } html += "
"; @@ -90,79 +90,79 @@ define(["loading", "dom", "globalize", "date-fns", "dfnshelper", "paper-icon-but imgUrl = ApiClient.getUserImageUrl(user.Id, { width: 300, tag: user.PrimaryImageTag, - type: "Primary" + type: 'Primary' }); } - var imageClass = "cardImage"; + var imageClass = 'cardImage'; if (user.Policy.IsDisabled) { - imageClass += " disabledUser"; + imageClass += ' disabledUser'; } if (imgUrl) { html += '
"; } else { html += '
'; - html += 'person'; + html += ''; } - html += "
"; - html += ""; - html += "
"; + html += '
'; + html += ''; + html += ''; html += '
'; html += '
'; html += '
'; html += user.Name; - html += "
"; - html += ''; - html += "
"; + html += '
'; + html += ''; + html += ''; html += '
'; var lastSeen = getLastSeenText(user.LastActivityDate); - html += "" != lastSeen ? lastSeen : " "; - html += "
"; - html += ""; - html += ""; - return html + ""; + html += '' != lastSeen ? lastSeen : ' '; + html += ''; + html += ''; + html += ''; + return html + ''; } // FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix // how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences function getLastSeenText(lastActivityDate) { if (lastActivityDate) { - return globalize.translate("LastSeen", datefns.formatDistanceToNow(Date.parse(lastActivityDate), dfnshelper.localeWithSuffix)); + return globalize.translate('LastSeen', datefns.formatDistanceToNow(Date.parse(lastActivityDate), dfnshelper.localeWithSuffix)); } - return ""; + return ''; } function getUserSectionHtml(users, addConnectIndicator) { return users.map(function (u__q) { return getUserHtml(u__q, addConnectIndicator); - }).join(""); + }).join(''); } function renderUsers(page, users) { - page.querySelector(".localUsers").innerHTML = getUserSectionHtml(users, true); + page.querySelector('.localUsers').innerHTML = getUserSectionHtml(users, true); } function showPendingUserMenu(elem) { var menuItems = []; menuItems.push({ - name: globalize.translate("ButtonCancel"), - id: "delete", - icon: "delete" + name: globalize.translate('ButtonCancel'), + id: 'delete', + icon: 'delete' }); - require(["actionsheet"], function (actionsheet) { - var card = dom.parentWithClass(elem, "card"); - var page = dom.parentWithClass(card, "page"); - var id = card.getAttribute("data-id"); + require(['actionsheet'], function (actionsheet) { + var card = dom.parentWithClass(elem, 'card'); + var page = dom.parentWithClass(card, 'page'); + var id = card.getAttribute('data-id'); actionsheet.show({ items: menuItems, positionTo: card, callback: function (menuItemId) { switch (menuItemId) { - case "delete": + case 'delete': cancelAuthorization(page, id); } } @@ -171,7 +171,7 @@ define(["loading", "dom", "globalize", "date-fns", "dfnshelper", "paper-icon-but } function getPendingUserHtml(user) { - var html = ""; + var html = ''; html += "
"; html += '
'; html += '
'; @@ -180,41 +180,41 @@ define(["loading", "dom", "globalize", "date-fns", "dfnshelper", "paper-icon-but if (user.ImageUrl) { html += '
"; - html += "
"; + html += '
'; } else { - html += 'person'; + html += ''; } - html += ""; - html += "
"; + html += ''; + html += '
'; html += '
'; html += '
'; - html += ''; - html += "
"; + html += ''; + html += '
'; html += '
'; html += user.UserName; - html += "
"; - html += ""; - html += ""; - return html + ""; + html += ''; + html += ''; + html += ''; + return html + ''; } function renderPendingGuests(page, users) { if (users.length) { - page.querySelector(".sectionPendingGuests").classList.remove("hide"); + page.querySelector('.sectionPendingGuests').classList.remove('hide'); } else { - page.querySelector(".sectionPendingGuests").classList.add("hide"); + page.querySelector('.sectionPendingGuests').classList.add('hide'); } - page.querySelector(".pending").innerHTML = users.map(getPendingUserHtml).join(""); + page.querySelector('.pending').innerHTML = users.map(getPendingUserHtml).join(''); } // TODO cvium: maybe reuse for invitation system function cancelAuthorization(page, id) { loading.show(); ApiClient.ajax({ - type: "DELETE", - url: ApiClient.getUrl("Connect/Pending", { + type: 'DELETE', + url: ApiClient.getUrl('Connect/Pending', { Id: id }) }).then(function () { @@ -236,34 +236,34 @@ define(["loading", "dom", "globalize", "date-fns", "dfnshelper", "paper-icon-but } function showInvitePopup(page) { - require(["components/guestinviter/guestinviter"], function (guestinviter) { + require(['components/guestinviter/guestinviter'], function (guestinviter) { guestinviter.show().then(function () { loadData(page); }); }); } - pageIdOn("pageinit", "userProfilesPage", function () { + pageIdOn('pageinit', 'userProfilesPage', function () { var page = this; - page.querySelector(".btnAddUser").addEventListener("click", function() { - Dashboard.navigate("usernew.html"); + page.querySelector('.btnAddUser').addEventListener('click', function() { + Dashboard.navigate('usernew.html'); }); - page.querySelector(".localUsers").addEventListener("click", function (e__e) { - var btnUserMenu = dom.parentWithClass(e__e.target, "btnUserMenu"); + page.querySelector('.localUsers').addEventListener('click', function (e__e) { + var btnUserMenu = dom.parentWithClass(e__e.target, 'btnUserMenu'); if (btnUserMenu) { showUserMenu(btnUserMenu); } }); - page.querySelector(".pending").addEventListener("click", function (e__r) { - var btnUserMenu = dom.parentWithClass(e__r.target, "btnUserMenu"); + page.querySelector('.pending').addEventListener('click', function (e__r) { + var btnUserMenu = dom.parentWithClass(e__r.target, 'btnUserMenu'); if (btnUserMenu) { showPendingUserMenu(btnUserMenu); } }); }); - pageIdOn("pagebeforeshow", "userProfilesPage", function () { + pageIdOn('pagebeforeshow', 'userProfilesPage', function () { loadData(this); }); }); diff --git a/src/controllers/device.js b/src/controllers/device.js deleted file mode 100644 index cfe7efbe73..0000000000 --- a/src/controllers/device.js +++ /dev/null @@ -1,50 +0,0 @@ -define(["loading", "libraryMenu", "dom", "emby-input", "emby-button"], function (loading, libraryMenu, dom) { - "use strict"; - - function load(page, device, deviceOptions) { - page.querySelector("#txtCustomName", page).value = deviceOptions.CustomName || ""; - page.querySelector(".reportedName", page).innerHTML = device.Name || ""; - } - - function loadData() { - var page = this; - loading.show(); - var id = getParameterByName("id"); - var promise1 = ApiClient.getJSON(ApiClient.getUrl("Devices/Info", { - Id: id - })); - var promise2 = ApiClient.getJSON(ApiClient.getUrl("Devices/Options", { - Id: id - })); - Promise.all([promise1, promise2]).then(function (responses) { - load(page, responses[0], responses[1]); - loading.hide(); - }); - } - - function save(page) { - var id = getParameterByName("id"); - ApiClient.ajax({ - url: ApiClient.getUrl("Devices/Options", { - Id: id - }), - type: "POST", - data: JSON.stringify({ - CustomName: page.querySelector("#txtCustomName").value - }), - contentType: "application/json" - }).then(Dashboard.processServerConfigurationUpdateResult); - } - - function onSubmit(e) { - var form = this; - save(dom.parentWithClass(form, "page")); - e.preventDefault(); - return false; - } - - return function (view, params) { - view.querySelector("form").addEventListener("submit", onSubmit); - view.addEventListener("viewshow", loadData); - }; -}); diff --git a/src/controllers/dlnaprofile.js b/src/controllers/dlnaprofile.js deleted file mode 100644 index ca0d3afdb1..0000000000 --- a/src/controllers/dlnaprofile.js +++ /dev/null @@ -1,829 +0,0 @@ -define(["jQuery", "loading", "fnchecked", "emby-select", "emby-button", "emby-input", "emby-checkbox", "listViewStyle", "emby-button"], function ($, loading) { - "use strict"; - - function loadProfile(page) { - loading.show(); - var promise1 = getProfile(); - var promise2 = ApiClient.getUsers(); - Promise.all([promise1, promise2]).then(function (responses) { - currentProfile = responses[0]; - renderProfile(page, currentProfile, responses[1]); - loading.hide(); - }); - } - - function getProfile() { - var id = getParameterByName("id"); - var url = id ? "Dlna/Profiles/" + id : "Dlna/Profiles/Default"; - return ApiClient.getJSON(ApiClient.getUrl(url)); - } - - function renderProfile(page, profile, users) { - $("#txtName", page).val(profile.Name); - $(".chkMediaType", page).each(function () { - this.checked = -1 != (profile.SupportedMediaTypes || "").split(",").indexOf(this.getAttribute("data-value")); - }); - $("#chkEnableAlbumArtInDidl", page).checked(profile.EnableAlbumArtInDidl); - $("#chkEnableSingleImageLimit", page).checked(profile.EnableSingleAlbumArtLimit); - renderXmlDocumentAttributes(page, profile.XmlRootAttributes || []); - var idInfo = profile.Identification || {}; - renderIdentificationHeaders(page, idInfo.Headers || []); - renderSubtitleProfiles(page, profile.SubtitleProfiles || []); - $("#txtInfoFriendlyName", page).val(profile.FriendlyName || ""); - $("#txtInfoModelName", page).val(profile.ModelName || ""); - $("#txtInfoModelNumber", page).val(profile.ModelNumber || ""); - $("#txtInfoModelDescription", page).val(profile.ModelDescription || ""); - $("#txtInfoModelUrl", page).val(profile.ModelUrl || ""); - $("#txtInfoManufacturer", page).val(profile.Manufacturer || ""); - $("#txtInfoManufacturerUrl", page).val(profile.ManufacturerUrl || ""); - $("#txtInfoSerialNumber", page).val(profile.SerialNumber || ""); - $("#txtIdFriendlyName", page).val(idInfo.FriendlyName || ""); - $("#txtIdModelName", page).val(idInfo.ModelName || ""); - $("#txtIdModelNumber", page).val(idInfo.ModelNumber || ""); - $("#txtIdModelDescription", page).val(idInfo.ModelDescription || ""); - $("#txtIdModelUrl", page).val(idInfo.ModelUrl || ""); - $("#txtIdManufacturer", page).val(idInfo.Manufacturer || ""); - $("#txtIdManufacturerUrl", page).val(idInfo.ManufacturerUrl || ""); - $("#txtIdSerialNumber", page).val(idInfo.SerialNumber || ""); - $("#txtIdDeviceDescription", page).val(idInfo.DeviceDescription || ""); - $("#txtAlbumArtPn", page).val(profile.AlbumArtPn || ""); - $("#txtAlbumArtMaxWidth", page).val(profile.MaxAlbumArtWidth || ""); - $("#txtAlbumArtMaxHeight", page).val(profile.MaxAlbumArtHeight || ""); - $("#txtIconMaxWidth", page).val(profile.MaxIconWidth || ""); - $("#txtIconMaxHeight", page).val(profile.MaxIconHeight || ""); - $("#chkIgnoreTranscodeByteRangeRequests", page).checked(profile.IgnoreTranscodeByteRangeRequests); - $("#txtMaxAllowedBitrate", page).val(profile.MaxStreamingBitrate || ""); - $("#txtMusicStreamingTranscodingBitrate", page).val(profile.MusicStreamingTranscodingBitrate || ""); - $("#chkRequiresPlainFolders", page).checked(profile.RequiresPlainFolders); - $("#chkRequiresPlainVideoItems", page).checked(profile.RequiresPlainVideoItems); - $("#txtProtocolInfo", page).val(profile.ProtocolInfo || ""); - $("#txtXDlnaCap", page).val(profile.XDlnaCap || ""); - $("#txtXDlnaDoc", page).val(profile.XDlnaDoc || ""); - $("#txtSonyAggregationFlags", page).val(profile.SonyAggregationFlags || ""); - profile.DirectPlayProfiles = profile.DirectPlayProfiles || []; - profile.TranscodingProfiles = profile.TranscodingProfiles || []; - profile.ContainerProfiles = profile.ContainerProfiles || []; - profile.CodecProfiles = profile.CodecProfiles || []; - profile.ResponseProfiles = profile.ResponseProfiles || []; - var usersHtml = "" + users.map(function (u) { - return '"; - }).join(""); - $("#selectUser", page).html(usersHtml).val(profile.UserId || ""); - renderSubProfiles(page, profile); - } - - function renderIdentificationHeaders(page, headers) { - var index = 0; - var html = '
' + headers.map(function (h) { - var li = '
'; - li += 'info'; - li += '
'; - li += '

' + h.Name + ": " + (h.Value || "") + "

"; - li += '
' + (h.Match || "") + "
"; - li += "
"; - li += ''; - li += "
"; - index++; - return li; - }).join("") + "
"; - var elem = $(".httpHeaderIdentificationList", page).html(html).trigger("create"); - $(".btnDeleteIdentificationHeader", elem).on("click", function () { - var itemIndex = parseInt(this.getAttribute("data-index")); - currentProfile.Identification.Headers.splice(itemIndex, 1); - renderIdentificationHeaders(page, currentProfile.Identification.Headers); - }); - } - - function openPopup(elem) { - elem.classList.remove("hide"); - } - - function closePopup(elem) { - elem.classList.add("hide"); - } - - function editIdentificationHeader(page, header) { - isSubProfileNew = null == header; - header = header || {}; - currentSubProfile = header; - var popup = $("#identificationHeaderPopup", page); - $("#txtIdentificationHeaderName", popup).val(header.Name || ""); - $("#txtIdentificationHeaderValue", popup).val(header.Value || ""); - $("#selectMatchType", popup).val(header.Match || "Equals"); - openPopup(popup[0]); - } - - function saveIdentificationHeader(page) { - currentSubProfile.Name = $("#txtIdentificationHeaderName", page).val(); - currentSubProfile.Value = $("#txtIdentificationHeaderValue", page).val(); - currentSubProfile.Match = $("#selectMatchType", page).val(); - - if (isSubProfileNew) { - currentProfile.Identification = currentProfile.Identification || {}; - currentProfile.Identification.Headers = currentProfile.Identification.Headers || []; - currentProfile.Identification.Headers.push(currentSubProfile); - } - - renderIdentificationHeaders(page, currentProfile.Identification.Headers); - currentSubProfile = null; - closePopup($("#identificationHeaderPopup", page)[0]); - } - - function renderXmlDocumentAttributes(page, attribute) { - var html = '
' + attribute.map(function (h) { - var li = '
'; - li += 'info'; - li += '
'; - li += '

' + h.Name + " = " + (h.Value || "") + "

"; - li += "
"; - li += ''; - return li += "
"; - }).join("") + "
"; - var elem = $(".xmlDocumentAttributeList", page).html(html).trigger("create"); - $(".btnDeleteXmlAttribute", elem).on("click", function () { - var itemIndex = parseInt(this.getAttribute("data-index")); - currentProfile.XmlRootAttributes.splice(itemIndex, 1); - renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes); - }); - } - - function editXmlDocumentAttribute(page, attribute) { - isSubProfileNew = null == attribute; - attribute = attribute || {}; - currentSubProfile = attribute; - var popup = $("#xmlAttributePopup", page); - $("#txtXmlAttributeName", popup).val(attribute.Name || ""); - $("#txtXmlAttributeValue", popup).val(attribute.Value || ""); - openPopup(popup[0]); - } - - function saveXmlDocumentAttribute(page) { - currentSubProfile.Name = $("#txtXmlAttributeName", page).val(); - currentSubProfile.Value = $("#txtXmlAttributeValue", page).val(); - - if (isSubProfileNew) { - currentProfile.XmlRootAttributes.push(currentSubProfile); - } - - renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes); - currentSubProfile = null; - closePopup($("#xmlAttributePopup", page)[0]); - } - - function renderSubtitleProfiles(page, profiles) { - var index = 0; - var html = '
' + profiles.map(function (h) { - var li = '
'; - li += 'info'; - li += '
'; - li += '

' + (h.Format || "") + "

"; - li += "
"; - li += ''; - li += "
"; - index++; - return li; - }).join("") + "
"; - var elem = $(".subtitleProfileList", page).html(html).trigger("create"); - $(".btnDeleteProfile", elem).on("click", function () { - var itemIndex = parseInt(this.getAttribute("data-index")); - currentProfile.SubtitleProfiles.splice(itemIndex, 1); - renderSubtitleProfiles(page, currentProfile.SubtitleProfiles); - }); - $(".lnkEditSubProfile", elem).on("click", function () { - var itemIndex = parseInt(this.getAttribute("data-index")); - editSubtitleProfile(page, currentProfile.SubtitleProfiles[itemIndex]); - }); - } - - function editSubtitleProfile(page, profile) { - isSubProfileNew = null == profile; - profile = profile || {}; - currentSubProfile = profile; - var popup = $("#subtitleProfilePopup", page); - $("#txtSubtitleProfileFormat", popup).val(profile.Format || ""); - $("#selectSubtitleProfileMethod", popup).val(profile.Method || ""); - $("#selectSubtitleProfileDidlMode", popup).val(profile.DidlMode || ""); - openPopup(popup[0]); - } - - function saveSubtitleProfile(page) { - currentSubProfile.Format = $("#txtSubtitleProfileFormat", page).val(); - currentSubProfile.Method = $("#selectSubtitleProfileMethod", page).val(); - currentSubProfile.DidlMode = $("#selectSubtitleProfileDidlMode", page).val(); - - if (isSubProfileNew) { - currentProfile.SubtitleProfiles.push(currentSubProfile); - } - - renderSubtitleProfiles(page, currentProfile.SubtitleProfiles); - currentSubProfile = null; - closePopup($("#subtitleProfilePopup", page)[0]); - } - - function renderSubProfiles(page, profile) { - renderDirectPlayProfiles(page, profile.DirectPlayProfiles); - renderTranscodingProfiles(page, profile.TranscodingProfiles); - renderContainerProfiles(page, profile.ContainerProfiles); - renderCodecProfiles(page, profile.CodecProfiles); - renderResponseProfiles(page, profile.ResponseProfiles); - } - - function saveDirectPlayProfile(page) { - currentSubProfile.Type = $("#selectDirectPlayProfileType", page).val(); - currentSubProfile.Container = $("#txtDirectPlayContainer", page).val(); - currentSubProfile.AudioCodec = $("#txtDirectPlayAudioCodec", page).val(); - currentSubProfile.VideoCodec = $("#txtDirectPlayVideoCodec", page).val(); - - if (isSubProfileNew) { - currentProfile.DirectPlayProfiles.push(currentSubProfile); - } - - renderSubProfiles(page, currentProfile); - currentSubProfile = null; - closePopup($("#popupEditDirectPlayProfile", page)[0]); - } - - function renderDirectPlayProfiles(page, profiles) { - var html = ""; - html += '"; - var elem = $(".directPlayProfiles", page).html(html).trigger("create"); - $(".btnDeleteProfile", elem).on("click", function () { - var index = this.getAttribute("data-profileindex"); - deleteDirectPlayProfile(page, index); - }); - $(".lnkEditSubProfile", elem).on("click", function () { - var index = parseInt(this.getAttribute("data-profileindex")); - editDirectPlayProfile(page, currentProfile.DirectPlayProfiles[index]); - }); - } - - function deleteDirectPlayProfile(page, index) { - currentProfile.DirectPlayProfiles.splice(index, 1); - renderDirectPlayProfiles(page, currentProfile.DirectPlayProfiles); - } - - function editDirectPlayProfile(page, directPlayProfile) { - isSubProfileNew = null == directPlayProfile; - directPlayProfile = directPlayProfile || {}; - currentSubProfile = directPlayProfile; - var popup = $("#popupEditDirectPlayProfile", page); - $("#selectDirectPlayProfileType", popup).val(directPlayProfile.Type || "Video").trigger("change"); - $("#txtDirectPlayContainer", popup).val(directPlayProfile.Container || ""); - $("#txtDirectPlayAudioCodec", popup).val(directPlayProfile.AudioCodec || ""); - $("#txtDirectPlayVideoCodec", popup).val(directPlayProfile.VideoCodec || ""); - openPopup(popup[0]); - } - - function renderTranscodingProfiles(page, profiles) { - var html = ""; - html += '"; - var elem = $(".transcodingProfiles", page).html(html).trigger("create"); - $(".btnDeleteProfile", elem).on("click", function () { - var index = this.getAttribute("data-profileindex"); - deleteTranscodingProfile(page, index); - }); - $(".lnkEditSubProfile", elem).on("click", function () { - var index = parseInt(this.getAttribute("data-profileindex")); - editTranscodingProfile(page, currentProfile.TranscodingProfiles[index]); - }); - } - - function editTranscodingProfile(page, transcodingProfile) { - isSubProfileNew = null == transcodingProfile; - transcodingProfile = transcodingProfile || {}; - currentSubProfile = transcodingProfile; - var popup = $("#transcodingProfilePopup", page); - $("#selectTranscodingProfileType", popup).val(transcodingProfile.Type || "Video").trigger("change"); - $("#txtTranscodingContainer", popup).val(transcodingProfile.Container || ""); - $("#txtTranscodingAudioCodec", popup).val(transcodingProfile.AudioCodec || ""); - $("#txtTranscodingVideoCodec", popup).val(transcodingProfile.VideoCodec || ""); - $("#selectTranscodingProtocol", popup).val(transcodingProfile.Protocol || "Http"); - $("#chkEnableMpegtsM2TsMode", popup).checked(transcodingProfile.EnableMpegtsM2TsMode || false); - $("#chkEstimateContentLength", popup).checked(transcodingProfile.EstimateContentLength || false); - $("#chkReportByteRangeRequests", popup).checked("Bytes" == transcodingProfile.TranscodeSeekInfo); - $(".radioTabButton:first", popup).trigger("click"); - openPopup(popup[0]); - } - - function deleteTranscodingProfile(page, index) { - currentProfile.TranscodingProfiles.splice(index, 1); - renderTranscodingProfiles(page, currentProfile.TranscodingProfiles); - } - - function saveTranscodingProfile(page) { - currentSubProfile.Type = $("#selectTranscodingProfileType", page).val(); - currentSubProfile.Container = $("#txtTranscodingContainer", page).val(); - currentSubProfile.AudioCodec = $("#txtTranscodingAudioCodec", page).val(); - currentSubProfile.VideoCodec = $("#txtTranscodingVideoCodec", page).val(); - currentSubProfile.Protocol = $("#selectTranscodingProtocol", page).val(); - currentSubProfile.Context = "Streaming"; - currentSubProfile.EnableMpegtsM2TsMode = $("#chkEnableMpegtsM2TsMode", page).checked(); - currentSubProfile.EstimateContentLength = $("#chkEstimateContentLength", page).checked(); - currentSubProfile.TranscodeSeekInfo = $("#chkReportByteRangeRequests", page).checked() ? "Bytes" : "Auto"; - - if (isSubProfileNew) { - currentProfile.TranscodingProfiles.push(currentSubProfile); - } - - renderSubProfiles(page, currentProfile); - currentSubProfile = null; - closePopup($("#transcodingProfilePopup", page)[0]); - } - - function renderContainerProfiles(page, profiles) { - var html = ""; - html += '"; - var elem = $(".containerProfiles", page).html(html).trigger("create"); - $(".btnDeleteProfile", elem).on("click", function () { - var index = this.getAttribute("data-profileindex"); - deleteContainerProfile(page, index); - }); - $(".lnkEditSubProfile", elem).on("click", function () { - var index = parseInt(this.getAttribute("data-profileindex")); - editContainerProfile(page, currentProfile.ContainerProfiles[index]); - }); - } - - function deleteContainerProfile(page, index) { - currentProfile.ContainerProfiles.splice(index, 1); - renderContainerProfiles(page, currentProfile.ContainerProfiles); - } - - function editContainerProfile(page, containerProfile) { - isSubProfileNew = null == containerProfile; - containerProfile = containerProfile || {}; - currentSubProfile = containerProfile; - var popup = $("#containerProfilePopup", page); - $("#selectContainerProfileType", popup).val(containerProfile.Type || "Video").trigger("change"); - $("#txtContainerProfileContainer", popup).val(containerProfile.Container || ""); - $(".radioTabButton:first", popup).trigger("click"); - openPopup(popup[0]); - } - - function saveContainerProfile(page) { - currentSubProfile.Type = $("#selectContainerProfileType", page).val(); - currentSubProfile.Container = $("#txtContainerProfileContainer", page).val(); - - if (isSubProfileNew) { - currentProfile.ContainerProfiles.push(currentSubProfile); - } - - renderSubProfiles(page, currentProfile); - currentSubProfile = null; - closePopup($("#containerProfilePopup", page)[0]); - } - - function renderCodecProfiles(page, profiles) { - var html = ""; - html += '"; - var elem = $(".codecProfiles", page).html(html).trigger("create"); - $(".btnDeleteProfile", elem).on("click", function () { - var index = this.getAttribute("data-profileindex"); - deleteCodecProfile(page, index); - }); - $(".lnkEditSubProfile", elem).on("click", function () { - var index = parseInt(this.getAttribute("data-profileindex")); - editCodecProfile(page, currentProfile.CodecProfiles[index]); - }); - } - - function deleteCodecProfile(page, index) { - currentProfile.CodecProfiles.splice(index, 1); - renderCodecProfiles(page, currentProfile.CodecProfiles); - } - - function editCodecProfile(page, codecProfile) { - isSubProfileNew = null == codecProfile; - codecProfile = codecProfile || {}; - currentSubProfile = codecProfile; - var popup = $("#codecProfilePopup", page); - $("#selectCodecProfileType", popup).val(codecProfile.Type || "Video").trigger("change"); - $("#txtCodecProfileCodec", popup).val(codecProfile.Codec || ""); - $(".radioTabButton:first", popup).trigger("click"); - openPopup(popup[0]); - } - - function saveCodecProfile(page) { - currentSubProfile.Type = $("#selectCodecProfileType", page).val(); - currentSubProfile.Codec = $("#txtCodecProfileCodec", page).val(); - - if (isSubProfileNew) { - currentProfile.CodecProfiles.push(currentSubProfile); - } - - renderSubProfiles(page, currentProfile); - currentSubProfile = null; - closePopup($("#codecProfilePopup", page)[0]); - } - - function renderResponseProfiles(page, profiles) { - var html = ""; - html += '"; - var elem = $(".mediaProfiles", page).html(html).trigger("create"); - $(".btnDeleteProfile", elem).on("click", function () { - var index = this.getAttribute("data-profileindex"); - deleteResponseProfile(page, index); - }); - $(".lnkEditSubProfile", elem).on("click", function () { - var index = parseInt(this.getAttribute("data-profileindex")); - editResponseProfile(page, currentProfile.ResponseProfiles[index]); - }); - } - - function deleteResponseProfile(page, index) { - currentProfile.ResponseProfiles.splice(index, 1); - renderResponseProfiles(page, currentProfile.ResponseProfiles); - } - - function editResponseProfile(page, responseProfile) { - isSubProfileNew = null == responseProfile; - responseProfile = responseProfile || {}; - currentSubProfile = responseProfile; - var popup = $("#responseProfilePopup", page); - $("#selectResponseProfileType", popup).val(responseProfile.Type || "Video").trigger("change"); - $("#txtResponseProfileContainer", popup).val(responseProfile.Container || ""); - $("#txtResponseProfileAudioCodec", popup).val(responseProfile.AudioCodec || ""); - $("#txtResponseProfileVideoCodec", popup).val(responseProfile.VideoCodec || ""); - $(".radioTabButton:first", popup).trigger("click"); - openPopup(popup[0]); - } - - function saveResponseProfile(page) { - currentSubProfile.Type = $("#selectResponseProfileType", page).val(); - currentSubProfile.Container = $("#txtResponseProfileContainer", page).val(); - currentSubProfile.AudioCodec = $("#txtResponseProfileAudioCodec", page).val(); - currentSubProfile.VideoCodec = $("#txtResponseProfileVideoCodec", page).val(); - - if (isSubProfileNew) { - currentProfile.ResponseProfiles.push(currentSubProfile); - } - - renderSubProfiles(page, currentProfile); - currentSubProfile = null; - closePopup($("#responseProfilePopup", page)[0]); - } - - function saveProfile(page, profile) { - updateProfile(page, profile); - var id = getParameterByName("id"); - - if (id) { - ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("Dlna/Profiles/" + id), - data: JSON.stringify(profile), - contentType: "application/json" - }).then(function () { - require(["toast"], function (toast) { - toast("Settings saved."); - }); - }, Dashboard.processErrorResponse); - } else { - ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("Dlna/Profiles"), - data: JSON.stringify(profile), - contentType: "application/json" - }).then(function () { - Dashboard.navigate("dlnaprofiles.html"); - }, Dashboard.processErrorResponse); - } - - loading.hide(); - } - - function updateProfile(page, profile) { - profile.Name = $("#txtName", page).val(); - profile.EnableAlbumArtInDidl = $("#chkEnableAlbumArtInDidl", page).checked(); - profile.EnableSingleAlbumArtLimit = $("#chkEnableSingleImageLimit", page).checked(); - profile.SupportedMediaTypes = $(".chkMediaType:checked", page).get().map(function (c) { - return c.getAttribute("data-value"); - }).join(","); - profile.Identification = profile.Identification || {}; - profile.FriendlyName = $("#txtInfoFriendlyName", page).val(); - profile.ModelName = $("#txtInfoModelName", page).val(); - profile.ModelNumber = $("#txtInfoModelNumber", page).val(); - profile.ModelDescription = $("#txtInfoModelDescription", page).val(); - profile.ModelUrl = $("#txtInfoModelUrl", page).val(); - profile.Manufacturer = $("#txtInfoManufacturer", page).val(); - profile.ManufacturerUrl = $("#txtInfoManufacturerUrl", page).val(); - profile.SerialNumber = $("#txtInfoSerialNumber", page).val(); - profile.Identification.FriendlyName = $("#txtIdFriendlyName", page).val(); - profile.Identification.ModelName = $("#txtIdModelName", page).val(); - profile.Identification.ModelNumber = $("#txtIdModelNumber", page).val(); - profile.Identification.ModelDescription = $("#txtIdModelDescription", page).val(); - profile.Identification.ModelUrl = $("#txtIdModelUrl", page).val(); - profile.Identification.Manufacturer = $("#txtIdManufacturer", page).val(); - profile.Identification.ManufacturerUrl = $("#txtIdManufacturerUrl", page).val(); - profile.Identification.SerialNumber = $("#txtIdSerialNumber", page).val(); - profile.Identification.DeviceDescription = $("#txtIdDeviceDescription", page).val(); - profile.AlbumArtPn = $("#txtAlbumArtPn", page).val(); - profile.MaxAlbumArtWidth = $("#txtAlbumArtMaxWidth", page).val(); - profile.MaxAlbumArtHeight = $("#txtAlbumArtMaxHeight", page).val(); - profile.MaxIconWidth = $("#txtIconMaxWidth", page).val(); - profile.MaxIconHeight = $("#txtIconMaxHeight", page).val(); - profile.RequiresPlainFolders = $("#chkRequiresPlainFolders", page).checked(); - profile.RequiresPlainVideoItems = $("#chkRequiresPlainVideoItems", page).checked(); - profile.IgnoreTranscodeByteRangeRequests = $("#chkIgnoreTranscodeByteRangeRequests", page).checked(); - profile.MaxStreamingBitrate = $("#txtMaxAllowedBitrate", page).val(); - profile.MusicStreamingTranscodingBitrate = $("#txtMusicStreamingTranscodingBitrate", page).val(); - profile.ProtocolInfo = $("#txtProtocolInfo", page).val(); - profile.XDlnaCap = $("#txtXDlnaCap", page).val(); - profile.XDlnaDoc = $("#txtXDlnaDoc", page).val(); - profile.SonyAggregationFlags = $("#txtSonyAggregationFlags", page).val(); - profile.UserId = $("#selectUser", page).val(); - } - - var currentProfile; - var currentSubProfile; - var isSubProfileNew; - var allText = Globalize.translate("LabelAll"); - - $(document).on("pageinit", "#dlnaProfilePage", function () { - var page = this; - $(".radioTabButton", page).on("click", function () { - $(this).siblings().removeClass("ui-btn-active"); - $(this).addClass("ui-btn-active"); - var value = "A" == this.tagName ? this.getAttribute("data-value") : this.value; - var elem = $("." + value, page); - elem.siblings(".tabContent").hide(); - elem.show(); - }); - $("#selectDirectPlayProfileType", page).on("change", function () { - if ("Video" == this.value) { - $("#fldDirectPlayVideoCodec", page).show(); - } else { - $("#fldDirectPlayVideoCodec", page).hide(); - } - - if ("Photo" == this.value) { - $("#fldDirectPlayAudioCodec", page).hide(); - } else { - $("#fldDirectPlayAudioCodec", page).show(); - } - }); - $("#selectTranscodingProfileType", page).on("change", function () { - if ("Video" == this.value) { - $("#fldTranscodingVideoCodec", page).show(); - $("#fldTranscodingProtocol", page).show(); - $("#fldEnableMpegtsM2TsMode", page).show(); - } else { - $("#fldTranscodingVideoCodec", page).hide(); - $("#fldTranscodingProtocol", page).hide(); - $("#fldEnableMpegtsM2TsMode", page).hide(); - } - - if ("Photo" == this.value) { - $("#fldTranscodingAudioCodec", page).hide(); - $("#fldEstimateContentLength", page).hide(); - $("#fldReportByteRangeRequests", page).hide(); - } else { - $("#fldTranscodingAudioCodec", page).show(); - $("#fldEstimateContentLength", page).show(); - $("#fldReportByteRangeRequests", page).show(); - } - }); - $("#selectResponseProfileType", page).on("change", function () { - if ("Video" == this.value) { - $("#fldResponseProfileVideoCodec", page).show(); - } else { - $("#fldResponseProfileVideoCodec", page).hide(); - } - - if ("Photo" == this.value) { - $("#fldResponseProfileAudioCodec", page).hide(); - } else { - $("#fldResponseProfileAudioCodec", page).show(); - } - }); - $(".btnAddDirectPlayProfile", page).on("click", function () { - editDirectPlayProfile(page); - }); - $(".btnAddTranscodingProfile", page).on("click", function () { - editTranscodingProfile(page); - }); - $(".btnAddContainerProfile", page).on("click", function () { - editContainerProfile(page); - }); - $(".btnAddCodecProfile", page).on("click", function () { - editCodecProfile(page); - }); - $(".btnAddResponseProfile", page).on("click", function () { - editResponseProfile(page); - }); - $(".btnAddIdentificationHttpHeader", page).on("click", function () { - editIdentificationHeader(page); - }); - $(".btnAddXmlDocumentAttribute", page).on("click", function () { - editXmlDocumentAttribute(page); - }); - $(".btnAddSubtitleProfile", page).on("click", function () { - editSubtitleProfile(page); - }); - $(".dlnaProfileForm").off("submit", DlnaProfilePage.onSubmit).on("submit", DlnaProfilePage.onSubmit); - $(".editDirectPlayProfileForm").off("submit", DlnaProfilePage.onDirectPlayFormSubmit).on("submit", DlnaProfilePage.onDirectPlayFormSubmit); - $(".transcodingProfileForm").off("submit", DlnaProfilePage.onTranscodingProfileFormSubmit).on("submit", DlnaProfilePage.onTranscodingProfileFormSubmit); - $(".containerProfileForm").off("submit", DlnaProfilePage.onContainerProfileFormSubmit).on("submit", DlnaProfilePage.onContainerProfileFormSubmit); - $(".codecProfileForm").off("submit", DlnaProfilePage.onCodecProfileFormSubmit).on("submit", DlnaProfilePage.onCodecProfileFormSubmit); - $(".editResponseProfileForm").off("submit", DlnaProfilePage.onResponseProfileFormSubmit).on("submit", DlnaProfilePage.onResponseProfileFormSubmit); - $(".identificationHeaderForm").off("submit", DlnaProfilePage.onIdentificationHeaderFormSubmit).on("submit", DlnaProfilePage.onIdentificationHeaderFormSubmit); - $(".xmlAttributeForm").off("submit", DlnaProfilePage.onXmlAttributeFormSubmit).on("submit", DlnaProfilePage.onXmlAttributeFormSubmit); - $(".subtitleProfileForm").off("submit", DlnaProfilePage.onSubtitleProfileFormSubmit).on("submit", DlnaProfilePage.onSubtitleProfileFormSubmit); - }).on("pageshow", "#dlnaProfilePage", function () { - var page = this; - $("#radioInfo", page).trigger("click"); - loadProfile(page); - }); - window.DlnaProfilePage = { - onSubmit: function () { - loading.show(); - saveProfile($(this).parents(".page"), currentProfile); - return false; - }, - onDirectPlayFormSubmit: function () { - saveDirectPlayProfile($(this).parents(".page")); - return false; - }, - onTranscodingProfileFormSubmit: function () { - saveTranscodingProfile($(this).parents(".page")); - return false; - }, - onContainerProfileFormSubmit: function () { - saveContainerProfile($(this).parents(".page")); - return false; - }, - onCodecProfileFormSubmit: function () { - saveCodecProfile($(this).parents(".page")); - return false; - }, - onResponseProfileFormSubmit: function () { - saveResponseProfile($(this).parents(".page")); - return false; - }, - onIdentificationHeaderFormSubmit: function () { - saveIdentificationHeader($(this).parents(".page")); - return false; - }, - onXmlAttributeFormSubmit: function () { - saveXmlDocumentAttribute($(this).parents(".page")); - return false; - }, - onSubtitleProfileFormSubmit: function () { - saveSubtitleProfile($(this).parents(".page")); - return false; - } - }; -}); diff --git a/src/controllers/dlnaprofiles.js b/src/controllers/dlnaprofiles.js deleted file mode 100644 index e1719ea597..0000000000 --- a/src/controllers/dlnaprofiles.js +++ /dev/null @@ -1,89 +0,0 @@ -define(["jQuery", "globalize", "loading", "libraryMenu", "listViewStyle", "emby-button"], function ($, globalize, loading, libraryMenu) { - "use strict"; - - function loadProfiles(page) { - loading.show(); - ApiClient.getJSON(ApiClient.getUrl("Dlna/ProfileInfos")).then(function (result) { - renderUserProfiles(page, result); - renderSystemProfiles(page, result); - loading.hide(); - }); - } - - function renderUserProfiles(page, profiles) { - renderProfiles(page, page.querySelector(".customProfiles"), profiles.filter(function (p) { - return "User" == p.Type; - })); - } - - function renderSystemProfiles(page, profiles) { - renderProfiles(page, page.querySelector(".systemProfiles"), profiles.filter(function (p) { - return "System" == p.Type; - })); - } - - function renderProfiles(page, element, profiles) { - var html = ""; - - if (profiles.length) { - html += '
'; - } - - for (var i = 0, length = profiles.length; i < length; i++) { - var profile = profiles[i]; - html += '
'; - html += ''; - html += '"; - - if ("User" == profile.Type) { - html += ''; - } - - html += "
"; - } - - if (profiles.length) { - html += "
"; - } - - element.innerHTML = html; - $(".btnDeleteProfile", element).on("click", function () { - var id = this.getAttribute("data-profileid"); - deleteProfile(page, id); - }); - } - - function deleteProfile(page, id) { - require(["confirm"], function (confirm) { - confirm(globalize.translate("MessageConfirmProfileDeletion"), globalize.translate("HeaderConfirmProfileDeletion")).then(function () { - loading.show(); - ApiClient.ajax({ - type: "DELETE", - url: ApiClient.getUrl("Dlna/Profiles/" + id) - }).then(function () { - loading.hide(); - loadProfiles(page); - }); - }); - }); - } - - function getTabs() { - return [{ - href: "dlnasettings.html", - name: globalize.translate("TabSettings") - }, { - href: "dlnaprofiles.html", - name: globalize.translate("TabProfiles") - }]; - } - - $(document).on("pageshow", "#dlnaProfilesPage", function () { - libraryMenu.setTabs("dlna", 1, getTabs); - loadProfiles(this); - }); -}); diff --git a/src/controllers/dlnasettings.js b/src/controllers/dlnasettings.js deleted file mode 100644 index fbb3af1205..0000000000 --- a/src/controllers/dlnasettings.js +++ /dev/null @@ -1,56 +0,0 @@ -define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, libraryMenu) { - "use strict"; - - function loadPage(page, config, users) { - page.querySelector("#chkEnablePlayTo").checked = config.EnablePlayTo; - page.querySelector("#chkEnableDlnaDebugLogging").checked = config.EnableDebugLog; - $("#txtClientDiscoveryInterval", page).val(config.ClientDiscoveryIntervalSeconds); - $("#chkEnableServer", page).checked(config.EnableServer); - $("#chkBlastAliveMessages", page).checked(config.BlastAliveMessages); - $("#txtBlastInterval", page).val(config.BlastAliveMessageIntervalSeconds); - var usersHtml = users.map(function (u) { - return '"; - }).join(""); - $("#selectUser", page).html(usersHtml).val(config.DefaultUserId || ""); - loading.hide(); - } - - function onSubmit() { - loading.show(); - var form = this; - ApiClient.getNamedConfiguration("dlna").then(function (config) { - config.EnablePlayTo = form.querySelector("#chkEnablePlayTo").checked; - config.EnableDebugLog = form.querySelector("#chkEnableDlnaDebugLogging").checked; - config.ClientDiscoveryIntervalSeconds = $("#txtClientDiscoveryInterval", form).val(); - config.EnableServer = $("#chkEnableServer", form).checked(); - config.BlastAliveMessages = $("#chkBlastAliveMessages", form).checked(); - config.BlastAliveMessageIntervalSeconds = $("#txtBlastInterval", form).val(); - config.DefaultUserId = $("#selectUser", form).val(); - ApiClient.updateNamedConfiguration("dlna", config).then(Dashboard.processServerConfigurationUpdateResult); - }); - return false; - } - - function getTabs() { - return [{ - href: "dlnasettings.html", - name: Globalize.translate("TabSettings") - }, { - href: "dlnaprofiles.html", - name: Globalize.translate("TabProfiles") - }]; - } - - $(document).on("pageinit", "#dlnaSettingsPage", function () { - $(".dlnaSettingsForm").off("submit", onSubmit).on("submit", onSubmit); - }).on("pageshow", "#dlnaSettingsPage", function () { - libraryMenu.setTabs("dlna", 0, getTabs); - loading.show(); - var page = this; - var promise1 = ApiClient.getNamedConfiguration("dlna"); - var promise2 = ApiClient.getUsers(); - Promise.all([promise1, promise2]).then(function (responses) { - loadPage(page, responses[0], responses[1]); - }); - }); -}); diff --git a/src/controllers/edititemmetadata.js b/src/controllers/edititemmetadata.js index aba741d64c..2bfc5e560d 100644 --- a/src/controllers/edititemmetadata.js +++ b/src/controllers/edititemmetadata.js @@ -1,25 +1,25 @@ -define(["loading", "scripts/editorsidebar"], function (loading) { - "use strict"; +define(['loading', 'scripts/editorsidebar'], function (loading) { + 'use strict'; function reload(context, itemId) { loading.show(); if (itemId) { - require(["metadataEditor"], function (metadataEditor) { - metadataEditor.embed(context.querySelector(".editPageInnerContent"), itemId, ApiClient.serverInfo().Id); + require(['metadataEditor'], function (metadataEditor) { + metadataEditor.embed(context.querySelector('.editPageInnerContent'), itemId, ApiClient.serverInfo().Id); }); } else { - context.querySelector(".editPageInnerContent").innerHTML = ""; + context.querySelector('.editPageInnerContent').innerHTML = ''; loading.hide(); } } return function (view, params) { - view.addEventListener("viewshow", function () { + view.addEventListener('viewshow', function () { reload(this, MetadataEditor.getCurrentItemId()); }); MetadataEditor.setCurrentItemId(null); - view.querySelector(".libraryTree").addEventListener("itemclicked", function (event) { + view.querySelector('.libraryTree').addEventListener('itemclicked', function (event) { var data = event.detail; if (data.id != MetadataEditor.getCurrentItemId()) { diff --git a/src/controllers/encodingsettings.js b/src/controllers/encodingsettings.js deleted file mode 100644 index 5a540a5e2f..0000000000 --- a/src/controllers/encodingsettings.js +++ /dev/null @@ -1,193 +0,0 @@ -define(["jQuery", "loading", "globalize", "dom", "libraryMenu"], function ($, loading, globalize, dom, libraryMenu) { - "use strict"; - - function loadPage(page, config, systemInfo) { - Array.prototype.forEach.call(page.querySelectorAll(".chkDecodeCodec"), function (c) { - c.checked = -1 !== (config.HardwareDecodingCodecs || []).indexOf(c.getAttribute("data-codec")); - }); - page.querySelector("#chkDecodingColorDepth10").checked = config.EnableDecodingColorDepth10; - page.querySelector("#chkHardwareEncoding").checked = config.EnableHardwareEncoding; - $("#selectVideoDecoder", page).val(config.HardwareAccelerationType); - $("#selectThreadCount", page).val(config.EncodingThreadCount); - $("#txtDownMixAudioBoost", page).val(config.DownMixAudioBoost); - page.querySelector(".txtEncoderPath").value = config.EncoderAppPathDisplay || ""; - $("#txtTranscodingTempPath", page).val(systemInfo.TranscodingTempPath || ""); - $("#txtVaapiDevice", page).val(config.VaapiDevice || ""); - page.querySelector("#selectEncoderPreset").value = config.EncoderPreset || ""; - page.querySelector("#txtH264Crf").value = config.H264Crf || ""; - page.querySelector("#selectDeinterlaceMethod").value = config.DeinterlaceMethod || ""; - page.querySelector("#chkEnableSubtitleExtraction").checked = config.EnableSubtitleExtraction || false; - page.querySelector("#chkEnableThrottling").checked = config.EnableThrottling || false; - page.querySelector("#selectVideoDecoder").dispatchEvent(new CustomEvent("change", { - bubbles: true - })); - loading.hide(); - } - - function onSaveEncodingPathFailure(response) { - loading.hide(); - var msg = ""; - msg = globalize.translate("FFmpegSavePathNotFound"); - - require(["alert"], function (alert) { - alert(msg); - }); - } - - function updateEncoder(form) { - return ApiClient.getSystemInfo().then(function (systemInfo) { - return ApiClient.ajax({ - url: ApiClient.getUrl("System/MediaEncoder/Path"), - type: "POST", - data: { - Path: form.querySelector(".txtEncoderPath").value, - PathType: "Custom" - } - }).then(Dashboard.processServerConfigurationUpdateResult, onSaveEncodingPathFailure); - }); - } - - function onSubmit() { - var form = this; - - var onDecoderConfirmed = function () { - loading.show(); - ApiClient.getNamedConfiguration("encoding").then(function (config) { - config.DownMixAudioBoost = $("#txtDownMixAudioBoost", form).val(); - config.TranscodingTempPath = $("#txtTranscodingTempPath", form).val(); - config.EncodingThreadCount = $("#selectThreadCount", form).val(); - config.HardwareAccelerationType = $("#selectVideoDecoder", form).val(); - config.VaapiDevice = $("#txtVaapiDevice", form).val(); - config.EncoderPreset = form.querySelector("#selectEncoderPreset").value; - config.H264Crf = parseInt(form.querySelector("#txtH264Crf").value || "0"); - config.DeinterlaceMethod = form.querySelector("#selectDeinterlaceMethod").value; - config.EnableSubtitleExtraction = form.querySelector("#chkEnableSubtitleExtraction").checked; - config.EnableThrottling = form.querySelector("#chkEnableThrottling").checked; - config.HardwareDecodingCodecs = Array.prototype.map.call(Array.prototype.filter.call(form.querySelectorAll(".chkDecodeCodec"), function (c) { - return c.checked; - }), function (c) { - return c.getAttribute("data-codec"); - }); - config.EnableDecodingColorDepth10 = form.querySelector("#chkDecodingColorDepth10").checked; - config.EnableHardwareEncoding = form.querySelector("#chkHardwareEncoding").checked; - ApiClient.updateNamedConfiguration("encoding", config).then(function () { - updateEncoder(form); - }, function () { - require(["alert"], function (alert) { - alert(globalize.translate("DefaultErrorMessage")); - }); - - Dashboard.processServerConfigurationUpdateResult(); - }); - }); - }; - - if ($("#selectVideoDecoder", form).val()) { - require(["alert"], function (alert) { - alert({ - title: globalize.translate("TitleHardwareAcceleration"), - text: globalize.translate("HardwareAccelerationWarning") - }).then(onDecoderConfirmed); - }); - } else { - onDecoderConfirmed(); - } - - return false; - } - - function setDecodingCodecsVisible(context, value) { - value = value || ""; - var any; - Array.prototype.forEach.call(context.querySelectorAll(".chkDecodeCodec"), function (c) { - if (-1 === c.getAttribute("data-types").split(",").indexOf(value)) { - dom.parentWithTag(c, "LABEL").classList.add("hide"); - } else { - dom.parentWithTag(c, "LABEL").classList.remove("hide"); - any = true; - } - }); - - if (any) { - context.querySelector(".decodingCodecsList").classList.remove("hide"); - } else { - context.querySelector(".decodingCodecsList").classList.add("hide"); - } - } - - function getTabs() { - return [{ - href: "encodingsettings.html", - name: Globalize.translate("Transcoding") - }, { - href: "playbackconfiguration.html", - name: Globalize.translate("TabResumeSettings") - }, { - href: "streamingsettings.html", - name: Globalize.translate("TabStreaming") - }]; - } - - $(document).on("pageinit", "#encodingSettingsPage", function () { - var page = this; - page.querySelector("#selectVideoDecoder").addEventListener("change", function () { - if ("vaapi" == this.value) { - page.querySelector(".fldVaapiDevice").classList.remove("hide"); - page.querySelector("#txtVaapiDevice").setAttribute("required", "required"); - } else { - page.querySelector(".fldVaapiDevice").classList.add("hide"); - page.querySelector("#txtVaapiDevice").removeAttribute("required"); - } - - if (this.value) { - page.querySelector(".hardwareAccelerationOptions").classList.remove("hide"); - } else { - page.querySelector(".hardwareAccelerationOptions").classList.add("hide"); - } - - setDecodingCodecsVisible(page, this.value); - }); - $("#btnSelectEncoderPath", page).on("click.selectDirectory", function () { - require(["directorybrowser"], function (directoryBrowser) { - var picker = new directoryBrowser(); - picker.show({ - includeFiles: true, - callback: function (path) { - if (path) { - $(".txtEncoderPath", page).val(path); - } - - picker.close(); - } - }); - }); - }); - $("#btnSelectTranscodingTempPath", page).on("click.selectDirectory", function () { - require(["directorybrowser"], function (directoryBrowser) { - var picker = new directoryBrowser(); - picker.show({ - callback: function (path) { - if (path) { - $("#txtTranscodingTempPath", page).val(path); - } - - picker.close(); - }, - validateWriteable: true, - header: globalize.translate("HeaderSelectTranscodingPath"), - instruction: globalize.translate("HeaderSelectTranscodingPathHelp") - }); - }); - }); - $(".encodingSettingsForm").off("submit", onSubmit).on("submit", onSubmit); - }).on("pageshow", "#encodingSettingsPage", function () { - loading.show(); - libraryMenu.setTabs("playback", 0, getTabs); - var page = this; - ApiClient.getNamedConfiguration("encoding").then(function (config) { - ApiClient.getSystemInfo().then(function (systemInfo) { - loadPage(page, config, systemInfo); - }); - }); - }); -}); diff --git a/src/controllers/favorites.js b/src/controllers/favorites.js index c06b0bd8fb..b4c7936239 100644 --- a/src/controllers/favorites.js +++ b/src/controllers/favorites.js @@ -1,26 +1,26 @@ -define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "apphost", "layoutManager", "focusManager", "emby-itemscontainer", "emby-scroller"], function (appRouter, cardBuilder, dom, globalize, connectionManager, appHost, layoutManager, focusManager) { - "use strict"; +define(['appRouter', 'cardBuilder', 'dom', 'globalize', 'connectionManager', 'apphost', 'layoutManager', 'focusManager', 'emby-itemscontainer', 'emby-scroller'], function (appRouter, cardBuilder, dom, globalize, connectionManager, appHost, layoutManager, focusManager) { + 'use strict'; function enableScrollX() { return true; } function getThumbShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop"; + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function getPosterShape() { - return enableScrollX() ? "overflowPortrait" : "portrait"; + return enableScrollX() ? 'overflowPortrait' : 'portrait'; } function getSquareShape() { - return enableScrollX() ? "overflowSquare" : "square"; + return enableScrollX() ? 'overflowSquare' : 'square'; } function getSections() { return [{ - name: "HeaderFavoriteMovies", - types: "Movie", + name: 'HeaderFavoriteMovies', + types: 'Movie', shape: getPosterShape(), showTitle: true, showYear: true, @@ -28,8 +28,8 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap overlayText: false, centerText: true }, { - name: "HeaderFavoriteShows", - types: "Series", + name: 'HeaderFavoriteShows', + types: 'Series', shape: getPosterShape(), showTitle: true, showYear: true, @@ -37,8 +37,8 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap overlayText: false, centerText: true }, { - name: "HeaderFavoriteEpisodes", - types: "Episode", + name: 'HeaderFavoriteEpisodes', + types: 'Episode', shape: getThumbShape(), preferThumb: false, showTitle: true, @@ -47,8 +47,8 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap overlayText: false, centerText: true }, { - name: "HeaderFavoriteVideos", - types: "Video", + name: 'HeaderFavoriteVideos', + types: 'Video', shape: getThumbShape(), preferThumb: true, showTitle: true, @@ -56,16 +56,16 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap overlayText: false, centerText: true }, { - name: "HeaderFavoriteCollections", - types: "BoxSet", + name: 'HeaderFavoriteCollections', + types: 'BoxSet', shape: getPosterShape(), showTitle: true, overlayPlayButton: true, overlayText: false, centerText: true }, { - name: "HeaderFavoritePlaylists", - types: "Playlist", + name: 'HeaderFavoritePlaylists', + types: 'Playlist', shape: getSquareShape(), preferThumb: false, showTitle: true, @@ -75,8 +75,8 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap overlayPlayButton: true, coverImage: true }, { - name: "HeaderFavoritePeople", - types: "Person", + name: 'HeaderFavoritePeople', + types: 'Person', shape: getPosterShape(), preferThumb: false, showTitle: true, @@ -86,8 +86,8 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap overlayPlayButton: true, coverImage: true }, { - name: "HeaderFavoriteArtists", - types: "MusicArtist", + name: 'HeaderFavoriteArtists', + types: 'MusicArtist', shape: getSquareShape(), preferThumb: false, showTitle: true, @@ -97,8 +97,8 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap overlayPlayButton: true, coverImage: true }, { - name: "HeaderFavoriteAlbums", - types: "MusicAlbum", + name: 'HeaderFavoriteAlbums', + types: 'MusicAlbum', shape: getSquareShape(), preferThumb: false, showTitle: true, @@ -108,8 +108,8 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap overlayPlayButton: true, coverImage: true }, { - name: "HeaderFavoriteSongs", - types: "Audio", + name: 'HeaderFavoriteSongs', + types: 'Audio', shape: getSquareShape(), preferThumb: false, showTitle: true, @@ -117,11 +117,11 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap showParentTitle: true, centerText: true, overlayMoreButton: true, - action: "instantmix", + action: 'instantmix', coverImage: true }, { - name: "HeaderFavoriteBooks", - types: "Book", + name: 'HeaderFavoriteBooks', + types: 'Book', shape: getPosterShape(), showTitle: true, showYear: true, @@ -135,23 +135,23 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap return function () { var apiClient = this.apiClient; var options = { - SortBy: (section.types, "SeriesName,SortName"), - SortOrder: "Ascending", - Filters: "IsFavorite", + SortBy: (section.types, 'SeriesName,SortName'), + SortOrder: 'Ascending', + Filters: 'IsFavorite', Recursive: true, - Fields: "PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo', CollapseBoxSetItems: false, - ExcludeLocationTypes: "Virtual", + ExcludeLocationTypes: 'Virtual', EnableTotalRecordCount: false }; options.Limit = 20; var userId = apiClient.getCurrentUserId(); - if ("MusicArtist" === section.types) { + if ('MusicArtist' === section.types) { return apiClient.getArtists(userId, options); } - if ("Person" === section.types) { + if ('Person' === section.types) { return apiClient.getPeople(userId, options); } @@ -161,7 +161,7 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap } function getRouteUrl(section, serverId) { - return appRouter.getRouteUrl("list", { + return appRouter.getRouteUrl('list', { serverId: serverId, itemTypes: section.types, isFavorite: true @@ -170,14 +170,14 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap function getItemsHtmlFn(section) { return function (items) { - var supportsImageAnalysis = appHost.supports("imageanalysis"); + var supportsImageAnalysis = appHost.supports('imageanalysis'); var cardLayout = (appHost.preferVisualCards || supportsImageAnalysis) && section.autoCardLayout && section.showTitle; cardLayout = false; var serverId = this.apiClient.serverId(); var leadingButtons = layoutManager.tv ? [{ - name: globalize.translate("All"), - id: "more", - icon: "favorite", + name: globalize.translate('All'), + id: 'more', + icon: 'favorite', routeUrl: getRouteUrl(section, serverId) }] : null; var lines = 0; @@ -220,7 +220,7 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap this.view = view; this.params = params; this.apiClient = connectionManager.currentApiClient(); - this.sectionsContainer = view.querySelector(".sections"); + this.sectionsContainer = view.querySelector('.sections'); createSections(this, this.sectionsContainer, this.apiClient); } @@ -228,50 +228,50 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap var i; var length; var sections = getSections(); - var html = ""; + var html = ''; for (i = 0, length = sections.length; i < length; i++) { var section = sections[i]; - var sectionClass = "verticalSection"; + var sectionClass = 'verticalSection'; if (!section.showTitle) { - sectionClass += " verticalSection-extrabottompadding"; + sectionClass += ' verticalSection-extrabottompadding'; } html += '
'; html += '
'; if (layoutManager.tv) { - html += '

' + globalize.translate(section.name) + "

"; + html += '

' + globalize.translate(section.name) + '

'; } else { html += ''; html += '

'; html += globalize.translate(section.name); - html += "

"; - html += ''; - html += "
"; + html += ''; + html += ''; + html += ''; } - html += "
"; + html += '
'; html += '
'; - html += ""; + html += ''; } elem.innerHTML = html; - var elems = elem.querySelectorAll(".itemsContainer"); + var elems = elem.querySelectorAll('.itemsContainer'); for (i = 0, length = elems.length; i < length; i++) { var itemsContainer = elems[i]; itemsContainer.fetchData = getFetchDataFn(sections[i]).bind(instance); itemsContainer.getItemsHtml = getItemsHtmlFn(sections[i]).bind(instance); - itemsContainer.parentContainer = dom.parentWithClass(itemsContainer, "verticalSection"); + itemsContainer.parentContainer = dom.parentWithClass(itemsContainer, 'verticalSection'); } } FavoritesTab.prototype.onResume = function (options) { var promises = (this.apiClient, []); var view = this.view; - var elems = this.sectionsContainer.querySelectorAll(".itemsContainer"); + var elems = this.sectionsContainer.querySelectorAll('.itemsContainer'); for (var i = 0, length = elems.length; i < length; i++) { promises.push(elems[i].resume(options)); @@ -285,7 +285,7 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap }; FavoritesTab.prototype.onPause = function () { - var elems = this.sectionsContainer.querySelectorAll(".itemsContainer"); + var elems = this.sectionsContainer.querySelectorAll('.itemsContainer'); for (var i = 0, length = elems.length; i < length; i++) { elems[i].pause(); @@ -296,7 +296,7 @@ define(["appRouter", "cardBuilder", "dom", "globalize", "connectionManager", "ap this.view = null; this.params = null; this.apiClient = null; - var elems = this.sectionsContainer.querySelectorAll(".itemsContainer"); + var elems = this.sectionsContainer.querySelectorAll('.itemsContainer'); for (var i = 0, length = elems.length; i < length; i++) { elems[i].fetchData = null; diff --git a/src/controllers/home.js b/src/controllers/home.js index b1dd70ebd6..9a4cea2227 100644 --- a/src/controllers/home.js +++ b/src/controllers/home.js @@ -1,11 +1,11 @@ -define(["tabbedView", "globalize", "require", "emby-tabs", "emby-button", "emby-scroller"], function (TabbedView, globalize, require) { - "use strict"; +define(['tabbedView', 'globalize', 'require', 'emby-tabs', 'emby-button', 'emby-scroller'], function (TabbedView, globalize, require) { + 'use strict'; function getTabs() { return [{ - name: globalize.translate("Home") + name: globalize.translate('Home') }, { - name: globalize.translate("Favorites") + name: globalize.translate('Favorites') }]; } @@ -21,18 +21,18 @@ define(["tabbedView", "globalize", "require", "emby-tabs", "emby-button", "emby- function getTabController(index) { if (null == index) { - throw new Error("index cannot be null"); + throw new Error('index cannot be null'); } var depends = []; switch (index) { case 0: - depends.push("controllers/hometab"); + depends.push('controllers/hometab'); break; case 1: - depends.push("controllers/favorites"); + depends.push('controllers/favorites'); } var instance = this; @@ -63,12 +63,12 @@ define(["tabbedView", "globalize", "require", "emby-tabs", "emby-button", "emby- HomeView.prototype.onPause = function () { TabbedView.prototype.onPause.call(this); - document.querySelector(".skinHeader").classList.remove("noHomeButtonHeader"); + document.querySelector('.skinHeader').classList.remove('noHomeButtonHeader'); }; HomeView.prototype.onResume = function (options) { TabbedView.prototype.onResume.call(this, options); - document.querySelector(".skinHeader").classList.add("noHomeButtonHeader"); + document.querySelector('.skinHeader').classList.add('noHomeButtonHeader'); }; return HomeView; diff --git a/src/controllers/hometab.js b/src/controllers/hometab.js index d2adcb2da2..8e2a1f92e7 100644 --- a/src/controllers/hometab.js +++ b/src/controllers/hometab.js @@ -1,12 +1,12 @@ -define(["userSettings", "loading", "connectionManager", "apphost", "layoutManager", "focusManager", "homeSections", "emby-itemscontainer"], function (userSettings, loading, connectionManager, appHost, layoutManager, focusManager, homeSections) { - "use strict"; +define(['userSettings', 'loading', 'connectionManager', 'apphost', 'layoutManager', 'focusManager', 'homeSections', 'emby-itemscontainer'], function (userSettings, loading, connectionManager, appHost, layoutManager, focusManager, homeSections) { + 'use strict'; function HomeTab(view, params) { this.view = view; this.params = params; this.apiClient = connectionManager.currentApiClient(); - this.sectionsContainer = view.querySelector(".sections"); - view.querySelector(".sections").addEventListener("settingschange", onHomeScreenSettingsChanged.bind(this)); + this.sectionsContainer = view.querySelector('.sections'); + view.querySelector('.sections').addEventListener('settingschange', onHomeScreenSettingsChanged.bind(this)); } function onHomeScreenSettingsChanged() { @@ -36,7 +36,7 @@ define(["userSettings", "loading", "connectionManager", "apphost", "layoutManage this.destroyHomeSections(); this.sectionsRendered = true; return apiClient.getCurrentUser().then(function (user) { - return homeSections.loadSections(view.querySelector(".sections"), apiClient, user, userSettings).then(function () { + return homeSections.loadSections(view.querySelector('.sections'), apiClient, user, userSettings).then(function () { if (options.autoFocus) { focusManager.autoFocus(view); } diff --git a/src/controllers/itemdetailpage.js b/src/controllers/itemDetails.js similarity index 59% rename from src/controllers/itemdetailpage.js rename to src/controllers/itemDetails.js index 178419e284..cbb8a1b43e 100644 --- a/src/controllers/itemdetailpage.js +++ b/src/controllers/itemDetails.js @@ -1,5 +1,5 @@ -define(["loading", "appRouter", "layoutManager", "connectionManager", "userSettings", "cardBuilder", "datetime", "mediaInfo", "backdrop", "listView", "itemContextMenu", "itemHelper", "dom", "indicators", "imageLoader", "libraryMenu", "globalize", "browser", "events", "playbackManager", "scrollStyles", "emby-itemscontainer", "emby-checkbox", "emby-button", "emby-playstatebutton", "emby-ratingbutton", "emby-scroller", "emby-select"], function (loading, appRouter, layoutManager, connectionManager, userSettings, cardBuilder, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, dom, indicators, imageLoader, libraryMenu, globalize, browser, events, playbackManager) { - "use strict"; +define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSettings', 'cardBuilder', 'datetime', 'mediaInfo', 'backdrop', 'listView', 'itemContextMenu', 'itemHelper', 'dom', 'indicators', 'imageLoader', 'libraryMenu', 'globalize', 'browser', 'events', 'playbackManager', 'scrollStyles', 'emby-itemscontainer', 'emby-checkbox', 'emby-button', 'emby-playstatebutton', 'emby-ratingbutton', 'emby-scroller', 'emby-select'], function (loading, appRouter, layoutManager, connectionManager, userSettings, cardBuilder, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, dom, indicators, imageLoader, libraryMenu, globalize, browser, events, playbackManager) { + 'use strict'; function getPromise(apiClient, params) { var id = params.id; @@ -24,19 +24,19 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti return apiClient.getArtist(params.musicartist, apiClient.getCurrentUserId()); } - throw new Error("Invalid request"); + throw new Error('Invalid request'); } function hideAll(page, className, show) { var i; var length; - var elems = page.querySelectorAll("." + className); + var elems = page.querySelectorAll('.' + className); for (i = 0, length = elems.length; i < length; i++) { if (show) { - elems[i].classList.remove("hide"); + elems[i].classList.remove('hide'); } else { - elems[i].classList.add("hide"); + elems[i].classList.add('hide'); } } } @@ -61,101 +61,101 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function getProgramScheduleHtml(items) { - var html = ""; + var html = ''; html += '
'; html += listView.getListViewHtml({ items: items, enableUserDataButtons: false, image: true, - imageSource: "channel", + imageSource: 'channel', showProgramDateTime: true, showChannel: false, mediaInfo: false, - action: "none", + action: 'none', moreButton: false, recordButton: false }); - return html += "
"; + return html += ''; } function renderSeriesTimerSchedule(page, apiClient, seriesTimerId) { apiClient.getLiveTvTimers({ UserId: apiClient.getCurrentUserId(), ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Thumb", - SortBy: "StartDate", + EnableImageTypes: 'Primary,Backdrop,Thumb', + SortBy: 'StartDate', EnableTotalRecordCount: false, EnableUserData: false, SeriesTimerId: seriesTimerId, - Fields: "ChannelInfo,ChannelImage" + Fields: 'ChannelInfo,ChannelImage' }).then(function (result) { if (result.Items.length && result.Items[0].SeriesTimerId != seriesTimerId) { result.Items = []; } var html = getProgramScheduleHtml(result.Items); - var scheduleTab = page.querySelector(".seriesTimerSchedule"); + var scheduleTab = page.querySelector('.seriesTimerSchedule'); scheduleTab.innerHTML = html; imageLoader.lazyChildren(scheduleTab); }); } function renderTimerEditor(page, item, apiClient, user) { - if ("Recording" !== item.Type || !user.Policy.EnableLiveTvManagement || !item.TimerId || "InProgress" !== item.Status) { - return void hideAll(page, "btnCancelTimer"); + if ('Recording' !== item.Type || !user.Policy.EnableLiveTvManagement || !item.TimerId || 'InProgress' !== item.Status) { + return void hideAll(page, 'btnCancelTimer'); } - hideAll(page, "btnCancelTimer", true); + hideAll(page, 'btnCancelTimer', true); } function renderSeriesTimerEditor(page, item, apiClient, user) { - if ("SeriesTimer" !== item.Type) { - return void hideAll(page, "btnCancelSeriesTimer"); + if ('SeriesTimer' !== item.Type) { + return void hideAll(page, 'btnCancelSeriesTimer'); } if (user.Policy.EnableLiveTvManagement) { - require(["seriesRecordingEditor"], function (seriesRecordingEditor) { + require(['seriesRecordingEditor'], function (seriesRecordingEditor) { seriesRecordingEditor.embed(item, apiClient.serverId(), { - context: page.querySelector(".seriesRecordingEditor") + context: page.querySelector('.seriesRecordingEditor') }); }); - page.querySelector(".seriesTimerScheduleSection").classList.remove("hide"); - hideAll(page, "btnCancelSeriesTimer", true); + page.querySelector('.seriesTimerScheduleSection').classList.remove('hide'); + hideAll(page, 'btnCancelSeriesTimer', true); return void renderSeriesTimerSchedule(page, apiClient, item.Id); } - page.querySelector(".seriesTimerScheduleSection").classList.add("hide"); - return void hideAll(page, "btnCancelSeriesTimer"); + page.querySelector('.seriesTimerScheduleSection').classList.add('hide'); + return void hideAll(page, 'btnCancelSeriesTimer'); } function renderTrackSelections(page, instance, item, forceReload) { - var select = page.querySelector(".selectSource"); + var select = page.querySelector('.selectSource'); - if (!item.MediaSources || !itemHelper.supportsMediaSourceSelection(item) || -1 === playbackManager.getSupportedCommands().indexOf("PlayMediaSource") || !playbackManager.canPlay(item)) { - page.querySelector(".trackSelections").classList.add("hide"); - select.innerHTML = ""; - page.querySelector(".selectVideo").innerHTML = ""; - page.querySelector(".selectAudio").innerHTML = ""; - page.querySelector(".selectSubtitles").innerHTML = ""; + if (!item.MediaSources || !itemHelper.supportsMediaSourceSelection(item) || -1 === playbackManager.getSupportedCommands().indexOf('PlayMediaSource') || !playbackManager.canPlay(item)) { + page.querySelector('.trackSelections').classList.add('hide'); + select.innerHTML = ''; + page.querySelector('.selectVideo').innerHTML = ''; + page.querySelector('.selectAudio').innerHTML = ''; + page.querySelector('.selectSubtitles').innerHTML = ''; return; } playbackManager.getPlaybackMediaSources(item).then(function (mediaSources) { instance._currentPlaybackMediaSources = mediaSources; - page.querySelector(".trackSelections").classList.remove("hide"); - select.setLabel(globalize.translate("LabelVersion")); + page.querySelector('.trackSelections').classList.remove('hide'); + select.setLabel(globalize.translate('LabelVersion')); var currentValue = select.value; var selectedId = mediaSources[0].Id; select.innerHTML = mediaSources.map(function (v) { - var selected = v.Id === selectedId ? " selected" : ""; - return '"; - }).join(""); + var selected = v.Id === selectedId ? ' selected' : ''; + return ''; + }).join(''); if (mediaSources.length > 1) { - page.querySelector(".selectSourceContainer").classList.remove("hide"); + page.querySelector('.selectSourceContainer').classList.remove('hide'); } else { - page.querySelector(".selectSourceContainer").classList.add("hide"); + page.querySelector('.selectSourceContainer').classList.add('hide'); } if (select.value !== currentValue || forceReload) { @@ -167,18 +167,18 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function renderVideoSelections(page, mediaSources) { - var mediaSourceId = page.querySelector(".selectSource").value; + var mediaSourceId = page.querySelector('.selectSource').value; var mediaSource = mediaSources.filter(function (m) { return m.Id === mediaSourceId; })[0]; var tracks = mediaSource.MediaStreams.filter(function (m) { - return "Video" === m.Type; + return 'Video' === m.Type; }); - var select = page.querySelector(".selectVideo"); - select.setLabel(globalize.translate("LabelVideo")); + var select = page.querySelector('.selectVideo'); + select.setLabel(globalize.translate('LabelVideo')); var selectedId = tracks.length ? tracks[0].Index : -1; select.innerHTML = tracks.map(function (v) { - var selected = v.Index === selectedId ? " selected" : ""; + var selected = v.Index === selectedId ? ' selected' : ''; var titleParts = []; var resolutionText = mediaInfo.getResolutionText(v); @@ -190,100 +190,100 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti titleParts.push(v.Codec.toUpperCase()); } - return '"; - }).join(""); - select.setAttribute("disabled", "disabled"); + return ''; + }).join(''); + select.setAttribute('disabled', 'disabled'); if (tracks.length) { - page.querySelector(".selectVideoContainer").classList.remove("hide"); + page.querySelector('.selectVideoContainer').classList.remove('hide'); } else { - page.querySelector(".selectVideoContainer").classList.add("hide"); + page.querySelector('.selectVideoContainer').classList.add('hide'); } } function renderAudioSelections(page, mediaSources) { - var mediaSourceId = page.querySelector(".selectSource").value; + var mediaSourceId = page.querySelector('.selectSource').value; var mediaSource = mediaSources.filter(function (m) { return m.Id === mediaSourceId; })[0]; var tracks = mediaSource.MediaStreams.filter(function (m) { - return "Audio" === m.Type; + return 'Audio' === m.Type; }); - var select = page.querySelector(".selectAudio"); - select.setLabel(globalize.translate("LabelAudio")); + var select = page.querySelector('.selectAudio'); + select.setLabel(globalize.translate('LabelAudio')); var selectedId = mediaSource.DefaultAudioStreamIndex; select.innerHTML = tracks.map(function (v) { - var selected = v.Index === selectedId ? " selected" : ""; - return '"; - }).join(""); + var selected = v.Index === selectedId ? ' selected' : ''; + return ''; + }).join(''); if (tracks.length > 1) { - select.removeAttribute("disabled"); + select.removeAttribute('disabled'); } else { - select.setAttribute("disabled", "disabled"); + select.setAttribute('disabled', 'disabled'); } if (tracks.length) { - page.querySelector(".selectAudioContainer").classList.remove("hide"); + page.querySelector('.selectAudioContainer').classList.remove('hide'); } else { - page.querySelector(".selectAudioContainer").classList.add("hide"); + page.querySelector('.selectAudioContainer').classList.add('hide'); } } function renderSubtitleSelections(page, mediaSources) { - var mediaSourceId = page.querySelector(".selectSource").value; + var mediaSourceId = page.querySelector('.selectSource').value; var mediaSource = mediaSources.filter(function (m) { return m.Id === mediaSourceId; })[0]; var tracks = mediaSource.MediaStreams.filter(function (m) { - return "Subtitle" === m.Type; + return 'Subtitle' === m.Type; }); - var select = page.querySelector(".selectSubtitles"); - select.setLabel(globalize.translate("LabelSubtitles")); + var select = page.querySelector('.selectSubtitles'); + select.setLabel(globalize.translate('LabelSubtitles')); var selectedId = null == mediaSource.DefaultSubtitleStreamIndex ? -1 : mediaSource.DefaultSubtitleStreamIndex; if (tracks.length) { - var selected = -1 === selectedId ? " selected" : ""; - select.innerHTML = '" + tracks.map(function (v) { - selected = v.Index === selectedId ? " selected" : ""; - return '"; - }).join(""); - page.querySelector(".selectSubtitlesContainer").classList.remove("hide"); + var selected = -1 === selectedId ? ' selected' : ''; + select.innerHTML = '' + tracks.map(function (v) { + selected = v.Index === selectedId ? ' selected' : ''; + return ''; + }).join(''); + page.querySelector('.selectSubtitlesContainer').classList.remove('hide'); } else { - select.innerHTML = ""; - page.querySelector(".selectSubtitlesContainer").classList.add("hide"); + select.innerHTML = ''; + page.querySelector('.selectSubtitlesContainer').classList.add('hide'); } } function reloadPlayButtons(page, item) { var canPlay = false; - if ("Program" == item.Type) { + if ('Program' == item.Type) { var now = new Date(); if (now >= datetime.parseISO8601Date(item.StartDate, true) && now < datetime.parseISO8601Date(item.EndDate, true)) { - hideAll(page, "btnPlay", true); + hideAll(page, 'btnPlay', true); canPlay = true; } else { - hideAll(page, "btnPlay"); + hideAll(page, 'btnPlay'); } - hideAll(page, "btnResume"); - hideAll(page, "btnInstantMix"); - hideAll(page, "btnShuffle"); + hideAll(page, 'btnResume'); + hideAll(page, 'btnInstantMix'); + hideAll(page, 'btnShuffle'); } else if (playbackManager.canPlay(item)) { - hideAll(page, "btnPlay", true); - var enableInstantMix = -1 !== ["Audio", "MusicAlbum", "MusicGenre", "MusicArtist"].indexOf(item.Type); - hideAll(page, "btnInstantMix", enableInstantMix); - var enableShuffle = item.IsFolder || -1 !== ["MusicAlbum", "MusicGenre", "MusicArtist"].indexOf(item.Type); - hideAll(page, "btnShuffle", enableShuffle); + hideAll(page, 'btnPlay', true); + var enableInstantMix = -1 !== ['Audio', 'MusicAlbum', 'MusicGenre', 'MusicArtist'].indexOf(item.Type); + hideAll(page, 'btnInstantMix', enableInstantMix); + var enableShuffle = item.IsFolder || -1 !== ['MusicAlbum', 'MusicGenre', 'MusicArtist'].indexOf(item.Type); + hideAll(page, 'btnShuffle', enableShuffle); canPlay = true; - hideAll(page, "btnResume", item.UserData && item.UserData.PlaybackPositionTicks > 0); + hideAll(page, 'btnResume', item.UserData && item.UserData.PlaybackPositionTicks > 0); } else { - hideAll(page, "btnPlay"); - hideAll(page, "btnResume"); - hideAll(page, "btnInstantMix"); - hideAll(page, "btnShuffle"); + hideAll(page, 'btnPlay'); + hideAll(page, 'btnResume'); + hideAll(page, 'btnInstantMix'); + hideAll(page, 'btnShuffle'); } return canPlay; @@ -292,30 +292,30 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti function reloadUserDataButtons(page, item) { var i; var length; - var btnPlaystates = page.querySelectorAll(".btnPlaystate"); + var btnPlaystates = page.querySelectorAll('.btnPlaystate'); for (i = 0, length = btnPlaystates.length; i < length; i++) { var btnPlaystate = btnPlaystates[i]; if (itemHelper.canMarkPlayed(item)) { - btnPlaystate.classList.remove("hide"); + btnPlaystate.classList.remove('hide'); btnPlaystate.setItem(item); } else { - btnPlaystate.classList.add("hide"); + btnPlaystate.classList.add('hide'); btnPlaystate.setItem(null); } } - var btnUserRatings = page.querySelectorAll(".btnUserRating"); + var btnUserRatings = page.querySelectorAll('.btnUserRating'); for (i = 0, length = btnUserRatings.length; i < length; i++) { var btnUserRating = btnUserRatings[i]; if (itemHelper.canRate(item)) { - btnUserRating.classList.remove("hide"); + btnUserRating.classList.remove('hide'); btnUserRating.setItem(item); } else { - btnUserRating.classList.add("hide"); + btnUserRating.classList.add('hide'); btnUserRating.setItem(null); } } @@ -328,15 +328,14 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti var artist = artists[i]; var href = appRouter.getRouteUrl(artist, { context: context, - itemType: "MusicArtist", + itemType: 'MusicArtist', serverId: serverId }); - html.push('' + artist.Name + ""); + html.push('' + artist.Name + ''); } - return html = html.join(" / "); + return html = html.join(' / '); } - function renderName(item, container, isStatic, context) { var parentRoute; var parentNameHtml = []; @@ -345,102 +344,117 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti if (item.AlbumArtists) { parentNameHtml.push(getArtistLinksHtml(item.AlbumArtists, item.ServerId, context)); parentNameLast = true; - } else if (item.ArtistItems && item.ArtistItems.length && "MusicVideo" === item.Type) { + } else if (item.ArtistItems && item.ArtistItems.length && 'MusicVideo' === item.Type) { parentNameHtml.push(getArtistLinksHtml(item.ArtistItems, item.ServerId, context)); parentNameLast = true; - } else if (item.SeriesName && "Episode" === item.Type) { + } else if (item.SeriesName && 'Episode' === item.Type) { parentRoute = appRouter.getRouteUrl({ Id: item.SeriesId, Name: item.SeriesName, - Type: "Series", + Type: 'Series', IsFolder: true, ServerId: item.ServerId }, { context: context }); - parentNameHtml.push('' + item.SeriesName + ""); + parentNameHtml.push('' + item.SeriesName + ''); } else if (item.IsSeries || item.EpisodeTitle) { parentNameHtml.push(item.Name); } - if (item.SeriesName && "Season" === item.Type) { + if (item.SeriesName && 'Season' === item.Type) { parentRoute = appRouter.getRouteUrl({ Id: item.SeriesId, Name: item.SeriesName, - Type: "Series", + Type: 'Series', IsFolder: true, ServerId: item.ServerId }, { context: context }); - parentNameHtml.push('' + item.SeriesName + ""); - } else if (null != item.ParentIndexNumber && "Episode" === item.Type) { + parentNameHtml.push('' + item.SeriesName + ''); + } else if (null != item.ParentIndexNumber && 'Episode' === item.Type) { parentRoute = appRouter.getRouteUrl({ Id: item.SeasonId, Name: item.SeasonName, - Type: "Season", + Type: 'Season', IsFolder: true, ServerId: item.ServerId }, { context: context }); - parentNameHtml.push('' + item.SeasonName + ""); + parentNameHtml.push('' + item.SeasonName + ''); } else if (null != item.ParentIndexNumber && item.IsSeries) { - parentNameHtml.push(item.SeasonName || "S" + item.ParentIndexNumber); - } else if (item.Album && item.AlbumId && ("MusicVideo" === item.Type || "Audio" === item.Type)) { + parentNameHtml.push(item.SeasonName || 'S' + item.ParentIndexNumber); + } else if (item.Album && item.AlbumId && ('MusicVideo' === item.Type || 'Audio' === item.Type)) { parentRoute = appRouter.getRouteUrl({ Id: item.AlbumId, Name: item.Album, - Type: "MusicAlbum", + Type: 'MusicAlbum', IsFolder: true, ServerId: item.ServerId }, { context: context }); - parentNameHtml.push('' + item.Album + ""); + parentNameHtml.push('' + item.Album + ''); } else if (item.Album) { parentNameHtml.push(item.Album); } - - var html = ""; + // FIXME: This whole section needs some refactoring, so it becames easier to scale across all form factors. See GH #1022 + var html = ''; + var tvShowHtml = parentNameHtml[0]; + var tvSeasonHtml = parentNameHtml[1]; if (parentNameHtml.length) { if (parentNameLast) { - html = '

' + parentNameHtml.join(" - ") + "

"; + // Music + if (layoutManager.mobile) { + html = '

' + parentNameHtml.join('
') + '

'; + } else { + html = '

' + parentNameHtml.join(' - ') + '

'; + } } else { - html = '

' + parentNameHtml.join(" - ") + "

"; + if (layoutManager.mobile) { + html = '

' + parentNameHtml.join('
') + '

'; + } else { + html = '

' + tvShowHtml + '

'; + } } } var name = itemHelper.getDisplayName(item, { includeParentInfo: false }); - var offset = parentNameLast ? ".25em" : ".5em"; + var offset = parentNameLast ? '.25em' : '.5em'; if (html && !parentNameLast) { - html += '

' + name + '

'; + if (!layoutManager.mobile && tvSeasonHtml) { + html += '

' + tvSeasonHtml + ' - ' + name + '

'; + } else { + html += '

' + name + '

'; + } } else { - html = '

' + name + "

" + html; + html = '

' + name + '

' + html; } if (item.OriginalTitle && item.OriginalTitle != item.Name) { - html += '

' + item.OriginalTitle + '

'; + html += '

' + item.OriginalTitle + '

'; } container.innerHTML = html; if (html.length) { - container.classList.remove("hide"); + container.classList.remove('hide'); } else { - container.classList.add("hide"); + container.classList.add('hide'); } } function setTrailerButtonVisibility(page, item) { - if ((item.LocalTrailerCount || item.RemoteTrailers && item.RemoteTrailers.length) && -1 !== playbackManager.getSupportedCommands().indexOf("PlayTrailers")) { - hideAll(page, "btnPlayTrailer", true); + if ((item.LocalTrailerCount || item.RemoteTrailers && item.RemoteTrailers.length) && -1 !== playbackManager.getSupportedCommands().indexOf('PlayTrailers')) { + hideAll(page, 'btnPlayTrailer', true); } else { - hideAll(page, "btnPlayTrailer"); + hideAll(page, 'btnPlayTrailer'); } } @@ -455,76 +469,76 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti function renderDetailPageBackdrop(page, item, apiClient) { var imgUrl; var hasbackdrop = false; - var itemBackdropElement = page.querySelector("#itemBackdrop"); - var usePrimaryImage = item.MediaType === "Video" && item.Type !== "Movie" && item.Type !== "Trailer" || - item.MediaType && item.MediaType !== "Video" || - item.Type === "MusicAlbum" || - item.Type === "Person"; + var itemBackdropElement = page.querySelector('#itemBackdrop'); + var usePrimaryImage = item.MediaType === 'Video' && item.Type !== 'Movie' && item.Type !== 'Trailer' || + item.MediaType && item.MediaType !== 'Video' || + item.Type === 'MusicAlbum' || + item.Type === 'Person'; - if (!layoutManager.mobile && !userSettings.enableBackdrops()) { + if (!layoutManager.mobile && !userSettings.detailsBanner()) { return false; } - if ("Program" === item.Type && item.ImageTags && item.ImageTags.Thumb) { + if ('Program' === item.Type && item.ImageTags && item.ImageTags.Thumb) { imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", + type: 'Thumb', maxWidth: dom.getScreenWidth(), index: 0, tag: item.ImageTags.Thumb }); - page.classList.remove("noBackdrop"); - imageLoader.lazyImage(itemBackdropElement, imgUrl, false); + page.classList.remove('noBackdrop'); + imageLoader.lazyImage(itemBackdropElement, imgUrl); hasbackdrop = true; } else if (usePrimaryImage && item.ImageTags && item.ImageTags.Primary) { imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Primary", + type: 'Primary', maxWidth: dom.getScreenWidth(), index: 0, tag: item.ImageTags.Primary }); - page.classList.remove("noBackdrop"); - imageLoader.lazyImage(itemBackdropElement, imgUrl, false); + page.classList.remove('noBackdrop'); + imageLoader.lazyImage(itemBackdropElement, imgUrl); hasbackdrop = true; } else if (item.BackdropImageTags && item.BackdropImageTags.length) { imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Backdrop", + type: 'Backdrop', maxWidth: dom.getScreenWidth(), index: 0, tag: item.BackdropImageTags[0] }); - page.classList.remove("noBackdrop"); - imageLoader.lazyImage(itemBackdropElement, imgUrl, false); + page.classList.remove('noBackdrop'); + imageLoader.lazyImage(itemBackdropElement, imgUrl); hasbackdrop = true; } else if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) { imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { - type: "Backdrop", + type: 'Backdrop', maxWidth: dom.getScreenWidth(), index: 0, tag: item.ParentBackdropImageTags[0] }); - page.classList.remove("noBackdrop"); - imageLoader.lazyImage(itemBackdropElement, imgUrl, false); + page.classList.remove('noBackdrop'); + imageLoader.lazyImage(itemBackdropElement, imgUrl); hasbackdrop = true; } else if (item.ImageTags && item.ImageTags.Thumb) { imgUrl = apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", + type: 'Thumb', maxWidth: dom.getScreenWidth(), index: 0, tag: item.ImageTags.Thumb }); - page.classList.remove("noBackdrop"); - imageLoader.lazyImage(itemBackdropElement, imgUrl, false); + page.classList.remove('noBackdrop'); + imageLoader.lazyImage(itemBackdropElement, imgUrl); hasbackdrop = true; } else { - itemBackdropElement.style.backgroundImage = ""; + itemBackdropElement.style.backgroundImage = ''; } - if ("Person" === item.Type) { + if ('Person' === item.Type) { // FIXME: This hides the backdrop on all persons to fix a margin issue. Ideally, a proper fix should be made. page.classList.add('noBackdrop'); - itemBackdropElement.classList.add("personBackdrop"); + itemBackdropElement.classList.add('personBackdrop'); } else { - itemBackdropElement.classList.remove("personBackdrop"); + itemBackdropElement.classList.remove('personBackdrop'); } return hasbackdrop; @@ -532,15 +546,15 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti function reloadFromItem(instance, page, params, item, user) { var context = params.context; - page.querySelector(".detailPagePrimaryContainer").classList.add("detailSticky"); + page.querySelector('.detailPagePrimaryContainer').classList.add('detailSticky'); - renderName(item, page.querySelector(".nameContainer"), false, context); + renderName(item, page.querySelector('.nameContainer'), false, context); var apiClient = connectionManager.getApiClient(item.ServerId); renderSeriesTimerEditor(page, item, apiClient, user); renderTimerEditor(page, item, apiClient, user); renderImage(page, item, apiClient, user); renderLogo(page, item, apiClient); - setTitle(item, apiClient); + Emby.Page.setTitle(''); setInitialCollapsibleState(page, item, apiClient, context, user); renderDetails(page, item, apiClient, context); renderTrackSelections(page, instance, item); @@ -548,96 +562,96 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti renderDetailPageBackdrop(page, item, apiClient); var canPlay = reloadPlayButtons(page, item); - if ((item.LocalTrailerCount || item.RemoteTrailers && item.RemoteTrailers.length) && -1 !== playbackManager.getSupportedCommands().indexOf("PlayTrailers")) { - hideAll(page, "btnPlayTrailer", true); + if ((item.LocalTrailerCount || item.RemoteTrailers && item.RemoteTrailers.length) && -1 !== playbackManager.getSupportedCommands().indexOf('PlayTrailers')) { + hideAll(page, 'btnPlayTrailer', true); } else { - hideAll(page, "btnPlayTrailer"); + hideAll(page, 'btnPlayTrailer'); } setTrailerButtonVisibility(page, item); if (item.CanDelete && !item.IsFolder) { - hideAll(page, "btnDeleteItem", true); + hideAll(page, 'btnDeleteItem', true); } else { - hideAll(page, "btnDeleteItem"); + hideAll(page, 'btnDeleteItem'); } - if ("Program" !== item.Type || canPlay) { - hideAll(page, "mainDetailButtons", true); + if ('Program' !== item.Type || canPlay) { + hideAll(page, 'mainDetailButtons', true); } else { - hideAll(page, "mainDetailButtons"); + hideAll(page, 'mainDetailButtons'); } showRecordingFields(instance, page, item, user); var groupedVersions = (item.MediaSources || []).filter(function (g) { - return "Grouping" == g.Type; + return 'Grouping' == g.Type; }); if (user.Policy.IsAdministrator && groupedVersions.length) { - page.querySelector(".btnSplitVersions").classList.remove("hide"); + page.querySelector('.btnSplitVersions').classList.remove('hide'); } else { - page.querySelector(".btnSplitVersions").classList.add("hide"); + page.querySelector('.btnSplitVersions').classList.add('hide'); } if (itemContextMenu.getCommands(getContextMenuOptions(item, user)).length) { - hideAll(page, "btnMoreCommands", true); + hideAll(page, 'btnMoreCommands', true); } else { - hideAll(page, "btnMoreCommands"); + hideAll(page, 'btnMoreCommands'); } - var itemBirthday = page.querySelector("#itemBirthday"); + var itemBirthday = page.querySelector('#itemBirthday'); - if ("Person" == item.Type && item.PremiereDate) { + if ('Person' == item.Type && item.PremiereDate) { try { var birthday = datetime.parseISO8601Date(item.PremiereDate, true).toDateString(); - itemBirthday.classList.remove("hide"); - itemBirthday.innerHTML = globalize.translate("BirthDateValue", birthday); + itemBirthday.classList.remove('hide'); + itemBirthday.innerHTML = globalize.translate('BirthDateValue', birthday); } catch (err) { - itemBirthday.classList.add("hide"); + itemBirthday.classList.add('hide'); } } else { - itemBirthday.classList.add("hide"); + itemBirthday.classList.add('hide'); } - var itemDeathDate = page.querySelector("#itemDeathDate"); + var itemDeathDate = page.querySelector('#itemDeathDate'); - if ("Person" == item.Type && item.EndDate) { + if ('Person' == item.Type && item.EndDate) { try { var deathday = datetime.parseISO8601Date(item.EndDate, true).toDateString(); - itemDeathDate.classList.remove("hide"); - itemDeathDate.innerHTML = globalize.translate("DeathDateValue", deathday); + itemDeathDate.classList.remove('hide'); + itemDeathDate.innerHTML = globalize.translate('DeathDateValue', deathday); } catch (err) { - itemDeathDate.classList.add("hide"); + itemDeathDate.classList.add('hide'); } } else { - itemDeathDate.classList.add("hide"); + itemDeathDate.classList.add('hide'); } - var itemBirthLocation = page.querySelector("#itemBirthLocation"); + var itemBirthLocation = page.querySelector('#itemBirthLocation'); - if ("Person" == item.Type && item.ProductionLocations && item.ProductionLocations.length) { - var gmap = '' + item.ProductionLocations[0] + ""; - itemBirthLocation.classList.remove("hide"); - itemBirthLocation.innerHTML = globalize.translate("BirthPlaceValue", gmap); + if ('Person' == item.Type && item.ProductionLocations && item.ProductionLocations.length) { + var gmap = '' + item.ProductionLocations[0] + ''; + itemBirthLocation.classList.remove('hide'); + itemBirthLocation.innerHTML = globalize.translate('BirthPlaceValue', gmap); } else { - itemBirthLocation.classList.add("hide"); + itemBirthLocation.classList.add('hide'); } setPeopleHeader(page, item); loading.hide(); - if (item.Type === "Book") { - hideAll(page, "btnDownload", true); + if (item.Type === 'Book') { + hideAll(page, 'btnDownload', true); } - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); } function logoImageUrl(item, apiClient, options) { options = options || {}; - options.type = "Logo"; + options.type = 'Logo'; if (item.ImageTags && item.ImageTags.Logo) { options.tag = item.ImageTags.Logo; @@ -652,53 +666,40 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti return null; } - function setTitle(item, apiClient) { - var url = logoImageUrl(item, apiClient, {}); - - if (url != null) { - var pageTitle = document.querySelector(".pageTitle"); - pageTitle.style.backgroundImage = "url('" + url + "')"; - pageTitle.classList.add("pageTitleWithLogo"); - pageTitle.innerHTML = ""; - } else { - Emby.Page.setTitle(""); - } - } - function renderLogo(page, item, apiClient) { var url = logoImageUrl(item, apiClient, { maxWidth: 400 }); - var detailLogo = page.querySelector(".detailLogo"); + var detailLogo = page.querySelector('.detailLogo'); if (!layoutManager.mobile && !userSettings.enableBackdrops()) { - detailLogo.classList.add("hide"); + detailLogo.classList.add('hide'); } else if (url) { - detailLogo.classList.remove("hide"); - detailLogo.classList.add("lazy"); - detailLogo.setAttribute("data-src", url); + detailLogo.classList.remove('hide'); + detailLogo.classList.add('lazy'); + detailLogo.setAttribute('data-src', url); imageLoader.lazyImage(detailLogo); } else { - detailLogo.classList.add("hide"); + detailLogo.classList.add('hide'); } } function showRecordingFields(instance, page, item, user) { if (!instance.currentRecordingFields) { - var recordingFieldsElement = page.querySelector(".recordingFields"); + var recordingFieldsElement = page.querySelector('.recordingFields'); - if ("Program" == item.Type && user.Policy.EnableLiveTvManagement) { - require(["recordingFields"], function (recordingFields) { + if ('Program' == item.Type && user.Policy.EnableLiveTvManagement) { + require(['recordingFields'], function (recordingFields) { instance.currentRecordingFields = new recordingFields({ parent: recordingFieldsElement, programId: item.Id, serverId: item.ServerId }); - recordingFieldsElement.classList.remove("hide"); + recordingFieldsElement.classList.remove('hide'); }); } else { - recordingFieldsElement.classList.add("hide"); - recordingFieldsElement.innerHTML = ""; + recordingFieldsElement.classList.add('hide'); + recordingFieldsElement.innerHTML = ''; } } } @@ -709,34 +710,34 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti var links = []; if (!layoutManager.tv && item.HomePageUrl) { - links.push('' + globalize.translate("ButtonWebsite") + ""); + links.push('' + globalize.translate('ButtonWebsite') + ''); } if (item.ExternalUrls) { for (var i = 0, length = item.ExternalUrls.length; i < length; i++) { var url = item.ExternalUrls[i]; - links.push('' + url.Name + ""); + links.push('' + url.Name + ''); } } if (links.length) { - html.push(links.join(", ")); + html.push(links.join(', ')); } - linksElem.innerHTML = html.join(", "); + linksElem.innerHTML = html.join(', '); if (html.length) { - linksElem.classList.remove("hide"); + linksElem.classList.remove('hide'); } else { - linksElem.classList.add("hide"); + linksElem.classList.add('hide'); } } function renderDetailImage(page, elem, item, apiClient, editable, imageLoader, indicators) { - if ("SeriesTimer" === item.Type || "Program" === item.Type) { + if ('SeriesTimer' === item.Type || 'Program' === item.Type) { editable = false; } - elem.classList.add("detailimg-hidemobile"); + elem.classList.add('detailimg-hidemobile'); var imageTags = item.ImageTags || {}; @@ -745,8 +746,8 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } var url; - var html = ""; - var shape = "portrait"; + var html = ''; + var shape = 'portrait'; var detectRatio = false; /* In the following section, getScreenWidth() is multiplied by 0.5 as the posters @@ -754,57 +755,57 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti // TODO: Find a reliable way to get the poster width if (imageTags.Primary) { url = apiClient.getScaledImageUrl(item.Id, { - type: "Primary", + type: 'Primary', maxWidth: Math.round(dom.getScreenWidth() * 0.5), tag: item.ImageTags.Primary }); detectRatio = true; } else if (item.BackdropImageTags && item.BackdropImageTags.length) { url = apiClient.getScaledImageUrl(item.Id, { - type: "Backdrop", + type: 'Backdrop', maxWidth: Math.round(dom.getScreenWidth() * 0.5), tag: item.BackdropImageTags[0] }); - shape = "thumb"; + shape = 'thumb'; } else if (imageTags.Thumb) { url = apiClient.getScaledImageUrl(item.Id, { - type: "Thumb", + type: 'Thumb', maxWidth: Math.round(dom.getScreenWidth() * 0.5), tag: item.ImageTags.Thumb }); - shape = "thumb"; + shape = 'thumb'; } else if (imageTags.Disc) { url = apiClient.getScaledImageUrl(item.Id, { - type: "Disc", + type: 'Disc', maxWidth: Math.round(dom.getScreenWidth() * 0.5), tag: item.ImageTags.Disc }); - shape = "square"; + shape = 'square'; } else if (item.AlbumId && item.AlbumPrimaryImageTag) { url = apiClient.getScaledImageUrl(item.AlbumId, { - type: "Primary", + type: 'Primary', maxWidth: Math.round(dom.getScreenWidth() * 0.5), tag: item.AlbumPrimaryImageTag }); - shape = "square"; + shape = 'square'; } else if (item.SeriesId && item.SeriesPrimaryImageTag) { url = apiClient.getScaledImageUrl(item.SeriesId, { - type: "Primary", + type: 'Primary', maxWidth: Math.round(dom.getScreenWidth() * 0.5), tag: item.SeriesPrimaryImageTag }); } else if (item.ParentPrimaryImageItemId && item.ParentPrimaryImageTag) { url = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, { - type: "Primary", + type: 'Primary', maxWidth: Math.round(dom.getScreenWidth() * 0.5), tag: item.ParentPrimaryImageTag }); } if (editable && url === undefined) { - html += ""; + html += ""; } else if (!editable && url === undefined) { - html += "'; } - var progressHtml = item.IsFolder || !item.UserData ? "" : indicators.getProgressBarHtml(item); + var progressHtml = item.IsFolder || !item.UserData ? '' : indicators.getProgressBarHtml(item); html += '
'; if (progressHtml) { html += progressHtml; } - html += "
"; + html += ''; elem.innerHTML = html; if (detectRatio && item.PrimaryImageAspectRatio) { if (item.PrimaryImageAspectRatio >= 1.48) { - shape = "thumb"; + shape = 'thumb'; } else if (item.PrimaryImageAspectRatio >= 0.85 && item.PrimaryImageAspectRatio <= 1.34) { - shape = "square"; + shape = 'square'; } } - if ("thumb" == shape) { - elem.classList.add("thumbDetailImageContainer"); - elem.classList.remove("portraitDetailImageContainer"); - elem.classList.remove("squareDetailImageContainer"); - } else if ("square" == shape) { - elem.classList.remove("thumbDetailImageContainer"); - elem.classList.remove("portraitDetailImageContainer"); - elem.classList.add("squareDetailImageContainer"); + if ('thumb' == shape) { + elem.classList.add('thumbDetailImageContainer'); + elem.classList.remove('portraitDetailImageContainer'); + elem.classList.remove('squareDetailImageContainer'); + } else if ('square' == shape) { + elem.classList.remove('thumbDetailImageContainer'); + elem.classList.remove('portraitDetailImageContainer'); + elem.classList.add('squareDetailImageContainer'); } else { - elem.classList.remove("thumbDetailImageContainer"); - elem.classList.add("portraitDetailImageContainer"); - elem.classList.remove("squareDetailImageContainer"); + elem.classList.remove('thumbDetailImageContainer'); + elem.classList.add('portraitDetailImageContainer'); + elem.classList.remove('squareDetailImageContainer'); } if (url) { - imageLoader.lazyImage(elem.querySelector("img"), url); + imageLoader.lazyImage(elem.querySelector('img'), url); } } function renderImage(page, item, apiClient, user) { renderDetailImage( page, - page.querySelector(".detailImageContainer"), + page.querySelector('.detailImageContainer'), item, apiClient, - user.Policy.IsAdministrator && "Photo" != item.MediaType, + user.Policy.IsAdministrator && 'Photo' != item.MediaType, imageLoader, indicators ); } function refreshDetailImageUserData(elem, item) { - elem.querySelector(".detailImageProgressContainer").innerHTML = indicators.getProgressBarHtml(item); + elem.querySelector('.detailImageProgressContainer').innerHTML = indicators.getProgressBarHtml(item); } function refreshImage(page, item) { - refreshDetailImageUserData(page.querySelector(".detailImageContainer"), item); + refreshDetailImageUserData(page.querySelector('.detailImageContainer'), item); } function setPeopleHeader(page, item) { - if ("Audio" == item.MediaType || "MusicAlbum" == item.Type || "Book" == item.MediaType || "Photo" == item.MediaType) { - page.querySelector("#peopleHeader").innerHTML = globalize.translate("HeaderPeople"); + if ('Audio' == item.MediaType || 'MusicAlbum' == item.Type || 'Book' == item.MediaType || 'Photo' == item.MediaType) { + page.querySelector('#peopleHeader').innerHTML = globalize.translate('HeaderPeople'); } else { - page.querySelector("#peopleHeader").innerHTML = globalize.translate("HeaderCastAndCrew"); + page.querySelector('#peopleHeader').innerHTML = globalize.translate('HeaderCastAndCrew'); } } function renderNextUp(page, item, user) { - var section = page.querySelector(".nextUpSection"); + var section = page.querySelector('.nextUpSection'); - if ("Series" != item.Type) { - return void section.classList.add("hide"); + if ('Series' != item.Type) { + return void section.classList.add('hide'); } connectionManager.getApiClient(item.ServerId).getNextUpEpisodes({ @@ -900,93 +901,121 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti UserId: user.Id }).then(function (result) { if (result.Items.length) { - section.classList.remove("hide"); + section.classList.remove('hide'); } else { - section.classList.add("hide"); + section.classList.add('hide'); } var html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "overflowBackdrop", + shape: 'overflowBackdrop', showTitle: true, - displayAsSpecial: "Season" == item.Type && item.IndexNumber, + displayAsSpecial: 'Season' == item.Type && item.IndexNumber, overlayText: false, centerText: true, overlayPlayButton: true }); - var itemsContainer = section.querySelector(".nextUpItems"); + var itemsContainer = section.querySelector('.nextUpItems'); itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); }); } function setInitialCollapsibleState(page, item, apiClient, context, user) { - page.querySelector(".collectionItems").innerHTML = ""; + page.querySelector('.collectionItems').innerHTML = ''; - if ("Playlist" == item.Type) { - page.querySelector("#childrenCollapsible").classList.remove("hide"); + if ('Playlist' == item.Type) { + page.querySelector('#childrenCollapsible').classList.remove('hide'); renderPlaylistItems(page, item); - } else if ("Studio" == item.Type || "Person" == item.Type || "Genre" == item.Type || "MusicGenre" == item.Type || "MusicArtist" == item.Type) { - page.querySelector("#childrenCollapsible").classList.remove("hide"); + } else if ('Studio' == item.Type || 'Person' == item.Type || 'Genre' == item.Type || 'MusicGenre' == item.Type || 'MusicArtist' == item.Type) { + page.querySelector('#childrenCollapsible').classList.remove('hide'); renderItemsByName(page, item); } else if (item.IsFolder) { - if ("BoxSet" == item.Type) { - page.querySelector("#childrenCollapsible").classList.add("hide"); + if ('BoxSet' == item.Type) { + page.querySelector('#childrenCollapsible').classList.add('hide'); } renderChildren(page, item); } else { - page.querySelector("#childrenCollapsible").classList.add("hide"); + page.querySelector('#childrenCollapsible').classList.add('hide'); } - if ("Series" == item.Type) { + if ('Series' == item.Type) { renderSeriesSchedule(page, item); renderNextUp(page, item, user); } else { - page.querySelector(".nextUpSection").classList.add("hide"); + page.querySelector('.nextUpSection').classList.add('hide'); } renderScenes(page, item); - if (item.SpecialFeatureCount && 0 != item.SpecialFeatureCount && "Series" != item.Type) { - page.querySelector("#specialsCollapsible").classList.remove("hide"); + if (item.SpecialFeatureCount && 0 != item.SpecialFeatureCount && 'Series' != item.Type) { + page.querySelector('#specialsCollapsible').classList.remove('hide'); renderSpecials(page, item, user, 6); } else { - page.querySelector("#specialsCollapsible").classList.add("hide"); + page.querySelector('#specialsCollapsible').classList.add('hide'); } renderCast(page, item); if (item.PartCount && item.PartCount > 1) { - page.querySelector("#additionalPartsCollapsible").classList.remove("hide"); + page.querySelector('#additionalPartsCollapsible').classList.remove('hide'); renderAdditionalParts(page, item, user); } else { - page.querySelector("#additionalPartsCollapsible").classList.add("hide"); + page.querySelector('#additionalPartsCollapsible').classList.add('hide'); } - if ("MusicAlbum" == item.Type) { + if ('MusicAlbum' == item.Type) { renderMusicVideos(page, item, user); } else { - page.querySelector("#musicVideosCollapsible").classList.add("hide"); + page.querySelector('#musicVideosCollapsible').classList.add('hide'); + } + } + + function toggleLineClamp(clampTarget, e) { + var expandButton = e.target; + var clampClassName = 'detail-clamp-text'; + + if (clampTarget.classList.contains(clampClassName)) { + clampTarget.classList.remove(clampClassName); + expandButton.innerHTML = globalize.translate('ShowLess'); + } else { + clampTarget.classList.add(clampClassName); + expandButton.innerHTML = globalize.translate('ShowMore'); } } function renderOverview(elems, item) { for (var i = 0, length = elems.length; i < length; i++) { var elem = elems[i]; - var overview = item.Overview || ""; + var overview = item.Overview || ''; if (overview) { elem.innerHTML = overview; - elem.classList.remove("hide"); - var anchors = elem.querySelectorAll("a"); + elem.classList.remove('hide'); + elem.classList.add('detail-clamp-text'); + + // Grab the sibling element to control the expand state + var expandButton = elem.parentElement.querySelector('.overview-expand'); + + // Detect if we have overflow of text. Based on this StackOverflow answer + // https://stackoverflow.com/a/35157976 + if (Math.abs(elem.scrollHeight - elem.offsetHeight) > 2) { + expandButton.classList.remove('hide'); + } else { + expandButton.classList.add('hide'); + } + + expandButton.addEventListener('click', toggleLineClamp.bind(null, elem)); + + var anchors = elem.querySelectorAll('a'); for (var j = 0, length2 = anchors.length; j < length2; j++) { - anchors[j].setAttribute("target", "_blank"); + anchors[j].setAttribute('target', '_blank'); } } else { - elem.innerHTML = ""; - elem.classList.add("hide"); + elem.innerHTML = ''; + elem.classList.add('hide'); } } } @@ -997,12 +1026,12 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti var genres = item.GenreItems || []; switch (context) { - case "music": - type = "MusicGenre"; + case 'music': + type = 'MusicGenre'; break; default: - type = "Genre"; + type = 'Genre'; } var html = genres.map(function (p) { @@ -1013,47 +1042,47 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti Id: p.Id }, { context: context - }) + '">' + p.Name + ""; - }).join(", "); + }) + '">' + p.Name + ''; + }).join(', '); - var genresLabel = page.querySelector(".genresLabel"); - genresLabel.innerHTML = globalize.translate(genres.length > 1 ? "Genres" : "Genre"); - var genresValue = page.querySelector(".genres"); + var genresLabel = page.querySelector('.genresLabel'); + genresLabel.innerHTML = globalize.translate(genres.length > 1 ? 'Genres' : 'Genre'); + var genresValue = page.querySelector('.genres'); genresValue.innerHTML = html; - var genresGroup = page.querySelector(".genresGroup"); + var genresGroup = page.querySelector('.genresGroup'); if (genres.length) { - genresGroup.classList.remove("hide"); + genresGroup.classList.remove('hide'); } else { - genresGroup.classList.add("hide"); + genresGroup.classList.add('hide'); } } function renderDirector(page, item, context) { var directors = (item.People || []).filter(function (p) { - return "Director" === p.Type; + return 'Director' === p.Type; }); var html = directors.map(function (p) { return '' + p.Name + ""; - }).join(", "); + }) + '">' + p.Name + ''; + }).join(', '); - var directorsLabel = page.querySelector(".directorsLabel"); - directorsLabel.innerHTML = globalize.translate(directors.length > 1 ? "Directors" : "Director"); - var directorsValue = page.querySelector(".directors"); + var directorsLabel = page.querySelector('.directorsLabel'); + directorsLabel.innerHTML = globalize.translate(directors.length > 1 ? 'Directors' : 'Director'); + var directorsValue = page.querySelector('.directors'); directorsValue.innerHTML = html; - var directorsGroup = page.querySelector(".directorsGroup"); + var directorsGroup = page.querySelector('.directorsGroup'); if (directors.length) { - directorsGroup.classList.remove("hide"); + directorsGroup.classList.remove('hide'); } else { - directorsGroup.classList.add("hide"); + directorsGroup.classList.add('hide'); } } @@ -1064,22 +1093,22 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti renderDirector(page, item, context); renderGenres(page, item, context); renderChannelGuide(page, apiClient, item); - var taglineElement = page.querySelector(".tagline"); + var taglineElement = page.querySelector('.tagline'); if (item.Taglines && item.Taglines.length) { - taglineElement.classList.remove("hide"); + taglineElement.classList.remove('hide'); taglineElement.innerHTML = item.Taglines[0]; } else { - taglineElement.classList.add("hide"); + taglineElement.classList.add('hide'); } - var overview = page.querySelector(".overview"); - var externalLinksElem = page.querySelector(".itemExternalLinks"); + var overview = page.querySelector('.overview'); + var externalLinksElem = page.querySelector('.itemExternalLinks'); renderOverview([overview], item); var i; var itemMiscInfo; - itemMiscInfo = page.querySelectorAll(".itemMiscInfo-primary"); + itemMiscInfo = page.querySelectorAll('.itemMiscInfo-primary'); for (i = 0; i < itemMiscInfo.length; i++) { mediaInfo.fillPrimaryMediaInfo(itemMiscInfo[i], item, { @@ -1088,24 +1117,24 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti subtitles: false }); - if (itemMiscInfo[i].innerHTML && "SeriesTimer" !== item.Type) { - itemMiscInfo[i].classList.remove("hide"); + if (itemMiscInfo[i].innerHTML && 'SeriesTimer' !== item.Type) { + itemMiscInfo[i].classList.remove('hide'); } else { - itemMiscInfo[i].classList.add("hide"); + itemMiscInfo[i].classList.add('hide'); } } - itemMiscInfo = page.querySelectorAll(".itemMiscInfo-secondary"); + itemMiscInfo = page.querySelectorAll('.itemMiscInfo-secondary'); for (i = 0; i < itemMiscInfo.length; i++) { mediaInfo.fillSecondaryMediaInfo(itemMiscInfo[i], item, { interactive: true }); - if (itemMiscInfo[i].innerHTML && "SeriesTimer" !== item.Type) { - itemMiscInfo[i].classList.remove("hide"); + if (itemMiscInfo[i].innerHTML && 'SeriesTimer' !== item.Type) { + itemMiscInfo[i].classList.remove('hide'); } else { - itemMiscInfo[i].classList.add("hide"); + itemMiscInfo[i].classList.add('hide'); } } @@ -1124,7 +1153,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti scrollX = enableScrollX(); } - return scrollX ? "overflowPortrait" : "portrait"; + return scrollX ? 'overflowPortrait' : 'portrait'; } function getSquareShape(scrollX) { @@ -1132,35 +1161,35 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti scrollX = enableScrollX(); } - return scrollX ? "overflowSquare" : "square"; + return scrollX ? 'overflowSquare' : 'square'; } function renderMoreFromSeason(view, item, apiClient) { - var section = view.querySelector(".moreFromSeasonSection"); + var section = view.querySelector('.moreFromSeasonSection'); if (section) { - if ("Episode" !== item.Type || !item.SeasonId || !item.SeriesId) { - return void section.classList.add("hide"); + if ('Episode' !== item.Type || !item.SeasonId || !item.SeriesId) { + return void section.classList.add('hide'); } var userId = apiClient.getCurrentUserId(); apiClient.getEpisodes(item.SeriesId, { SeasonId: item.SeasonId, UserId: userId, - Fields: "ItemCounts,PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount" + Fields: 'ItemCounts,PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount' }).then(function (result) { if (result.Items.length < 2) { - return void section.classList.add("hide"); + return void section.classList.add('hide'); } - section.classList.remove("hide"); - section.querySelector("h2").innerHTML = globalize.translate("MoreFromValue", item.SeasonName); - var itemsContainer = section.querySelector(".itemsContainer"); + section.classList.remove('hide'); + section.querySelector('h2').innerHTML = globalize.translate('MoreFromValue', item.SeasonName); + var itemsContainer = section.querySelector('.itemsContainer'); cardBuilder.buildCards(result.Items, { parentContainer: section, itemsContainer: itemsContainer, - shape: "autooverflow", - sectionTitleTagName: "h2", + shape: 'autooverflow', + sectionTitleTagName: 'h2', scalable: true, showTitle: true, overlayText: false, @@ -1172,7 +1201,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti if (card) { setTimeout(function () { - section.querySelector(".emby-scroller").toStart(card.previousSibling || card, true); + section.querySelector('.emby-scroller').toStart(card.previousSibling || card, true); }, 100); } }); @@ -1180,28 +1209,28 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function renderMoreFromArtist(view, item, apiClient) { - var section = view.querySelector(".moreFromArtistSection"); + var section = view.querySelector('.moreFromArtistSection'); if (section) { - if ("MusicArtist" === item.Type) { - if (!apiClient.isMinServerVersion("3.4.1.19")) { - return void section.classList.add("hide"); + if ('MusicArtist' === item.Type) { + if (!apiClient.isMinServerVersion('3.4.1.19')) { + return void section.classList.add('hide'); } - } else if ("MusicAlbum" !== item.Type || !item.AlbumArtists || !item.AlbumArtists.length) { - return void section.classList.add("hide"); + } else if ('MusicAlbum' !== item.Type || !item.AlbumArtists || !item.AlbumArtists.length) { + return void section.classList.add('hide'); } var query = { - IncludeItemTypes: "MusicAlbum", + IncludeItemTypes: 'MusicAlbum', Recursive: true, ExcludeItemIds: item.Id, - SortBy: "ProductionYear,SortName", - SortOrder: "Descending" + SortBy: 'ProductionYear,SortName', + SortOrder: 'Descending' }; - if ("MusicArtist" === item.Type) { + if ('MusicArtist' === item.Type) { query.ContributingArtistIds = item.Id; - } else if (apiClient.isMinServerVersion("3.4.1.18")) { + } else if (apiClient.isMinServerVersion('3.4.1.18')) { query.AlbumArtistIds = item.AlbumArtists[0].Id; } else { query.ArtistIds = item.AlbumArtists[0].Id; @@ -1209,24 +1238,24 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (result) { if (!result.Items.length) { - return void section.classList.add("hide"); + return void section.classList.add('hide'); } - section.classList.remove("hide"); + section.classList.remove('hide'); - if ("MusicArtist" === item.Type) { - section.querySelector("h2").innerHTML = globalize.translate("HeaderAppearsOn"); + if ('MusicArtist' === item.Type) { + section.querySelector('h2').innerHTML = globalize.translate('HeaderAppearsOn'); } else { - section.querySelector("h2").innerHTML = globalize.translate("MoreFromValue", item.AlbumArtists[0].Name); + section.querySelector('h2').innerHTML = globalize.translate('MoreFromValue', item.AlbumArtists[0].Name); } cardBuilder.buildCards(result.Items, { parentContainer: section, - itemsContainer: section.querySelector(".itemsContainer"), - shape: "autooverflow", - sectionTitleTagName: "h2", + itemsContainer: section.querySelector('.itemsContainer'), + shape: 'autooverflow', + sectionTitleTagName: 'h2', scalable: true, - coverImage: "MusicArtist" === item.Type || "MusicAlbum" === item.Type, + coverImage: 'MusicArtist' === item.Type || 'MusicAlbum' === item.Type, showTitle: true, showParentTitle: false, centerText: true, @@ -1239,47 +1268,47 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function renderSimilarItems(page, item, context) { - var similarCollapsible = page.querySelector("#similarCollapsible"); + var similarCollapsible = page.querySelector('#similarCollapsible'); if (similarCollapsible) { - if ("Movie" != item.Type && "Trailer" != item.Type && "Series" != item.Type && "Program" != item.Type && "Recording" != item.Type && "MusicAlbum" != item.Type && "MusicArtist" != item.Type && "Playlist" != item.Type) { - return void similarCollapsible.classList.add("hide"); + if ('Movie' != item.Type && 'Trailer' != item.Type && 'Series' != item.Type && 'Program' != item.Type && 'Recording' != item.Type && 'MusicAlbum' != item.Type && 'MusicArtist' != item.Type && 'Playlist' != item.Type) { + return void similarCollapsible.classList.add('hide'); } - similarCollapsible.classList.remove("hide"); + similarCollapsible.classList.remove('hide'); var apiClient = connectionManager.getApiClient(item.ServerId); var options = { userId: apiClient.getCurrentUserId(), limit: 12, - fields: "PrimaryImageAspectRatio,UserData,CanDelete" + fields: 'PrimaryImageAspectRatio,UserData,CanDelete' }; - if ("MusicAlbum" == item.Type && item.AlbumArtists && item.AlbumArtists.length) { + if ('MusicAlbum' == item.Type && item.AlbumArtists && item.AlbumArtists.length) { options.ExcludeArtistIds = item.AlbumArtists[0].Id; } apiClient.getSimilarItems(item.Id, options).then(function (result) { if (!result.Items.length) { - return void similarCollapsible.classList.add("hide"); + return void similarCollapsible.classList.add('hide'); } - similarCollapsible.classList.remove("hide"); - var html = ""; + similarCollapsible.classList.remove('hide'); + var html = ''; html += cardBuilder.getCardsHtml({ items: result.Items, - shape: "autooverflow", - showParentTitle: "MusicAlbum" == item.Type, + shape: 'autooverflow', + showParentTitle: 'MusicAlbum' == item.Type, centerText: true, showTitle: true, context: context, lazy: true, showDetailsMenu: true, - coverImage: "MusicAlbum" == item.Type || "MusicArtist" == item.Type, + coverImage: 'MusicAlbum' == item.Type || 'MusicArtist' == item.Type, overlayPlayButton: true, overlayText: false, - showYear: "Movie" === item.Type || "Trailer" === item.Type || "Series" === item.Type + showYear: 'Movie' === item.Type || 'Trailer' === item.Type || 'Series' === item.Type }); - var similarContent = similarCollapsible.querySelector(".similarContent"); + var similarContent = similarCollapsible.querySelector('.similarContent'); similarContent.innerHTML = html; imageLoader.lazyChildren(similarContent); }); @@ -1287,52 +1316,52 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function renderSeriesAirTime(page, item, isStatic) { - var seriesAirTime = page.querySelector("#seriesAirTime"); - if ("Series" != item.Type) { - seriesAirTime.classList.add("hide"); + var seriesAirTime = page.querySelector('#seriesAirTime'); + if ('Series' != item.Type) { + seriesAirTime.classList.add('hide'); return; } - var html = ""; + var html = ''; if (item.AirDays && item.AirDays.length) { if (7 == item.AirDays.length) { - html += "daily"; + html += 'daily'; } else { html += item.AirDays.map(function (a) { - return a + "s"; - }).join(","); + return a + 's'; + }).join(','); } } if (item.AirTime) { - html += " at " + item.AirTime; + html += ' at ' + item.AirTime; } if (item.Studios.length) { if (isStatic) { - html += " on " + item.Studios[0].Name; + html += ' on ' + item.Studios[0].Name; } else { var context = inferContext(item); var href = appRouter.getRouteUrl(item.Studios[0], { context: context, - itemType: "Studio", + itemType: 'Studio', serverId: item.ServerId }); - html += ' on ' + item.Studios[0].Name + ""; + html += ' on ' + item.Studios[0].Name + ''; } } if (html) { - html = ("Ended" == item.Status ? "Aired " : "Airs ") + html; + html = ('Ended' == item.Status ? 'Aired ' : 'Airs ') + html; seriesAirTime.innerHTML = html; - seriesAirTime.classList.remove("hide"); + seriesAirTime.classList.remove('hide'); } else { - seriesAirTime.classList.add("hide"); + seriesAirTime.classList.add('hide'); } } function renderTags(page, item) { - var itemTags = page.querySelector(".itemTags"); + var itemTags = page.querySelector('.itemTags'); var tagElements = []; var tags = item.Tags || []; - if ("Program" === item.Type) { + if ('Program' === item.Type) { tags = []; } @@ -1341,93 +1370,93 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } if (tagElements.length) { - itemTags.innerHTML = globalize.translate("TagsValue", tagElements.join(", ")); - itemTags.classList.remove("hide"); + itemTags.innerHTML = globalize.translate('TagsValue', tagElements.join(', ')); + itemTags.classList.remove('hide'); } else { - itemTags.innerHTML = ""; - itemTags.classList.add("hide"); + itemTags.innerHTML = ''; + itemTags.classList.add('hide'); } } function renderChildren(page, item) { - var fields = "ItemCounts,PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount"; + var fields = 'ItemCounts,PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount'; var query = { ParentId: item.Id, Fields: fields }; - if ("BoxSet" !== item.Type) { - query.SortBy = "SortName"; + if ('BoxSet' !== item.Type) { + query.SortBy = 'SortName'; } var promise; var apiClient = connectionManager.getApiClient(item.ServerId); var userId = apiClient.getCurrentUserId(); - if ("Series" == item.Type) { + if ('Series' == item.Type) { promise = apiClient.getSeasons(item.Id, { userId: userId, Fields: fields }); - } else if ("Season" == item.Type) { - fields += ",Overview"; + } else if ('Season' == item.Type) { + fields += ',Overview'; promise = apiClient.getEpisodes(item.SeriesId, { seasonId: item.Id, userId: userId, Fields: fields }); - } else if ("MusicArtist" == item.Type) { - query.SortBy = "ProductionYear,SortName"; + } else if ('MusicArtist' == item.Type) { + query.SortBy = 'ProductionYear,SortName'; } promise = promise || apiClient.getItems(apiClient.getCurrentUserId(), query); promise.then(function (result) { - var html = ""; + var html = ''; var scrollX = false; var isList = false; - var childrenItemsContainer = page.querySelector(".childrenItemsContainer"); + var childrenItemsContainer = page.querySelector('.childrenItemsContainer'); - if ("MusicAlbum" == item.Type) { + if ('MusicAlbum' == item.Type) { html = listView.getListViewHtml({ items: result.Items, smallIcon: true, showIndex: true, - index: "disc", + index: 'disc', showIndexNumberLeft: true, playFromHere: true, - action: "playallfromhere", + action: 'playallfromhere', image: false, - artist: "auto", + artist: 'auto', containerAlbumArtists: item.AlbumArtists, addToListButton: true }); isList = true; - } else if ("Series" == item.Type) { + } else if ('Series' == item.Type) { scrollX = enableScrollX(); html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "overflowPortrait", + shape: 'overflowPortrait', showTitle: true, centerText: true, lazy: true, overlayPlayButton: true, allowBottomPadding: !scrollX }); - } else if ("Season" == item.Type || "Episode" == item.Type) { - if ("Episode" !== item.Type) { + } else if ('Season' == item.Type || 'Episode' == item.Type) { + if ('Episode' !== item.Type) { isList = true; } - scrollX = "Episode" == item.Type; - if (result.Items.length < 2 && "Episode" === item.Type) { + scrollX = 'Episode' == item.Type; + if (result.Items.length < 2 && 'Episode' === item.Type) { return; } - if ("Episode" === item.Type) { + if ('Episode' === item.Type) { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "overflowBackdrop", + shape: 'overflowBackdrop', showTitle: true, - displayAsSpecial: "Season" == item.Type && item.IndexNumber, + displayAsSpecial: 'Season' == item.Type && item.IndexNumber, playFromHere: true, overlayText: true, lazy: true, @@ -1436,15 +1465,15 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti allowBottomPadding: !scrollX, includeParentInfoInTitle: false }); - } else if ("Season" === item.Type) { + } else if ('Season' === item.Type) { html = listView.getListViewHtml({ items: result.Items, showIndexNumber: false, enableOverview: true, - imageSize: "large", + imageSize: 'large', enableSideMediaInfo: false, highlight: false, - action: layoutManager.tv ? "resume" : "none", + action: layoutManager.tv ? 'resume' : 'none', infoButton: true, imagePlayButton: true, includeParentInfoInTitle: false @@ -1452,78 +1481,78 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } } - if ("BoxSet" !== item.Type) { - page.querySelector("#childrenCollapsible").classList.remove("hide"); + if ('BoxSet' !== item.Type) { + page.querySelector('#childrenCollapsible').classList.remove('hide'); } if (scrollX) { - childrenItemsContainer.classList.add("scrollX"); - childrenItemsContainer.classList.add("hiddenScrollX"); - childrenItemsContainer.classList.remove("vertical-wrap"); - childrenItemsContainer.classList.remove("vertical-list"); + childrenItemsContainer.classList.add('scrollX'); + childrenItemsContainer.classList.add('hiddenScrollX'); + childrenItemsContainer.classList.remove('vertical-wrap'); + childrenItemsContainer.classList.remove('vertical-list'); } else { - childrenItemsContainer.classList.remove("scrollX"); - childrenItemsContainer.classList.remove("hiddenScrollX"); - childrenItemsContainer.classList.remove("smoothScrollX"); + childrenItemsContainer.classList.remove('scrollX'); + childrenItemsContainer.classList.remove('hiddenScrollX'); + childrenItemsContainer.classList.remove('smoothScrollX'); if (isList) { - childrenItemsContainer.classList.add("vertical-list"); - childrenItemsContainer.classList.remove("vertical-wrap"); + childrenItemsContainer.classList.add('vertical-list'); + childrenItemsContainer.classList.remove('vertical-wrap'); } else { - childrenItemsContainer.classList.add("vertical-wrap"); - childrenItemsContainer.classList.remove("vertical-list"); + childrenItemsContainer.classList.add('vertical-wrap'); + childrenItemsContainer.classList.remove('vertical-list'); } } childrenItemsContainer.innerHTML = html; imageLoader.lazyChildren(childrenItemsContainer); - if ("BoxSet" == item.Type) { + if ('BoxSet' == item.Type) { var collectionItemTypes = [{ - name: globalize.translate("HeaderVideos"), - mediaType: "Video" + name: globalize.translate('HeaderVideos'), + mediaType: 'Video' }, { - name: globalize.translate("HeaderSeries"), - type: "Series" + name: globalize.translate('HeaderSeries'), + type: 'Series' }, { - name: globalize.translate("HeaderAlbums"), - type: "MusicAlbum" + name: globalize.translate('HeaderAlbums'), + type: 'MusicAlbum' }, { - name: globalize.translate("HeaderBooks"), - type: "Book" + name: globalize.translate('HeaderBooks'), + type: 'Book' }]; renderCollectionItems(page, item, collectionItemTypes, result.Items); } }); - if ("Season" == item.Type) { - page.querySelector("#childrenTitle").innerHTML = globalize.translate("HeaderEpisodes"); - } else if ("Series" == item.Type) { - page.querySelector("#childrenTitle").innerHTML = globalize.translate("HeaderSeasons"); - } else if ("MusicAlbum" == item.Type) { - page.querySelector("#childrenTitle").innerHTML = globalize.translate("HeaderTracks"); + if ('Season' == item.Type) { + page.querySelector('#childrenTitle').innerHTML = globalize.translate('HeaderEpisodes'); + } else if ('Series' == item.Type) { + page.querySelector('#childrenTitle').innerHTML = globalize.translate('HeaderSeasons'); + } else if ('MusicAlbum' == item.Type) { + page.querySelector('#childrenTitle').innerHTML = globalize.translate('HeaderTracks'); } else { - page.querySelector("#childrenTitle").innerHTML = globalize.translate("HeaderItems"); + page.querySelector('#childrenTitle').innerHTML = globalize.translate('HeaderItems'); } - if ("MusicAlbum" == item.Type || "Season" == item.Type) { - page.querySelector(".childrenSectionHeader").classList.add("hide"); - page.querySelector("#childrenCollapsible").classList.add("verticalSection-extrabottompadding"); + if ('MusicAlbum' == item.Type || 'Season' == item.Type) { + page.querySelector('.childrenSectionHeader').classList.add('hide'); + page.querySelector('#childrenCollapsible').classList.add('verticalSection-extrabottompadding'); } else { - page.querySelector(".childrenSectionHeader").classList.remove("hide"); + page.querySelector('.childrenSectionHeader').classList.remove('hide'); } } function renderItemsByName(page, item) { - require("scripts/itembynamedetailpage".split(","), function () { + require('scripts/itembynamedetailpage'.split(','), function () { window.ItemsByName.renderItems(page, item); }); } function renderPlaylistItems(page, item) { - require("scripts/playlistedit".split(","), function () { + require('scripts/playlistedit'.split(','), function () { PlaylistViewer.render(page, item); }); } function renderProgramsForChannel(page, result) { - var html = ""; + var html = ''; var currentItems = []; var currentStartDate = null; @@ -1535,10 +1564,10 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti if (currentItems.length) { html += '
'; html += '

' + datetime.toLocaleDateString(currentStartDate, { - weekday: "long", - month: "long", - day: "numeric" - }) + "

"; + weekday: 'long', + month: 'long', + day: 'numeric' + }) + ''; html += '
' + listView.getListViewHtml({ items: currentItems, enableUserDataButtons: false, @@ -1547,7 +1576,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti showProgramTime: true, mediaInfo: false, parentTitleWithTitle: true - }) + "
"; + }) + ''; } currentStartDate = itemStartDate; @@ -1560,10 +1589,10 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti if (currentItems.length) { html += '
'; html += '

' + datetime.toLocaleDateString(currentStartDate, { - weekday: "long", - month: "long", - day: "numeric" - }) + "

"; + weekday: 'long', + month: 'long', + day: 'numeric' + }) + ''; html += '
' + listView.getListViewHtml({ items: currentItems, enableUserDataButtons: false, @@ -1572,20 +1601,20 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti showProgramTime: true, mediaInfo: false, parentTitleWithTitle: true - }) + "
"; + }) + ''; } - page.querySelector(".programGuide").innerHTML = html; + page.querySelector('.programGuide').innerHTML = html; } function renderChannelGuide(page, apiClient, item) { - if ("TvChannel" === item.Type) { - page.querySelector(".programGuideSection").classList.remove("hide"); + if ('TvChannel' === item.Type) { + page.querySelector('.programGuideSection').classList.remove('hide'); apiClient.getLiveTvPrograms({ ChannelIds: item.Id, UserId: apiClient.getCurrentUserId(), HasAired: false, - SortBy: "StartDate", + SortBy: 'StartDate', EnableTotalRecordCount: false, EnableImages: false, ImageTypeLimit: 0, @@ -1601,7 +1630,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti apiClient.getLiveTvPrograms({ UserId: apiClient.getCurrentUserId(), HasAired: false, - SortBy: "StartDate", + SortBy: 'StartDate', EnableTotalRecordCount: false, EnableImages: false, ImageTypeLimit: 0, @@ -1610,12 +1639,12 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti LibrarySeriesId: item.Id }).then(function (result) { if (result.Items.length) { - page.querySelector("#seriesScheduleSection").classList.remove("hide"); + page.querySelector('#seriesScheduleSection').classList.remove('hide'); } else { - page.querySelector("#seriesScheduleSection").classList.add("hide"); + page.querySelector('#seriesScheduleSection').classList.add('hide'); } - page.querySelector("#seriesScheduleList").innerHTML = listView.getListViewHtml({ + page.querySelector('#seriesScheduleList').innerHTML = listView.getListViewHtml({ items: result.Items, enableUserDataButtons: false, showParentTitle: false, @@ -1624,27 +1653,27 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti mediaInfo: false, showTitle: true, moreButton: false, - action: "programdialog" + action: 'programdialog' }); loading.hide(); }); } function inferContext(item) { - if ("Movie" === item.Type || "BoxSet" === item.Type) { - return "movies"; + if ('Movie' === item.Type || 'BoxSet' === item.Type) { + return 'movies'; } - if ("Series" === item.Type || "Season" === item.Type || "Episode" === item.Type) { - return "tvshows"; + if ('Series' === item.Type || 'Season' === item.Type || 'Episode' === item.Type) { + return 'tvshows'; } - if ("MusicArtist" === item.Type || "MusicAlbum" === item.Type || "Audio" === item.Type || "AudioBook" === item.Type) { - return "music"; + if ('MusicArtist' === item.Type || 'MusicAlbum' === item.Type || 'Audio' === item.Type || 'AudioBook' === item.Type) { + return 'music'; } - if ("Program" === item.Type) { - return "livetv"; + if ('Program' === item.Type) { + return 'livetv'; } return null; @@ -1673,7 +1702,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function renderCollectionItems(page, parentItem, types, items) { - page.querySelector(".collectionItems").innerHTML = ""; + page.querySelector('.collectionItems').innerHTML = ''; var i; var length; @@ -1687,7 +1716,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } var otherType = { - name: globalize.translate("HeaderOtherItems") + name: globalize.translate('HeaderOtherItems') }; var otherTypeItems = items.filter(function (curr) { return !types.filter(function (t) { @@ -1701,11 +1730,11 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti if (!items.length) { renderCollectionItemType(page, parentItem, { - name: globalize.translate("HeaderItems") + name: globalize.translate('HeaderItems') }, items); } - var containers = page.querySelectorAll(".collectionItemsContainer"); + var containers = page.querySelectorAll('.collectionItemsContainer'); var notifyRefreshNeeded = function () { renderChildren(page, parentItem); @@ -1717,33 +1746,33 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti // if nothing in the collection can be played hide play and shuffle buttons if (!canPlaySomeItemInCollection(items)) { - hideAll(page, "btnPlay", false); - hideAll(page, "btnShuffle", false); + hideAll(page, 'btnPlay', false); + hideAll(page, 'btnShuffle', false); } // HACK: Call autoFocuser again because btnPlay may be hidden, but focused by reloadFromItem // FIXME: Sometimes focus does not move until all (?) sections are loaded - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); } function renderCollectionItemType(page, parentItem, type, items) { - var html = ""; + var html = ''; html += '
'; html += '
'; html += '

'; - html += "" + type.name + ""; - html += "

"; - html += ''; - html += "
"; + html += '' + type.name + ''; + html += ''; + html += ''; + html += '
'; html += '
'; - var shape = "MusicAlbum" == type.type ? getSquareShape(false) : getPortraitShape(false); + var shape = 'MusicAlbum' == type.type ? getSquareShape(false) : getPortraitShape(false); html += cardBuilder.getCardsHtml({ items: items, shape: shape, showTitle: true, - showYear: "Video" === type.mediaType || "Series" === type.type, + showYear: 'Video' === type.mediaType || 'Series' === type.type, centerText: true, lazy: true, showDetailsMenu: true, @@ -1752,16 +1781,16 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti showRemoveFromCollection: true, collectionId: parentItem.Id }); - html += "
"; - html += ""; - var collectionItems = page.querySelector(".collectionItems"); - collectionItems.insertAdjacentHTML("beforeend", html); + html += ''; + html += ''; + var collectionItems = page.querySelector('.collectionItems'); + collectionItems.insertAdjacentHTML('beforeend', html); imageLoader.lazyChildren(collectionItems); - collectionItems.querySelector(".btnAddToCollection").addEventListener("click", function () { - require(["alert"], function (alert) { + collectionItems.querySelector('.btnAddToCollection').addEventListener('click', function () { + require(['alert'], function (alert) { alert({ - text: globalize.translate("AddItemToCollectionHelp"), - html: globalize.translate("AddItemToCollectionHelp") + '

' + globalize.translate("ButtonLearnMore") + "" + text: globalize.translate('AddItemToCollectionHelp'), + html: globalize.translate('AddItemToCollectionHelp') + '

' + globalize.translate('ButtonLearnMore') + '' }); }); }); @@ -1769,20 +1798,20 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti function renderMusicVideos(page, item, user) { connectionManager.getApiClient(item.ServerId).getItems(user.Id, { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "MusicVideo", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'MusicVideo', Recursive: true, - Fields: "PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount', AlbumIds: item.Id }).then(function (result) { if (result.Items.length) { - page.querySelector("#musicVideosCollapsible").classList.remove("hide"); - var musicVideosContent = page.querySelector(".musicVideosContent"); + page.querySelector('#musicVideosCollapsible').classList.remove('hide'); + var musicVideosContent = page.querySelector('.musicVideosContent'); musicVideosContent.innerHTML = getVideosHtml(result.Items, user); imageLoader.lazyChildren(musicVideosContent); } else { - page.querySelector("#musicVideosCollapsible").classList.add("hide"); + page.querySelector('#musicVideosCollapsible').classList.add('hide'); } }); } @@ -1790,12 +1819,12 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti function renderAdditionalParts(page, item, user) { connectionManager.getApiClient(item.ServerId).getAdditionalVideoParts(user.Id, item.Id).then(function (result) { if (result.Items.length) { - page.querySelector("#additionalPartsCollapsible").classList.remove("hide"); - var additionalPartsContent = page.querySelector("#additionalPartsContent"); + page.querySelector('#additionalPartsCollapsible').classList.remove('hide'); + var additionalPartsContent = page.querySelector('#additionalPartsContent'); additionalPartsContent.innerHTML = getVideosHtml(result.Items, user); imageLoader.lazyChildren(additionalPartsContent); } else { - page.querySelector("#additionalPartsCollapsible").classList.add("hide"); + page.querySelector('#additionalPartsCollapsible').classList.add('hide'); } }); } @@ -1804,34 +1833,34 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti var chapters = item.Chapters || []; if (chapters.length && !chapters[0].ImageTag && (chapters = []), chapters.length) { - page.querySelector("#scenesCollapsible").classList.remove("hide"); - var scenesContent = page.querySelector("#scenesContent"); + page.querySelector('#scenesCollapsible').classList.remove('hide'); + var scenesContent = page.querySelector('#scenesContent'); - require(["chaptercardbuilder"], function (chaptercardbuilder) { + require(['chaptercardbuilder'], function (chaptercardbuilder) { chaptercardbuilder.buildChapterCards(item, chapters, { itemsContainer: scenesContent, - backdropShape: "overflowBackdrop", - squareShape: "overflowSquare" + backdropShape: 'overflowBackdrop', + squareShape: 'overflowSquare' }); }); } else { - page.querySelector("#scenesCollapsible").classList.add("hide"); + page.querySelector('#scenesCollapsible').classList.add('hide'); } } function getVideosHtml(items, user, limit, moreButtonClass) { var html = cardBuilder.getCardsHtml({ items: items, - shape: "auto", + shape: 'auto', showTitle: true, - action: "play", + action: 'play', overlayText: false, centerText: true, showRuntime: true }); if (limit && items.length > limit) { - html += '

"; + html += '

'; } return html; @@ -1839,30 +1868,31 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti function renderSpecials(page, item, user, limit) { connectionManager.getApiClient(item.ServerId).getSpecialFeatures(user.Id, item.Id).then(function (specials) { - var specialsContent = page.querySelector("#specialsContent"); - specialsContent.innerHTML = getVideosHtml(specials, user, limit, "moreSpecials"); + var specialsContent = page.querySelector('#specialsContent'); + specialsContent.innerHTML = getVideosHtml(specials, user, limit, 'moreSpecials'); imageLoader.lazyChildren(specialsContent); }); } function renderCast(page, item) { var people = (item.People || []).filter(function (p) { - return "Director" !== p.Type; + return 'Director' !== p.Type; }); if (!people.length) { - return void page.querySelector("#castCollapsible").classList.add("hide"); + return void page.querySelector('#castCollapsible').classList.add('hide'); } - page.querySelector("#castCollapsible").classList.remove("hide"); - var castContent = page.querySelector("#castContent"); + page.querySelector('#castCollapsible').classList.remove('hide'); + var castContent = page.querySelector('#castContent'); - require(["peoplecardbuilder"], function (peoplecardbuilder) { + require(['peoplecardbuilder'], function (peoplecardbuilder) { peoplecardbuilder.buildPeopleCards(people, { itemsContainer: castContent, coverImage: true, serverId: item.ServerId, - shape: "overflowPortrait" + shape: 'overflowPortrait', + imageBlurhashes: item.ImageBlurHashes }); }); } @@ -1904,12 +1934,12 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function splitVersions(instance, page, apiClient, params) { - require(["confirm"], function (confirm) { - confirm("Are you sure you wish to split the media sources into separate items?", "Split Media Apart").then(function () { + require(['confirm'], function (confirm) { + confirm('Are you sure you wish to split the media sources into separate items?', 'Split Media Apart').then(function () { loading.show(); apiClient.ajax({ - type: "DELETE", - url: apiClient.getUrl("Videos/" + params.id + "/AlternateSources") + type: 'DELETE', + url: apiClient.getUrl('Videos/' + params.id + '/AlternateSources') }).then(function () { loading.hide(); reload(instance, page, params); @@ -1919,12 +1949,12 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function getPlayOptions(startPosition) { - var audioStreamIndex = view.querySelector(".selectAudio").value || null; + var audioStreamIndex = view.querySelector('.selectAudio').value || null; return { startPositionTicks: startPosition, - mediaSourceId: view.querySelector(".selectSource").value, + mediaSourceId: view.querySelector('.selectSource').value, audioStreamIndex: audioStreamIndex, - subtitleStreamIndex: view.querySelector(".selectSubtitles").value + subtitleStreamIndex: view.querySelector('.selectSubtitles').value }; } @@ -1941,7 +1971,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti function playCurrentItem(button, mode) { var item = currentItem; - if ("Program" === item.Type) { + if ('Program' === item.Type) { var apiClient = connectionManager.getApiClient(item.ServerId); return void apiClient.getLiveTvChannel(item.ChannelId, apiClient.getCurrentUserId()).then(function (channel) { playbackManager.play({ @@ -1950,11 +1980,11 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti }); } - playItem(item, item.UserData && "resume" === mode ? item.UserData.PlaybackPositionTicks : 0); + playItem(item, item.UserData && 'resume' === mode ? item.UserData.PlaybackPositionTicks : 0); } function onPlayClick() { - playCurrentItem(this, this.getAttribute("data-mode")); + playCurrentItem(this, this.getAttribute('data-mode')); } function onInstantMixClick() { @@ -1966,7 +1996,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function onDeleteClick() { - require(["deleteHelper"], function (deleteHelper) { + require(['deleteHelper'], function (deleteHelper) { deleteHelper.deleteItem({ item: currentItem, navigate: true @@ -1975,15 +2005,15 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti } function onCancelSeriesTimerClick() { - require(["recordingHelper"], function (recordingHelper) { + require(['recordingHelper'], function (recordingHelper) { recordingHelper.cancelSeriesTimerWithConfirmation(currentItem.Id, currentItem.ServerId).then(function () { - Dashboard.navigate("livetv.html"); + Dashboard.navigate('livetv.html'); }); }); } function onCancelTimerClick() { - require(["recordingHelper"], function (recordingHelper) { + require(['recordingHelper'], function (recordingHelper) { recordingHelper.cancelTimer(connectionManager.getApiClient(currentItem.ServerId), currentItem.TimerId).then(function () { reload(self, view, params); }); @@ -2025,7 +2055,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti function editImages() { return new Promise(function (resolve, reject) { - require(["imageEditor"], function (imageEditor) { + require(['imageEditor'], function (imageEditor) { imageEditor.show({ itemId: currentItem.Id, serverId: currentItem.ServerId @@ -2037,7 +2067,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti function onWebSocketMessage(e, data) { var msg = data; - if ("UserDataChanged" === msg.MessageType && currentItem && msg.Data.UserId == apiClient.getCurrentUserId()) { + if ('UserDataChanged' === msg.MessageType && currentItem && msg.Data.UserId == apiClient.getCurrentUserId()) { var key = currentItem.UserData.Key; var userData = msg.Data.UserDataList.filter(function (u) { return u.Key == key; @@ -2054,46 +2084,45 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti var currentItem; var self = this; var apiClient = params.serverId ? connectionManager.getApiClient(params.serverId) : ApiClient; - view.querySelectorAll(".btnPlay"); - bindAll(view, ".btnPlay", "click", onPlayClick); - bindAll(view, ".btnResume", "click", onPlayClick); - bindAll(view, ".btnInstantMix", "click", onInstantMixClick); - bindAll(view, ".btnShuffle", "click", onShuffleClick); - bindAll(view, ".btnPlayTrailer", "click", onPlayTrailerClick); - bindAll(view, ".btnCancelSeriesTimer", "click", onCancelSeriesTimerClick); - bindAll(view, ".btnCancelTimer", "click", onCancelTimerClick); - bindAll(view, ".btnDeleteItem", "click", onDeleteClick); - bindAll(view, ".btnDownload", "click", onDownloadClick); - view.querySelector(".btnMoreCommands i").innerHTML = ""; - view.querySelector(".trackSelections").addEventListener("submit", onTrackSelectionsSubmit); - view.querySelector(".btnSplitVersions").addEventListener("click", function () { + view.querySelectorAll('.btnPlay'); + bindAll(view, '.btnPlay', 'click', onPlayClick); + bindAll(view, '.btnResume', 'click', onPlayClick); + bindAll(view, '.btnInstantMix', 'click', onInstantMixClick); + bindAll(view, '.btnShuffle', 'click', onShuffleClick); + bindAll(view, '.btnPlayTrailer', 'click', onPlayTrailerClick); + bindAll(view, '.btnCancelSeriesTimer', 'click', onCancelSeriesTimerClick); + bindAll(view, '.btnCancelTimer', 'click', onCancelTimerClick); + bindAll(view, '.btnDeleteItem', 'click', onDeleteClick); + bindAll(view, '.btnDownload', 'click', onDownloadClick); + view.querySelector('.trackSelections').addEventListener('submit', onTrackSelectionsSubmit); + view.querySelector('.btnSplitVersions').addEventListener('click', function () { splitVersions(self, view, apiClient, params); }); - bindAll(view, ".btnMoreCommands", "click", onMoreCommandsClick); - view.querySelector(".selectSource").addEventListener("change", function () { + bindAll(view, '.btnMoreCommands', 'click', onMoreCommandsClick); + view.querySelector('.selectSource').addEventListener('change', function () { renderVideoSelections(view, self._currentPlaybackMediaSources); renderAudioSelections(view, self._currentPlaybackMediaSources); renderSubtitleSelections(view, self._currentPlaybackMediaSources); }); - view.addEventListener("click", function (e) { - if (dom.parentWithClass(e.target, "moreScenes")) { + view.addEventListener('click', function (e) { + if (dom.parentWithClass(e.target, 'moreScenes')) { renderScenes(view, currentItem); - } else if (dom.parentWithClass(e.target, "morePeople")) { + } else if (dom.parentWithClass(e.target, 'morePeople')) { renderCast(view, currentItem); - } else if (dom.parentWithClass(e.target, "moreSpecials")) { + } else if (dom.parentWithClass(e.target, 'moreSpecials')) { apiClient.getCurrentUser().then(function (user) { renderSpecials(view, currentItem, user); }); } }); - view.querySelector(".detailImageContainer").addEventListener("click", function (e) { - if (dom.parentWithClass(e.target, "itemDetailGalleryLink")) { + view.querySelector('.detailImageContainer').addEventListener('click', function (e) { + if (dom.parentWithClass(e.target, 'itemDetailGalleryLink')) { editImages().then(function () { reload(self, view, params); }); } }); - view.addEventListener("viewshow", function (e) { + view.addEventListener('viewshow', function (e) { var page = this; if (layoutManager.mobile) { @@ -2102,22 +2131,22 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti if (e.detail.isRestored) { if (currentItem) { - setTitle(currentItem, connectionManager.getApiClient(currentItem.ServerId)); + Emby.Page.setTitle(''); renderTrackSelections(page, self, currentItem, true); } } else { reload(self, page, params); } - events.on(apiClient, "message", onWebSocketMessage); - events.on(playbackManager, "playerchange", onPlayerChange); + events.on(apiClient, 'message', onWebSocketMessage); + events.on(playbackManager, 'playerchange', onPlayerChange); }); - view.addEventListener("viewbeforehide", function () { - events.off(apiClient, "message", onWebSocketMessage); - events.off(playbackManager, "playerchange", onPlayerChange); + view.addEventListener('viewbeforehide', function () { + events.off(apiClient, 'message', onWebSocketMessage); + events.off(playbackManager, 'playerchange', onPlayerChange); libraryMenu.setTransparentMenu(false); }); - view.addEventListener("viewdestroy", function () { + view.addEventListener('viewdestroy', function () { currentItem = null; self._currentPlaybackMediaSources = null; self.currentRecordingFields = null; diff --git a/src/controllers/librarydisplay.js b/src/controllers/librarydisplay.js deleted file mode 100644 index 97e323e665..0000000000 --- a/src/controllers/librarydisplay.js +++ /dev/null @@ -1,71 +0,0 @@ -define(["globalize", "loading", "libraryMenu", "emby-checkbox", "emby-button", "emby-button"], function(globalize, loading, libraryMenu) { - "use strict"; - - function getTabs() { - return [{ - href: "library.html", - name: Globalize.translate("HeaderLibraries") - }, { - href: "librarydisplay.html", - name: Globalize.translate("TabDisplay") - }, { - href: "metadataimages.html", - name: Globalize.translate("TabMetadata") - }, { - href: "metadatanfo.html", - name: Globalize.translate("TabNfoSettings") - }]; - } - - return function(view, params) { - function loadData() { - ApiClient.getServerConfiguration().then(function(config) { - view.querySelector(".chkFolderView").checked = config.EnableFolderView; - view.querySelector(".chkGroupMoviesIntoCollections").checked = config.EnableGroupingIntoCollections; - view.querySelector(".chkDisplaySpecialsWithinSeasons").checked = config.DisplaySpecialsWithinSeasons; - view.querySelector(".chkExternalContentInSuggestions").checked = config.EnableExternalContentInSuggestions; - view.querySelector("#chkSaveMetadataHidden").checked = config.SaveMetadataHidden; - }); - ApiClient.getNamedConfiguration("metadata").then(function(metadata) { - loadMetadataConfig(this, metadata); - }); - } - - function loadMetadataConfig(page, config) { - $("#selectDateAdded", page).val(config.UseFileCreationTimeForDateAdded ? "1" : "0"); - } - - view.querySelector("form").addEventListener("submit", function(e) { - loading.show(); - var form = this; - ApiClient.getServerConfiguration().then(function(config) { - config.EnableFolderView = form.querySelector(".chkFolderView").checked; - config.EnableGroupingIntoCollections = form.querySelector(".chkGroupMoviesIntoCollections").checked; - config.DisplaySpecialsWithinSeasons = form.querySelector(".chkDisplaySpecialsWithinSeasons").checked; - config.EnableExternalContentInSuggestions = form.querySelector(".chkExternalContentInSuggestions").checked; - config.SaveMetadataHidden = form.querySelector("#chkSaveMetadataHidden").checked; - ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult); - }); - ApiClient.getNamedConfiguration("metadata").then(function(config) { - config.UseFileCreationTimeForDateAdded = "1" === $("#selectDateAdded", form).val(); - ApiClient.updateNamedConfiguration("metadata", config); - }); - - e.preventDefault(); - loading.hide(); - return false; - }); - - view.addEventListener("viewshow", function() { - libraryMenu.setTabs("librarysetup", 1, getTabs); - loadData(); - ApiClient.getSystemInfo().then(function(info) { - if ("Windows" === info.OperatingSystem) { - view.querySelector(".fldSaveMetadataHidden").classList.remove("hide"); - } else { - view.querySelector(".fldSaveMetadataHidden").classList.add("hide"); - } - }); - }); - }; -}); diff --git a/src/controllers/list.js b/src/controllers/list.js index bcc38f27cf..d05616ec9d 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -1,15 +1,15 @@ -define(["globalize", "listView", "layoutManager", "userSettings", "focusManager", "cardBuilder", "loading", "connectionManager", "alphaNumericShortcuts", "scroller", "playbackManager", "alphaPicker", "emby-itemscontainer", "emby-scroller"], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, scroller, playbackManager, alphaPicker) { - "use strict"; +define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager', 'cardBuilder', 'loading', 'connectionManager', 'alphaNumericShortcuts', 'scroller', 'playbackManager', 'alphaPicker', 'emby-itemscontainer', 'emby-scroller'], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, scroller, playbackManager, alphaPicker) { + 'use strict'; function getInitialLiveTvQuery(instance, params) { var query = { UserId: connectionManager.getApiClient(params.serverId).getCurrentUserId(), StartIndex: 0, - Fields: "ChannelInfo,PrimaryImageAspectRatio", + Fields: 'ChannelInfo,PrimaryImageAspectRatio', Limit: 300 }; - if ("Recordings" === params.type) { + if ('Recordings' === params.type) { query.IsInProgress = false; } else { query.HasAired = false; @@ -19,39 +19,39 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" query.GenreIds = params.genreId; } - if ("true" === params.IsMovie) { + if ('true' === params.IsMovie) { query.IsMovie = true; - } else if ("false" === params.IsMovie) { + } else if ('false' === params.IsMovie) { query.IsMovie = false; } - if ("true" === params.IsSeries) { + if ('true' === params.IsSeries) { query.IsSeries = true; - } else if ("false" === params.IsSeries) { + } else if ('false' === params.IsSeries) { query.IsSeries = false; } - if ("true" === params.IsNews) { + if ('true' === params.IsNews) { query.IsNews = true; - } else if ("false" === params.IsNews) { + } else if ('false' === params.IsNews) { query.IsNews = false; } - if ("true" === params.IsSports) { + if ('true' === params.IsSports) { query.IsSports = true; - } else if ("false" === params.IsSports) { + } else if ('false' === params.IsSports) { query.IsSports = false; } - if ("true" === params.IsKids) { + if ('true' === params.IsKids) { query.IsKids = true; - } else if ("false" === params.IsKids) { + } else if ('false' === params.IsKids) { query.IsKids = false; } - if ("true" === params.IsAiring) { + if ('true' === params.IsAiring) { query.IsAiring = true; - } else if ("false" === params.IsAiring) { + } else if ('false' === params.IsAiring) { query.IsAiring = false; } @@ -66,29 +66,29 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" query.SortOrder = sortValues.sortOrder; } - query.Fields = query.Fields ? query.Fields + ",PrimaryImageAspectRatio" : "PrimaryImageAspectRatio"; + query.Fields = query.Fields ? query.Fields + ',PrimaryImageAspectRatio' : 'PrimaryImageAspectRatio'; query.ImageTypeLimit = 1; var hasFilters; var queryFilters = []; var filters = instance.getFilters(); if (filters.IsPlayed) { - queryFilters.push("IsPlayed"); + queryFilters.push('IsPlayed'); hasFilters = true; } if (filters.IsUnplayed) { - queryFilters.push("IsUnplayed"); + queryFilters.push('IsUnplayed'); hasFilters = true; } if (filters.IsFavorite) { - queryFilters.push("IsFavorite"); + queryFilters.push('IsFavorite'); hasFilters = true; } if (filters.IsResumable) { - queryFilters.push("IsResumable"); + queryFilters.push('IsResumable'); hasFilters = true; } @@ -147,7 +147,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" hasFilters = true; } - query.Filters = queryFilters.length ? queryFilters.join(",") : null; + query.Filters = queryFilters.length ? queryFilters.join(',') : null; instance.setFilterStatus(hasFilters); if (instance.alphaPicker) { @@ -158,8 +158,8 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" } function setSortButtonIcon(btnSortIcon, icon) { - btnSortIcon.classList.remove("arrow_downward"); - btnSortIcon.classList.remove("arrow_upward"); + btnSortIcon.classList.remove('arrow_downward'); + btnSortIcon.classList.remove('arrow_upward'); btnSortIcon.classList.add(icon); } @@ -173,7 +173,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" for (var i = 0, length = options.length; i < length; i++) { if (sortBy === options[i].value) { - btnSortText.innerHTML = globalize.translate("SortByValue", options[i].name); + btnSortText.innerHTML = globalize.translate('SortByValue', options[i].name); break; } } @@ -181,18 +181,18 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" var btnSortIcon = instance.btnSortIcon; if (btnSortIcon) { - setSortButtonIcon(btnSortIcon, "Descending" === values.sortOrder ? "arrow_downward" : "arrow_upward"); + setSortButtonIcon(btnSortIcon, 'Descending' === values.sortOrder ? 'arrow_downward' : 'arrow_upward'); } } } function updateItemsContainerForViewType(instance) { - if ("list" === instance.getViewSettings().imageType) { - instance.itemsContainer.classList.remove("vertical-wrap"); - instance.itemsContainer.classList.add("vertical-list"); + if ('list' === instance.getViewSettings().imageType) { + instance.itemsContainer.classList.remove('vertical-wrap'); + instance.itemsContainer.classList.add('vertical-list'); } else { - instance.itemsContainer.classList.add("vertical-wrap"); - instance.itemsContainer.classList.remove("vertical-list"); + instance.itemsContainer.classList.add('vertical-wrap'); + instance.itemsContainer.classList.remove('vertical-list'); } } @@ -207,12 +207,12 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" numItems = 100; } - if ("SortName" === values.sortBy && "Ascending" === values.sortOrder && numItems > 40) { - alphaPicker.classList.remove("hide"); - instance.itemsContainer.parentNode.classList.add("padded-right-withalphapicker"); + if ('SortName' === values.sortBy && 'Ascending' === values.sortOrder && numItems > 40) { + alphaPicker.classList.remove('hide'); + instance.itemsContainer.parentNode.classList.add('padded-right-withalphapicker'); } else { - alphaPicker.classList.add("hide"); - instance.itemsContainer.parentNode.classList.remove("padded-right-withalphapicker"); + alphaPicker.classList.add('hide'); + instance.itemsContainer.parentNode.classList.remove('padded-right-withalphapicker'); } } } @@ -222,25 +222,25 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" var apiClient = connectionManager.getApiClient(params.serverId); instance.queryRecursive = false; - if ("Recordings" === params.type) { + if ('Recordings' === params.type) { return apiClient.getLiveTvRecordings(getInitialLiveTvQuery(instance, params)); } - if ("Programs" === params.type) { - if ("true" === params.IsAiring) { + if ('Programs' === params.type) { + if ('true' === params.IsAiring) { return apiClient.getLiveTvRecommendedPrograms(getInitialLiveTvQuery(instance, params)); } return apiClient.getLiveTvPrograms(getInitialLiveTvQuery(instance, params)); } - if ("nextup" === params.type) { + if ('nextup' === params.type) { return apiClient.getNextUpEpisodes(modifyQueryWithFilters(instance, { Limit: limit, - Fields: "PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo', UserId: apiClient.getCurrentUserId(), ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Thumb", + EnableImageTypes: 'Primary,Backdrop,Thumb', EnableTotalRecordCount: false, SortBy: sortBy })); @@ -248,57 +248,57 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" if (!item) { instance.queryRecursive = true; - var method = "getItems"; + var method = 'getItems'; - if ("MusicArtist" === params.type) { - method = "getArtists"; - } else if ("Person" === params.type) { - method = "getPeople"; + if ('MusicArtist' === params.type) { + method = 'getArtists'; + } else if ('Person' === params.type) { + method = 'getPeople'; } return apiClient[method](apiClient.getCurrentUserId(), modifyQueryWithFilters(instance, { StartIndex: startIndex, Limit: limit, - Fields: "PrimaryImageAspectRatio,SortName", + Fields: 'PrimaryImageAspectRatio,SortName', ImageTypeLimit: 1, - IncludeItemTypes: "MusicArtist" === params.type || "Person" === params.type ? null : params.type, + IncludeItemTypes: 'MusicArtist' === params.type || 'Person' === params.type ? null : params.type, Recursive: true, - IsFavorite: "true" === params.IsFavorite || null, + IsFavorite: 'true' === params.IsFavorite || null, ArtistIds: params.artistId || null, SortBy: sortBy })); } - if ("Genre" === item.Type || "MusicGenre" === item.Type || "Studio" === item.Type || "Person" === item.Type) { + if ('Genre' === item.Type || 'MusicGenre' === item.Type || 'Studio' === item.Type || 'Person' === item.Type) { instance.queryRecursive = true; var query = { StartIndex: startIndex, Limit: limit, - Fields: "PrimaryImageAspectRatio,SortName", + Fields: 'PrimaryImageAspectRatio,SortName', Recursive: true, parentId: params.parentId, SortBy: sortBy }; - if ("Studio" === item.Type) { + if ('Studio' === item.Type) { query.StudioIds = item.Id; - } else if ("Genre" === item.Type || "MusicGenre" === item.Type) { + } else if ('Genre' === item.Type || 'MusicGenre' === item.Type) { query.GenreIds = item.Id; - } else if ("Person" === item.Type) { + } else if ('Person' === item.Type) { query.PersonIds = item.Id; } - if ("MusicGenre" === item.Type) { - query.IncludeItemTypes = "MusicAlbum"; - } else if ("GameGenre" === item.Type) { - query.IncludeItemTypes = "Game"; - } else if ("movies" === item.CollectionType) { - query.IncludeItemTypes = "Movie"; - } else if ("tvshows" === item.CollectionType) { - query.IncludeItemTypes = "Series"; - } else if ("Genre" === item.Type) { - query.IncludeItemTypes = "Movie,Series,Video"; - } else if ("Person" === item.Type) { + if ('MusicGenre' === item.Type) { + query.IncludeItemTypes = 'MusicAlbum'; + } else if ('GameGenre' === item.Type) { + query.IncludeItemTypes = 'Game'; + } else if ('movies' === item.CollectionType) { + query.IncludeItemTypes = 'Movie'; + } else if ('tvshows' === item.CollectionType) { + query.IncludeItemTypes = 'Series'; + } else if ('Genre' === item.Type) { + query.IncludeItemTypes = 'Movie,Series,Video'; + } else if ('Person' === item.Type) { query.IncludeItemTypes = params.type; } @@ -308,7 +308,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" return apiClient.getItems(apiClient.getCurrentUserId(), modifyQueryWithFilters(instance, { StartIndex: startIndex, Limit: limit, - Fields: "PrimaryImageAspectRatio,SortName", + Fields: 'PrimaryImageAspectRatio,SortName,Path', ImageTypeLimit: 1, ParentId: item.Id, SortBy: sortBy @@ -316,7 +316,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" } function getItem(params) { - if ("Recordings" === params.type || "Programs" === params.type || "nextup" === params.type) { + if ('Recordings' === params.type || 'Programs' === params.type || 'nextup' === params.type) { return Promise.resolve(null); } @@ -333,7 +333,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" function showViewSettingsMenu() { var instance = this; - require(["viewSettings"], function (ViewSettings) { + require(['viewSettings'], function (ViewSettings) { new ViewSettings().show({ settingsKey: instance.getSettingsKey(), settings: instance.getViewSettings(), @@ -348,7 +348,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" function showFilterMenu() { var instance = this; - require(["filterMenu"], function (FilterMenu) { + require(['filterMenu'], function (FilterMenu) { new FilterMenu().show({ settingsKey: instance.getSettingsKey(), settings: instance.getFilters(), @@ -367,7 +367,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" function showSortMenu() { var instance = this; - require(["sortMenu"], function (SortMenu) { + require(['sortMenu'], function (SortMenu) { new SortMenu().show({ settingsKey: instance.getSettingsKey(), settings: instance.getSortValues(), @@ -385,7 +385,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" function onNewItemClick() { var instance = this; - require(["playlistEditor"], function (playlistEditor) { + require(['playlistEditor'], function (playlistEditor) { new playlistEditor().show({ items: [], serverId: instance.params.serverId @@ -396,9 +396,9 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" function hideOrShowAll(elems, hide) { for (var i = 0, length = elems.length; i < length; i++) { if (hide) { - elems[i].classList.add("hide"); + elems[i].classList.add('hide'); } else { - elems[i].classList.remove("hide"); + elems[i].classList.remove('hide'); } } } @@ -424,7 +424,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" function getItemsHtml(items) { var settings = self.getViewSettings(); - if ("list" === settings.imageType) { + if ('list' === settings.imageType) { return listView.getListViewHtml({ items: items }); @@ -438,26 +438,26 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" var item = self.currentItem; var lines = settings.showTitle ? 2 : 0; - if ("banner" === settings.imageType) { - shape = "banner"; - } else if ("disc" === settings.imageType) { - shape = "square"; + if ('banner' === settings.imageType) { + shape = 'banner'; + } else if ('disc' === settings.imageType) { + shape = 'square'; preferDisc = true; - } else if ("logo" === settings.imageType) { - shape = "backdrop"; + } else if ('logo' === settings.imageType) { + shape = 'backdrop'; preferLogo = true; - } else if ("thumb" === settings.imageType) { - shape = "backdrop"; + } else if ('thumb' === settings.imageType) { + shape = 'backdrop'; preferThumb = true; - } else if ("nextup" === params.type) { - shape = "backdrop"; - preferThumb = "thumb" === settings.imageType; - } else if ("Programs" === params.type || "Recordings" === params.type) { - shape = "true" === params.IsMovie ? "portrait" : "autoVertical"; - preferThumb = "true" !== params.IsMovie ? "auto" : false; - defaultShape = "true" === params.IsMovie ? "portrait" : "backdrop"; + } else if ('nextup' === params.type) { + shape = 'backdrop'; + preferThumb = 'thumb' === settings.imageType; + } else if ('Programs' === params.type || 'Recordings' === params.type) { + shape = 'true' === params.IsMovie ? 'portrait' : 'autoVertical'; + preferThumb = 'true' !== params.IsMovie ? 'auto' : false; + defaultShape = 'true' === params.IsMovie ? 'portrait' : 'backdrop'; } else { - shape = "autoVertical"; + shape = 'autoVertical'; } var posterOptions = { @@ -473,47 +473,47 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" overlayMoreButton: true, overlayText: !settings.showTitle, defaultShape: defaultShape, - action: "Audio" === params.type ? "playallfromhere" : null + action: 'Audio' === params.type ? 'playallfromhere' : null }; - if ("nextup" === params.type) { + if ('nextup' === params.type) { posterOptions.showParentTitle = settings.showTitle; - } else if ("Person" === params.type) { + } else if ('Person' === params.type) { posterOptions.showYear = false; posterOptions.showParentTitle = false; lines = 1; - } else if ("Audio" === params.type) { + } else if ('Audio' === params.type) { posterOptions.showParentTitle = settings.showTitle; - } else if ("MusicAlbum" === params.type) { + } else if ('MusicAlbum' === params.type) { posterOptions.showParentTitle = settings.showTitle; - } else if ("Episode" === params.type) { + } else if ('Episode' === params.type) { posterOptions.showParentTitle = settings.showTitle; - } else if ("MusicArtist" === params.type) { + } else if ('MusicArtist' === params.type) { posterOptions.showYear = false; lines = 1; - } else if ("Programs" === params.type) { + } else if ('Programs' === params.type) { lines = settings.showTitle ? 1 : 0; - var showParentTitle = settings.showTitle && "true" !== params.IsMovie; + var showParentTitle = settings.showTitle && 'true' !== params.IsMovie; if (showParentTitle) { lines++; } - var showAirTime = settings.showTitle && "Recordings" !== params.type; + var showAirTime = settings.showTitle && 'Recordings' !== params.type; if (showAirTime) { lines++; } - var showYear = settings.showTitle && "true" === params.IsMovie && "Recordings" === params.type; + var showYear = settings.showTitle && 'true' === params.IsMovie && 'Recordings' === params.type; if (showYear) { lines++; } posterOptions = Object.assign(posterOptions, { - inheritThumb: "Recordings" === params.type, - context: "livetv", + inheritThumb: 'Recordings' === params.type, + context: 'livetv', showParentTitle: showParentTitle, showAirTime: showAirTime, showAirDateTime: showAirTime, @@ -529,28 +529,28 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" posterOptions.lines = lines; posterOptions.items = items; - if (item && "folders" === item.CollectionType) { - posterOptions.context = "folders"; + if (item && 'folders' === item.CollectionType) { + posterOptions.context = 'folders'; } return cardBuilder.getCardsHtml(posterOptions); } function initAlphaPicker() { - self.scroller = view.querySelector(".scrollFrameY"); + self.scroller = view.querySelector('.scrollFrameY'); var alphaPickerElement = self.alphaPickerElement; - alphaPickerElement.classList.add("alphaPicker-fixed-right"); - alphaPickerElement.classList.add("focuscontainer-right"); - self.itemsContainer.parentNode.classList.add("padded-right-withalphapicker"); + alphaPickerElement.classList.add('alphaPicker-fixed-right'); + alphaPickerElement.classList.add('focuscontainer-right'); + self.itemsContainer.parentNode.classList.add('padded-right-withalphapicker'); self.alphaPicker = new alphaPicker({ element: alphaPickerElement, itemsContainer: layoutManager.tv ? self.itemsContainer : null, - itemClass: "card", - valueChangeEvent: layoutManager.tv ? null : "click" + itemClass: 'card', + valueChangeEvent: layoutManager.tv ? null : 'click' }); - self.alphaPicker.on("alphavaluechanged", onAlphaPickerValueChanged); + self.alphaPicker.on('alphavaluechanged', onAlphaPickerValueChanged); } function onAlphaPickerValueChanged() { @@ -559,90 +559,90 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" } function setTitle(item) { - Emby.Page.setTitle(getTitle(item) || ""); + Emby.Page.setTitle(getTitle(item) || ''); - if (item && "playlists" === item.CollectionType) { - hideOrShowAll(view.querySelectorAll(".btnNewItem"), false); + if (item && 'playlists' === item.CollectionType) { + hideOrShowAll(view.querySelectorAll('.btnNewItem'), false); } else { - hideOrShowAll(view.querySelectorAll(".btnNewItem"), true); + hideOrShowAll(view.querySelectorAll('.btnNewItem'), true); } } function getTitle(item) { - if ("Recordings" === params.type) { - return globalize.translate("Recordings"); + if ('Recordings' === params.type) { + return globalize.translate('Recordings'); } - if ("Programs" === params.type) { - if ("true" === params.IsMovie) { - return globalize.translate("Movies"); + if ('Programs' === params.type) { + if ('true' === params.IsMovie) { + return globalize.translate('Movies'); } - if ("true" === params.IsSports) { - return globalize.translate("Sports"); + if ('true' === params.IsSports) { + return globalize.translate('Sports'); } - if ("true" === params.IsKids) { - return globalize.translate("HeaderForKids"); + if ('true' === params.IsKids) { + return globalize.translate('HeaderForKids'); } - if ("true" === params.IsAiring) { - return globalize.translate("HeaderOnNow"); + if ('true' === params.IsAiring) { + return globalize.translate('HeaderOnNow'); } - if ("true" === params.IsSeries) { - return globalize.translate("Shows"); + if ('true' === params.IsSeries) { + return globalize.translate('Shows'); } - if ("true" === params.IsNews) { - return globalize.translate("News"); + if ('true' === params.IsNews) { + return globalize.translate('News'); } - return globalize.translate("Programs"); + return globalize.translate('Programs'); } - if ("nextup" === params.type) { - return globalize.translate("NextUp"); + if ('nextup' === params.type) { + return globalize.translate('NextUp'); } - if ("favoritemovies" === params.type) { - return globalize.translate("FavoriteMovies"); + if ('favoritemovies' === params.type) { + return globalize.translate('FavoriteMovies'); } if (item) { return item.Name; } - if ("Movie" === params.type) { - return globalize.translate("Movies"); + if ('Movie' === params.type) { + return globalize.translate('Movies'); } - if ("Series" === params.type) { - return globalize.translate("Shows"); + if ('Series' === params.type) { + return globalize.translate('Shows'); } - if ("Season" === params.type) { - return globalize.translate("Seasons"); + if ('Season' === params.type) { + return globalize.translate('Seasons'); } - if ("Episode" === params.type) { - return globalize.translate("Episodes"); + if ('Episode' === params.type) { + return globalize.translate('Episodes'); } - if ("MusicArtist" === params.type) { - return globalize.translate("Artists"); + if ('MusicArtist' === params.type) { + return globalize.translate('Artists'); } - if ("MusicAlbum" === params.type) { - return globalize.translate("Albums"); + if ('MusicAlbum' === params.type) { + return globalize.translate('Albums'); } - if ("Audio" === params.type) { - return globalize.translate("Songs"); + if ('Audio' === params.type) { + return globalize.translate('Songs'); } - if ("Video" === params.type) { - return globalize.translate("Videos"); + if ('Video' === params.type) { + return globalize.translate('Videos'); } return void 0; @@ -686,7 +686,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" if (currentItem && !self.hasFilters) { playbackManager.shuffle(currentItem); } else { - getItems(self, self.params, currentItem, "Random", null, 300).then(function (result) { + getItems(self, self.params, currentItem, 'Random', null, 300).then(function (result) { playbackManager.play({ items: result.Items }); @@ -696,59 +696,59 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" var self = this; self.params = params; - this.itemsContainer = view.querySelector(".itemsContainer"); + this.itemsContainer = view.querySelector('.itemsContainer'); if (params.parentId) { - this.itemsContainer.setAttribute("data-parentid", params.parentId); - } else if ("nextup" === params.type) { - this.itemsContainer.setAttribute("data-monitor", "videoplayback"); - } else if ("favoritemovies" === params.type) { - this.itemsContainer.setAttribute("data-monitor", "markfavorite"); - } else if ("Programs" === params.type) { - this.itemsContainer.setAttribute("data-refreshinterval", "300000"); + this.itemsContainer.setAttribute('data-parentid', params.parentId); + } else if ('nextup' === params.type) { + this.itemsContainer.setAttribute('data-monitor', 'videoplayback'); + } else if ('favoritemovies' === params.type) { + this.itemsContainer.setAttribute('data-monitor', 'markfavorite'); + } else if ('Programs' === params.type) { + this.itemsContainer.setAttribute('data-refreshinterval', '300000'); } var i; var length; - var btnViewSettings = view.querySelectorAll(".btnViewSettings"); + var btnViewSettings = view.querySelectorAll('.btnViewSettings'); for (i = 0, length = btnViewSettings.length; i < length; i++) { - btnViewSettings[i].addEventListener("click", showViewSettingsMenu.bind(this)); + btnViewSettings[i].addEventListener('click', showViewSettingsMenu.bind(this)); } - var filterButtons = view.querySelectorAll(".btnFilter"); + var filterButtons = view.querySelectorAll('.btnFilter'); this.filterButtons = filterButtons; var hasVisibleFilters = this.getVisibleFilters().length; for (i = 0, length = filterButtons.length; i < length; i++) { var btnFilter = filterButtons[i]; - btnFilter.addEventListener("click", showFilterMenu.bind(this)); + btnFilter.addEventListener('click', showFilterMenu.bind(this)); if (hasVisibleFilters) { - btnFilter.classList.remove("hide"); + btnFilter.classList.remove('hide'); } else { - btnFilter.classList.add("hide"); + btnFilter.classList.add('hide'); } } - var sortButtons = view.querySelectorAll(".btnSort"); + var sortButtons = view.querySelectorAll('.btnSort'); for (this.sortButtons = sortButtons, i = 0, length = sortButtons.length; i < length; i++) { var sortButton = sortButtons[i]; - sortButton.addEventListener("click", showSortMenu.bind(this)); + sortButton.addEventListener('click', showSortMenu.bind(this)); - if ("nextup" !== params.type) { - sortButton.classList.remove("hide"); + if ('nextup' !== params.type) { + sortButton.classList.remove('hide'); } } - this.btnSortText = view.querySelector(".btnSortText"); - this.btnSortIcon = view.querySelector(".btnSortIcon"); - bindAll(view.querySelectorAll(".btnNewItem"), "click", onNewItemClick.bind(this)); - this.alphaPickerElement = view.querySelector(".alphaPicker"); + this.btnSortText = view.querySelector('.btnSortText'); + this.btnSortIcon = view.querySelector('.btnSortIcon'); + bindAll(view.querySelectorAll('.btnNewItem'), 'click', onNewItemClick.bind(this)); + this.alphaPickerElement = view.querySelector('.alphaPicker'); self.itemsContainer.fetchData = fetchData; self.itemsContainer.getItemsHtml = getItemsHtml; - view.addEventListener("viewshow", function (e) { + view.addEventListener('viewshow', function (e) { var isRestored = e.detail.isRestored; if (!isRestored) { @@ -772,42 +772,42 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" } }); - if (!isRestored && item && "PhotoAlbum" !== item.Type) { + if (!isRestored && item && 'PhotoAlbum' !== item.Type) { initAlphaPicker(); } var itemType = item ? item.Type : null; - if ("MusicGenre" === itemType || "Programs" !== params.type && "Channel" !== itemType) { - hideOrShowAll(view.querySelectorAll(".btnPlay"), false); + if ('MusicGenre' === itemType || 'Programs' !== params.type && 'Channel' !== itemType) { + hideOrShowAll(view.querySelectorAll('.btnPlay'), false); } else { - hideOrShowAll(view.querySelectorAll(".btnPlay"), true); + hideOrShowAll(view.querySelectorAll('.btnPlay'), true); } - if ("MusicGenre" === itemType || "Programs" !== params.type && "nextup" !== params.type && "Channel" !== itemType) { - hideOrShowAll(view.querySelectorAll(".btnShuffle"), false); + if ('MusicGenre' === itemType || 'Programs' !== params.type && 'nextup' !== params.type && 'Channel' !== itemType) { + hideOrShowAll(view.querySelectorAll('.btnShuffle'), false); } else { - hideOrShowAll(view.querySelectorAll(".btnShuffle"), true); + hideOrShowAll(view.querySelectorAll('.btnShuffle'), true); } if (item && playbackManager.canQueue(item)) { - hideOrShowAll(view.querySelectorAll(".btnQueue"), false); + hideOrShowAll(view.querySelectorAll('.btnQueue'), false); } else { - hideOrShowAll(view.querySelectorAll(".btnQueue"), true); + hideOrShowAll(view.querySelectorAll('.btnQueue'), true); } }); if (!isRestored) { - bindAll(view.querySelectorAll(".btnPlay"), "click", play); - bindAll(view.querySelectorAll(".btnQueue"), "click", queue); - bindAll(view.querySelectorAll(".btnShuffle"), "click", shuffle); + bindAll(view.querySelectorAll('.btnPlay'), 'click', play); + bindAll(view.querySelectorAll('.btnQueue'), 'click', queue); + bindAll(view.querySelectorAll('.btnShuffle'), 'click', shuffle); } this.alphaNumericShortcuts = new AlphaNumericShortcuts({ itemsContainer: self.itemsContainer }); }); - view.addEventListener("viewhide", function (e) { + view.addEventListener('viewhide', function (e) { var itemsContainer = self.itemsContainer; if (itemsContainer) { @@ -821,13 +821,13 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" self.alphaNumericShortcuts = null; } }); - view.addEventListener("viewdestroy", function () { + view.addEventListener('viewdestroy', function () { if (self.listController) { self.listController.destroy(); } if (self.alphaPicker) { - self.alphaPicker.off("alphavaluechanged", onAlphaPickerValueChanged); + self.alphaPicker.off('alphavaluechanged', onAlphaPickerValueChanged); self.alphaPicker.destroy(); } @@ -845,30 +845,30 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" ItemsView.prototype.getFilters = function () { var basekey = this.getSettingsKey(); return { - IsPlayed: "true" === userSettings.getFilter(basekey + "-filter-IsPlayed"), - IsUnplayed: "true" === userSettings.getFilter(basekey + "-filter-IsUnplayed"), - IsFavorite: "true" === userSettings.getFilter(basekey + "-filter-IsFavorite"), - IsResumable: "true" === userSettings.getFilter(basekey + "-filter-IsResumable"), - Is4K: "true" === userSettings.getFilter(basekey + "-filter-Is4K"), - IsHD: "true" === userSettings.getFilter(basekey + "-filter-IsHD"), - IsSD: "true" === userSettings.getFilter(basekey + "-filter-IsSD"), - Is3D: "true" === userSettings.getFilter(basekey + "-filter-Is3D"), - VideoTypes: userSettings.getFilter(basekey + "-filter-VideoTypes"), - SeriesStatus: userSettings.getFilter(basekey + "-filter-SeriesStatus"), - HasSubtitles: userSettings.getFilter(basekey + "-filter-HasSubtitles"), - HasTrailer: userSettings.getFilter(basekey + "-filter-HasTrailer"), - HasSpecialFeature: userSettings.getFilter(basekey + "-filter-HasSpecialFeature"), - HasThemeSong: userSettings.getFilter(basekey + "-filter-HasThemeSong"), - HasThemeVideo: userSettings.getFilter(basekey + "-filter-HasThemeVideo"), - GenreIds: userSettings.getFilter(basekey + "-filter-GenreIds") + IsPlayed: 'true' === userSettings.getFilter(basekey + '-filter-IsPlayed'), + IsUnplayed: 'true' === userSettings.getFilter(basekey + '-filter-IsUnplayed'), + IsFavorite: 'true' === userSettings.getFilter(basekey + '-filter-IsFavorite'), + IsResumable: 'true' === userSettings.getFilter(basekey + '-filter-IsResumable'), + Is4K: 'true' === userSettings.getFilter(basekey + '-filter-Is4K'), + IsHD: 'true' === userSettings.getFilter(basekey + '-filter-IsHD'), + IsSD: 'true' === userSettings.getFilter(basekey + '-filter-IsSD'), + Is3D: 'true' === userSettings.getFilter(basekey + '-filter-Is3D'), + VideoTypes: userSettings.getFilter(basekey + '-filter-VideoTypes'), + SeriesStatus: userSettings.getFilter(basekey + '-filter-SeriesStatus'), + HasSubtitles: userSettings.getFilter(basekey + '-filter-HasSubtitles'), + HasTrailer: userSettings.getFilter(basekey + '-filter-HasTrailer'), + HasSpecialFeature: userSettings.getFilter(basekey + '-filter-HasSpecialFeature'), + HasThemeSong: userSettings.getFilter(basekey + '-filter-HasThemeSong'), + HasThemeVideo: userSettings.getFilter(basekey + '-filter-HasThemeVideo'), + GenreIds: userSettings.getFilter(basekey + '-filter-GenreIds') }; }; ItemsView.prototype.getSortValues = function () { var basekey = this.getSettingsKey(); return { - sortBy: userSettings.getFilter(basekey + "-sortby") || this.getDefaultSortBy(), - sortOrder: "Descending" === userSettings.getFilter(basekey + "-sortorder") ? "Descending" : "Ascending" + sortBy: userSettings.getFilter(basekey + '-sortby') || this.getDefaultSortBy(), + sortOrder: 'Descending' === userSettings.getFilter(basekey + '-sortorder') ? 'Descending' : 'Ascending' }; }; @@ -880,17 +880,17 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" return sortNameOption.value; } - return "IsFolder," + sortNameOption.value; + return 'IsFolder,' + sortNameOption.value; }; ItemsView.prototype.getSortMenuOptions = function () { var sortBy = []; var params = this.params; - if ("Programs" === params.type) { + if ('Programs' === params.type) { sortBy.push({ - name: globalize.translate("AirDate"), - value: "StartDate,SortName" + name: globalize.translate('AirDate'), + value: 'StartDate,SortName' }); } @@ -912,10 +912,10 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" sortBy.push(option); } - if ("Programs" !== params.type) { + if ('Programs' !== params.type) { sortBy.push({ - name: globalize.translate("DateAdded"), - value: "DateCreated,SortName" + name: globalize.translate('DateAdded'), + value: 'DateCreated,SortName' }); } @@ -928,14 +928,14 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" if (!params.type) { option = this.getNameSortOption(params); sortBy.push({ - name: globalize.translate("Folders"), - value: "IsFolder," + option.value + name: globalize.translate('Folders'), + value: 'IsFolder,' + option.value }); } sortBy.push({ - name: globalize.translate("ParentalRating"), - value: "OfficialRating,SortName" + name: globalize.translate('ParentalRating'), + value: 'OfficialRating,SortName' }); option = this.getPlayCountSortOption(); @@ -944,67 +944,67 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" } sortBy.push({ - name: globalize.translate("ReleaseDate"), - value: "ProductionYear,PremiereDate,SortName" + name: globalize.translate('ReleaseDate'), + value: 'ProductionYear,PremiereDate,SortName' }); sortBy.push({ - name: globalize.translate("Runtime"), - value: "Runtime,SortName" + name: globalize.translate('Runtime'), + value: 'Runtime,SortName' }); return sortBy; }; ItemsView.prototype.getNameSortOption = function (params) { - if ("Episode" === params.type) { + if ('Episode' === params.type) { return { - name: globalize.translate("Name"), - value: "SeriesName,SortName" + name: globalize.translate('Name'), + value: 'SeriesName,SortName' }; } return { - name: globalize.translate("Name"), - value: "SortName" + name: globalize.translate('Name'), + value: 'SortName' }; }; ItemsView.prototype.getPlayCountSortOption = function () { - if ("Programs" === this.params.type) { + if ('Programs' === this.params.type) { return null; } return { - name: globalize.translate("PlayCount"), - value: "PlayCount,SortName" + name: globalize.translate('PlayCount'), + value: 'PlayCount,SortName' }; }; ItemsView.prototype.getDatePlayedSortOption = function () { - if ("Programs" === this.params.type) { + if ('Programs' === this.params.type) { return null; } return { - name: globalize.translate("DatePlayed"), - value: "DatePlayed,SortName" + name: globalize.translate('DatePlayed'), + value: 'DatePlayed,SortName' }; }; ItemsView.prototype.getCriticRatingSortOption = function () { - if ("Programs" === this.params.type) { + if ('Programs' === this.params.type) { return null; } return { - name: globalize.translate("CriticRating"), - value: "CriticRating,SortName" + name: globalize.translate('CriticRating'), + value: 'CriticRating,SortName' }; }; ItemsView.prototype.getCommunityRatingSortOption = function () { return { - name: globalize.translate("CommunityRating"), - value: "CommunityRating,SortName" + name: globalize.translate('CommunityRating'), + value: 'CommunityRating,SortName' }; }; @@ -1012,25 +1012,24 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" var filters = []; var params = this.params; - if (!("nextup" === params.type)) { - if ("Programs" === params.type) { - filters.push("Genres"); + if (!('nextup' === params.type)) { + if ('Programs' === params.type) { + filters.push('Genres'); } else { - params.type; - filters.push("IsUnplayed"); - filters.push("IsPlayed"); + filters.push('IsUnplayed'); + filters.push('IsPlayed'); if (!params.IsFavorite) { - filters.push("IsFavorite"); + filters.push('IsFavorite'); } - filters.push("IsResumable"); - filters.push("VideoType"); - filters.push("HasSubtitles"); - filters.push("HasTrailer"); - filters.push("HasSpecialFeature"); - filters.push("HasThemeSong"); - filters.push("HasThemeVideo"); + filters.push('IsResumable'); + filters.push('VideoType'); + filters.push('HasSubtitles'); + filters.push('HasTrailer'); + filters.push('HasSpecialFeature'); + filters.push('HasThemeSong'); + filters.push('HasThemeVideo'); } } @@ -1044,22 +1043,22 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" if (filterButtons.length) { for (var i = 0, length = filterButtons.length; i < length; i++) { var btnFilter = filterButtons[i]; - var bubble = btnFilter.querySelector(".filterButtonBubble"); + var bubble = btnFilter.querySelector('.filterButtonBubble'); if (!bubble) { if (!hasFilters) { continue; } - btnFilter.insertAdjacentHTML("afterbegin", '
!
'); - btnFilter.classList.add("btnFilterWithBubble"); - bubble = btnFilter.querySelector(".filterButtonBubble"); + btnFilter.insertAdjacentHTML('afterbegin', '
!
'); + btnFilter.classList.add('btnFilterWithBubble'); + bubble = btnFilter.querySelector('.filterButtonBubble'); } if (hasFilters) { - bubble.classList.remove("hide"); + bubble.classList.remove('hide'); } else { - bubble.classList.add("hide"); + bubble.classList.add('hide'); } } } @@ -1080,13 +1079,13 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" ItemsView.prototype.getVisibleViewSettings = function () { var item = (this.params, this.currentItem); - var fields = ["showTitle"]; + var fields = ['showTitle']; - if (!item || "PhotoAlbum" !== item.Type && "ChannelFolderItem" !== item.Type) { - fields.push("imageType"); + if (!item || 'PhotoAlbum' !== item.Type && 'ChannelFolderItem' !== item.Type) { + fields.push('imageType'); } - fields.push("viewType"); + fields.push('viewType'); return fields; }; @@ -1094,41 +1093,41 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" var basekey = this.getSettingsKey(); var params = this.params; var item = this.currentItem; - var showTitle = userSettings.get(basekey + "-showTitle"); + var showTitle = userSettings.get(basekey + '-showTitle'); - if ("true" === showTitle) { + if ('true' === showTitle) { showTitle = true; - } else if ("false" === showTitle) { + } else if ('false' === showTitle) { showTitle = false; - } else if ("Programs" === params.type || "Recordings" === params.type || "Person" === params.type || "nextup" === params.type || "Audio" === params.type || "MusicAlbum" === params.type || "MusicArtist" === params.type) { + } else if ('Programs' === params.type || 'Recordings' === params.type || 'Person' === params.type || 'nextup' === params.type || 'Audio' === params.type || 'MusicAlbum' === params.type || 'MusicArtist' === params.type) { showTitle = true; - } else if (item && "PhotoAlbum" !== item.Type) { + } else if (item && 'PhotoAlbum' !== item.Type) { showTitle = true; } - var imageType = userSettings.get(basekey + "-imageType"); + var imageType = userSettings.get(basekey + '-imageType'); - if (!imageType && "nextup" === params.type) { - imageType = "thumb"; + if (!imageType && 'nextup' === params.type) { + imageType = 'thumb'; } return { showTitle: showTitle, - showYear: "false" !== userSettings.get(basekey + "-showYear"), - imageType: imageType || "primary", - viewType: userSettings.get(basekey + "-viewType") || "images" + showYear: 'false' !== userSettings.get(basekey + '-showYear'), + imageType: imageType || 'primary', + viewType: userSettings.get(basekey + '-viewType') || 'images' }; }; ItemsView.prototype.getItemTypes = function () { var params = this.params; - if ("nextup" === params.type) { - return ["Episode"]; + if ('nextup' === params.type) { + return ['Episode']; } - if ("Programs" === params.type) { - return ["Program"]; + if ('Programs' === params.type) { + return ['Program']; } return []; @@ -1136,7 +1135,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" ItemsView.prototype.getSettingsKey = function () { var values = []; - values.push("items"); + values.push('items'); var params = this.params; if (params.type) { @@ -1146,54 +1145,54 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager" } if (params.IsAiring) { - values.push("IsAiring"); + values.push('IsAiring'); } if (params.IsMovie) { - values.push("IsMovie"); + values.push('IsMovie'); } if (params.IsKids) { - values.push("IsKids"); + values.push('IsKids'); } if (params.IsSports) { - values.push("IsSports"); + values.push('IsSports'); } if (params.IsNews) { - values.push("IsNews"); + values.push('IsNews'); } if (params.IsSeries) { - values.push("IsSeries"); + values.push('IsSeries'); } if (params.IsFavorite) { - values.push("IsFavorite"); + values.push('IsFavorite'); } if (params.genreId) { - values.push("Genre"); + values.push('Genre'); } if (params.musicGenreId) { - values.push("MusicGenre"); + values.push('MusicGenre'); } if (params.studioId) { - values.push("Studio"); + values.push('Studio'); } if (params.personId) { - values.push("Person"); + values.push('Person'); } if (params.parentId) { - values.push("Folder"); + values.push('Folder'); } - return values.join("-"); + return values.join('-'); }; return ItemsView; diff --git a/src/controllers/livetv/livetvchannels.js b/src/controllers/livetv/livetvchannels.js index 2de7e81843..62906d9d21 100644 --- a/src/controllers/livetv/livetvchannels.js +++ b/src/controllers/livetv/livetvchannels.js @@ -1,5 +1,5 @@ -define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "emby-itemscontainer"], function (cardBuilder, imageLoader, libraryBrowser, loading, events) { - "use strict"; +define(['cardBuilder', 'imageLoader', 'libraryBrowser', 'loading', 'events', 'userSettings', 'emby-itemscontainer'], function (cardBuilder, imageLoader, libraryBrowser, loading, events, userSettings) { + 'use strict'; return function (view, params, tabContent) { function getPageData() { @@ -7,12 +7,15 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em pageData = { query: { StartIndex: 0, - Limit: 100, - Fields: "PrimaryImageAspectRatio" + Fields: 'PrimaryImageAspectRatio' } }; } + if (userSettings.libraryPageSize() > 0) { + pageData.query['Limit'] = userSettings.libraryPageSize(); + } + return pageData; } @@ -23,7 +26,7 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em function getChannelsHtml(channels) { return cardBuilder.getCardsHtml({ items: channels, - shape: "square", + shape: 'square', showTitle: true, lazy: true, cardLayout: true, @@ -39,7 +42,9 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em return; } - query.StartIndex += query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } reloadItems(context); } @@ -48,12 +53,14 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em return; } - query.StartIndex -= query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } reloadItems(context); } var query = getQuery(); - context.querySelector(".paging").innerHTML = libraryBrowser.getQueryPagingHtml({ + context.querySelector('.paging').innerHTML = libraryBrowser.getQueryPagingHtml({ startIndex: query.StartIndex, limit: query.Limit, totalRecordCount: result.TotalRecordCount, @@ -62,30 +69,30 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em filterButton: false }); var html = getChannelsHtml(result.Items); - var elem = context.querySelector("#items"); + var elem = context.querySelector('#items'); elem.innerHTML = html; imageLoader.lazyChildren(elem); var i; var length; var elems; - for (elems = context.querySelectorAll(".btnNextPage"), i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onNextPageClick); + for (elems = context.querySelectorAll('.btnNextPage'), i = 0, length = elems.length; i < length; i++) { + elems[i].addEventListener('click', onNextPageClick); } - for (elems = context.querySelectorAll(".btnPreviousPage"), i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onPreviousPageClick); + for (elems = context.querySelectorAll('.btnPreviousPage'), i = 0, length = elems.length; i < length; i++) { + elems[i].addEventListener('click', onPreviousPageClick); } } function showFilterMenu(context) { - require(["components/filterdialog/filterdialog"], function (filterDialogFactory) { + require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { var filterDialog = new filterDialogFactory({ query: getQuery(), - mode: "livetvchannels", + mode: 'livetvchannels', serverId: ApiClient.serverId() }); - events.on(filterDialog, "filterchange", function () { + events.on(filterDialog, 'filterchange', function () { reloadItems(context); }); filterDialog.show(); @@ -103,7 +110,7 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em loading.hide(); isLoading = false; - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(view); }); }); @@ -112,7 +119,7 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em var pageData; var self = this; var isLoading = false; - tabContent.querySelector(".btnFilter").addEventListener("click", function () { + tabContent.querySelector('.btnFilter').addEventListener('click', function () { showFilterMenu(tabContent); }); diff --git a/src/controllers/livetv/livetvguide.js b/src/controllers/livetv/livetvguide.js index f7c2f1baaa..ec7a7a3f81 100644 --- a/src/controllers/livetv/livetvguide.js +++ b/src/controllers/livetv/livetvguide.js @@ -1,5 +1,5 @@ -define(["tvguide"], function (tvguide) { - "use strict"; +define(['tvguide'], function (tvguide) { + 'use strict'; return function (view, params, tabContent) { var guideInstance; diff --git a/src/controllers/livetv/livetvrecordings.js b/src/controllers/livetv/livetvrecordings.js index f8e49f2590..d5cfe66672 100644 --- a/src/controllers/livetv/livetvrecordings.js +++ b/src/controllers/livetv/livetvrecordings.js @@ -1,5 +1,5 @@ -define(["layoutManager", "loading", "cardBuilder", "apphost", "imageLoader", "scripts/livetvcomponents", "listViewStyle", "emby-itemscontainer"], function (layoutManager, loading, cardBuilder, appHost, imageLoader) { - "use strict"; +define(['layoutManager', 'loading', 'cardBuilder', 'apphost', 'imageLoader', 'scripts/livetvcomponents', 'listViewStyle', 'emby-itemscontainer'], function (layoutManager, loading, cardBuilder, appHost, imageLoader) { + 'use strict'; function renderRecordings(elem, recordings, cardOptions, scrollX) { if (!elem) { @@ -7,35 +7,35 @@ define(["layoutManager", "loading", "cardBuilder", "apphost", "imageLoader", "sc } if (recordings.length) { - elem.classList.remove("hide"); + elem.classList.remove('hide'); } else { - elem.classList.add("hide"); + elem.classList.add('hide'); } - var recordingItems = elem.querySelector(".recordingItems"); + var recordingItems = elem.querySelector('.recordingItems'); if (scrollX) { - recordingItems.classList.add("scrollX"); - recordingItems.classList.add("hiddenScrollX"); - recordingItems.classList.remove("vertical-wrap"); + recordingItems.classList.add('scrollX'); + recordingItems.classList.add('hiddenScrollX'); + recordingItems.classList.remove('vertical-wrap'); } else { - recordingItems.classList.remove("scrollX"); - recordingItems.classList.remove("hiddenScrollX"); - recordingItems.classList.add("vertical-wrap"); + recordingItems.classList.remove('scrollX'); + recordingItems.classList.remove('hiddenScrollX'); + recordingItems.classList.add('vertical-wrap'); } - appHost.supports("imageanalysis"); + appHost.supports('imageanalysis'); recordingItems.innerHTML = cardBuilder.getCardsHtml(Object.assign({ items: recordings, - shape: scrollX ? "autooverflow" : "auto", - defaultShape: scrollX ? "overflowBackdrop" : "backdrop", + shape: scrollX ? 'autooverflow' : 'auto', + defaultShape: scrollX ? 'overflowBackdrop' : 'backdrop', showTitle: true, showParentTitle: true, coverImage: true, cardLayout: false, centerText: true, allowBottomPadding: !scrollX, - preferThumb: "auto", + preferThumb: 'auto', overlayText: false }, cardOptions || {})); imageLoader.lazyChildren(recordingItems); @@ -43,7 +43,7 @@ define(["layoutManager", "loading", "cardBuilder", "apphost", "imageLoader", "sc function renderLatestRecordings(context, promise) { promise.then(function (result) { - renderRecordings(context.querySelector("#latestRecordings"), result.Items, { + renderRecordings(context.querySelector('#latestRecordings'), result.Items, { showYear: true, lines: 2 }, false); @@ -53,7 +53,7 @@ define(["layoutManager", "loading", "cardBuilder", "apphost", "imageLoader", "sc function renderRecordingFolders(context, promise) { promise.then(function (result) { - renderRecordings(context.querySelector("#recordingFolders"), result.Items, { + renderRecordings(context.querySelector('#recordingFolders'), result.Items, { showYear: false, showParentTitle: false }, false); @@ -61,12 +61,12 @@ define(["layoutManager", "loading", "cardBuilder", "apphost", "imageLoader", "sc } function onMoreClick(e) { - var type = this.getAttribute("data-type"); + var type = this.getAttribute('data-type'); var serverId = ApiClient.serverId(); switch (type) { - case "latest": - Dashboard.navigate("list.html?type=Recordings&serverId=" + serverId); + case 'latest': + Dashboard.navigate('list.html?type=Recordings&serverId=' + serverId); } } @@ -79,10 +79,10 @@ define(["layoutManager", "loading", "cardBuilder", "apphost", "imageLoader", "sc var latestPromise; var self = this; var lastFullRender = 0; - var moreButtons = tabContent.querySelectorAll(".more"); + var moreButtons = tabContent.querySelectorAll('.more'); for (var i = 0, length = moreButtons.length; i < length; i++) { - moreButtons[i].addEventListener("click", onMoreClick); + moreButtons[i].addEventListener('click', onMoreClick); } self.preRender = function () { @@ -90,9 +90,9 @@ define(["layoutManager", "loading", "cardBuilder", "apphost", "imageLoader", "sc latestPromise = ApiClient.getLiveTvRecordings({ UserId: Dashboard.getCurrentUserId(), Limit: 12, - Fields: "CanDelete,PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'CanDelete,PrimaryImageAspectRatio,BasicSyncInfo', EnableTotalRecordCount: false, - EnableImageTypes: "Primary,Thumb,Backdrop" + EnableImageTypes: 'Primary,Thumb,Backdrop' }); foldersPromise = ApiClient.getRecordingFolders(Dashboard.getCurrentUserId()); } diff --git a/src/controllers/livetv/livetvschedule.js b/src/controllers/livetv/livetvschedule.js index 3bb85598cb..a6f509c6f1 100644 --- a/src/controllers/livetv/livetvschedule.js +++ b/src/controllers/livetv/livetvschedule.js @@ -1,5 +1,5 @@ -define(["layoutManager", "cardBuilder", "apphost", "imageLoader", "loading", "scripts/livetvcomponents", "emby-button", "emby-itemscontainer"], function (layoutManager, cardBuilder, appHost, imageLoader, loading) { - "use strict"; +define(['layoutManager', 'cardBuilder', 'apphost', 'imageLoader', 'loading', 'scripts/livetvcomponents', 'emby-button', 'emby-itemscontainer'], function (layoutManager, cardBuilder, appHost, imageLoader, loading) { + 'use strict'; function enableScrollX() { return !layoutManager.desktop; @@ -7,54 +7,54 @@ define(["layoutManager", "cardBuilder", "apphost", "imageLoader", "loading", "sc function renderRecordings(elem, recordings, cardOptions) { if (recordings.length) { - elem.classList.remove("hide"); + elem.classList.remove('hide'); } else { - elem.classList.add("hide"); + elem.classList.add('hide'); } - var recordingItems = elem.querySelector(".recordingItems"); + var recordingItems = elem.querySelector('.recordingItems'); if (enableScrollX()) { - recordingItems.classList.add("scrollX"); + recordingItems.classList.add('scrollX'); if (layoutManager.tv) { - recordingItems.classList.add("smoothScrollX"); + recordingItems.classList.add('smoothScrollX'); } - recordingItems.classList.add("hiddenScrollX"); - recordingItems.classList.remove("vertical-wrap"); + recordingItems.classList.add('hiddenScrollX'); + recordingItems.classList.remove('vertical-wrap'); } else { - recordingItems.classList.remove("scrollX"); - recordingItems.classList.remove("smoothScrollX"); - recordingItems.classList.remove("hiddenScrollX"); - recordingItems.classList.add("vertical-wrap"); + recordingItems.classList.remove('scrollX'); + recordingItems.classList.remove('smoothScrollX'); + recordingItems.classList.remove('hiddenScrollX'); + recordingItems.classList.add('vertical-wrap'); } - var supportsImageAnalysis = appHost.supports("imageanalysis"); + var supportsImageAnalysis = appHost.supports('imageanalysis'); var cardLayout = appHost.preferVisualCards || supportsImageAnalysis; cardLayout = false; recordingItems.innerHTML = cardBuilder.getCardsHtml(Object.assign({ items: recordings, - shape: enableScrollX() ? "autooverflow" : "auto", + shape: enableScrollX() ? 'autooverflow' : 'auto', showTitle: true, showParentTitle: true, coverImage: true, cardLayout: cardLayout, centerText: !cardLayout, allowBottomPadding: !enableScrollX(), - preferThumb: "auto" + preferThumb: 'auto' }, cardOptions || {})); imageLoader.lazyChildren(recordingItems); } function getBackdropShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop"; + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function renderActiveRecordings(context, promise) { promise.then(function (result) { - renderRecordings(context.querySelector("#activeRecordings"), result.Items, { - shape: enableScrollX() ? "autooverflow" : "auto", + renderRecordings(context.querySelector('#activeRecordings'), result.Items, { + shape: enableScrollX() ? 'autooverflow' : 'auto', defaultShape: getBackdropShape(), showParentTitle: false, showParentTitleOrTitle: true, @@ -74,19 +74,19 @@ define(["layoutManager", "cardBuilder", "apphost", "imageLoader", "loading", "sc var elem = context; if (html) { - elem.classList.remove("hide"); + elem.classList.remove('hide'); } else { - elem.classList.add("hide"); + elem.classList.add('hide'); } - elem.querySelector(".recordingItems").innerHTML = html; + elem.querySelector('.recordingItems').innerHTML = html; imageLoader.lazyChildren(elem); }); } function renderUpcomingRecordings(context, promise) { promise.then(function (result) { - renderTimers(context.querySelector("#upcomingRecordings"), result.Items); + renderTimers(context.querySelector('#upcomingRecordings'), result.Items); loading.hide(); }); } @@ -95,7 +95,7 @@ define(["layoutManager", "cardBuilder", "apphost", "imageLoader", "loading", "sc var activeRecordingsPromise; var upcomingRecordingsPromise; var self = this; - tabContent.querySelector("#upcomingRecordings .recordingItems").addEventListener("timercancelled", function () { + tabContent.querySelector('#upcomingRecordings .recordingItems').addEventListener('timercancelled', function () { self.preRender(); self.renderTab(); }); @@ -104,9 +104,9 @@ define(["layoutManager", "cardBuilder", "apphost", "imageLoader", "loading", "sc activeRecordingsPromise = ApiClient.getLiveTvRecordings({ UserId: Dashboard.getCurrentUserId(), IsInProgress: true, - Fields: "CanDelete,PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'CanDelete,PrimaryImageAspectRatio,BasicSyncInfo', EnableTotalRecordCount: false, - EnableImageTypes: "Primary,Thumb,Backdrop" + EnableImageTypes: 'Primary,Thumb,Backdrop' }); upcomingRecordingsPromise = ApiClient.getLiveTvTimers({ IsActive: false, diff --git a/src/controllers/livetv/livetvseriestimers.js b/src/controllers/livetv/livetvseriestimers.js index 9c95cfa91f..27daca1983 100644 --- a/src/controllers/livetv/livetvseriestimers.js +++ b/src/controllers/livetv/livetvseriestimers.js @@ -1,16 +1,16 @@ -define(["datetime", "cardBuilder", "imageLoader", "apphost", "loading", "paper-icon-button-light", "emby-button"], function (datetime, cardBuilder, imageLoader, appHost, loading) { - "use strict"; +define(['datetime', 'cardBuilder', 'imageLoader', 'apphost', 'loading', 'paper-icon-button-light', 'emby-button'], function (datetime, cardBuilder, imageLoader, appHost, loading) { + 'use strict'; function renderTimers(context, timers) { - var html = ""; - appHost.supports("imageanalysis"); + var html = ''; + appHost.supports('imageanalysis'); html += cardBuilder.getCardsHtml({ items: timers, - shape: "auto", - defaultShape: "portrait", + shape: 'auto', + defaultShape: 'portrait', showTitle: true, cardLayout: false, - preferThumb: "auto", + preferThumb: 'auto', coverImage: true, overlayText: false, showSeriesTimerTime: true, @@ -19,7 +19,7 @@ define(["datetime", "cardBuilder", "imageLoader", "apphost", "loading", "paper-i overlayMoreButton: true, lines: 3 }); - var elem = context.querySelector("#items"); + var elem = context.querySelector('#items'); elem.innerHTML = html; imageLoader.lazyChildren(elem); loading.hide(); @@ -33,8 +33,8 @@ define(["datetime", "cardBuilder", "imageLoader", "apphost", "loading", "paper-i } var query = { - SortBy: "SortName", - SortOrder: "Ascending" + SortBy: 'SortName', + SortOrder: 'Ascending' }; return function (view, params, tabContent) { var timersPromise; diff --git a/src/controllers/livetv/livetvsuggested.js b/src/controllers/livetv/livetvsuggested.js index 8f65df7e6f..036eee9fc6 100644 --- a/src/controllers/livetv/livetvsuggested.js +++ b/src/controllers/livetv/livetvsuggested.js @@ -1,5 +1,5 @@ -define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", "libraryBrowser", "mainTabsManager", "cardBuilder", "apphost", "imageLoader", "scrollStyles", "emby-itemscontainer", "emby-tabs", "emby-button"], function (layoutManager, userSettings, inputManager, loading, globalize, libraryBrowser, mainTabsManager, cardBuilder, appHost, imageLoader) { - "use strict"; +define(['layoutManager', 'userSettings', 'inputManager', 'loading', 'globalize', 'libraryBrowser', 'mainTabsManager', 'cardBuilder', 'apphost', 'imageLoader', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (layoutManager, userSettings, inputManager, loading, globalize, libraryBrowser, mainTabsManager, cardBuilder, appHost, imageLoader) { + 'use strict'; function enableScrollX() { return !layoutManager.desktop; @@ -7,16 +7,16 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", function getBackdropShape() { if (enableScrollX()) { - return "overflowBackdrop"; + return 'overflowBackdrop'; } - return "backdrop"; + return 'backdrop'; } function getPortraitShape() { if (enableScrollX()) { - return "overflowPortrait"; + return 'overflowPortrait'; } - return "portrait"; + return 'portrait'; } function getLimit() { @@ -40,17 +40,17 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", IsAiring: true, limit: limit, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Thumb,Backdrop", + EnableImageTypes: 'Primary,Thumb,Backdrop', EnableTotalRecordCount: false, - Fields: "ChannelInfo,PrimaryImageAspectRatio" + Fields: 'ChannelInfo,PrimaryImageAspectRatio' }).then(function (result) { - renderItems(page, result.Items, "activeProgramItems", "play", { + renderItems(page, result.Items, 'activeProgramItems', 'play', { showAirDateTime: false, showAirEndTime: true }); loading.hide(); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); }); @@ -69,10 +69,10 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", IsNews: false, IsSeries: true, EnableTotalRecordCount: false, - Fields: "ChannelInfo,PrimaryImageAspectRatio", - EnableImageTypes: "Primary,Thumb" + Fields: 'ChannelInfo,PrimaryImageAspectRatio', + EnableImageTypes: 'Primary,Thumb' }).then(function (result) { - renderItems(page, result.Items, "upcomingEpisodeItems"); + renderItems(page, result.Items, 'upcomingEpisodeItems'); }); ApiClient.getLiveTvPrograms({ userId: Dashboard.getCurrentUserId(), @@ -80,10 +80,10 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", limit: getLimit(), IsMovie: true, EnableTotalRecordCount: false, - Fields: "ChannelInfo", - EnableImageTypes: "Primary,Thumb" + Fields: 'ChannelInfo', + EnableImageTypes: 'Primary,Thumb' }).then(function (result) { - renderItems(page, result.Items, "upcomingTvMovieItems", null, { + renderItems(page, result.Items, 'upcomingTvMovieItems', null, { shape: getPortraitShape(), preferThumb: null, showParentTitle: false @@ -95,10 +95,10 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", limit: getLimit(), IsSports: true, EnableTotalRecordCount: false, - Fields: "ChannelInfo,PrimaryImageAspectRatio", - EnableImageTypes: "Primary,Thumb" + Fields: 'ChannelInfo,PrimaryImageAspectRatio', + EnableImageTypes: 'Primary,Thumb' }).then(function (result) { - renderItems(page, result.Items, "upcomingSportsItems"); + renderItems(page, result.Items, 'upcomingSportsItems'); }); ApiClient.getLiveTvPrograms({ userId: Dashboard.getCurrentUserId(), @@ -106,10 +106,10 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", limit: getLimit(), IsKids: true, EnableTotalRecordCount: false, - Fields: "ChannelInfo,PrimaryImageAspectRatio", - EnableImageTypes: "Primary,Thumb" + Fields: 'ChannelInfo,PrimaryImageAspectRatio', + EnableImageTypes: 'Primary,Thumb' }).then(function (result) { - renderItems(page, result.Items, "upcomingKidsItems"); + renderItems(page, result.Items, 'upcomingKidsItems'); }); ApiClient.getLiveTvPrograms({ userId: Dashboard.getCurrentUserId(), @@ -117,10 +117,10 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", limit: getLimit(), IsNews: true, EnableTotalRecordCount: false, - Fields: "ChannelInfo,PrimaryImageAspectRatio", - EnableImageTypes: "Primary,Thumb" + Fields: 'ChannelInfo,PrimaryImageAspectRatio', + EnableImageTypes: 'Primary,Thumb' }).then(function (result) { - renderItems(page, result.Items, "upcomingNewsItems", null, { + renderItems(page, result.Items, 'upcomingNewsItems', null, { showParentTitleOrTitle: true, showTitle: false, showParentTitle: false @@ -132,9 +132,9 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", function renderItems(page, items, sectionClass, overlayButton, cardOptions) { var html = cardBuilder.getCardsHtml(Object.assign({ items: items, - preferThumb: "auto", + preferThumb: 'auto', inheritThumb: false, - shape: enableScrollX() ? "autooverflow" : "auto", + shape: enableScrollX() ? 'autooverflow' : 'auto', defaultShape: getBackdropShape(), showParentTitle: true, showTitle: true, @@ -142,57 +142,57 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", coverImage: true, overlayText: false, lazy: true, - overlayPlayButton: "play" === overlayButton, - overlayMoreButton: "more" === overlayButton, - overlayInfoButton: "info" === overlayButton, + overlayPlayButton: 'play' === overlayButton, + overlayMoreButton: 'more' === overlayButton, + overlayInfoButton: 'info' === overlayButton, allowBottomPadding: !enableScrollX(), showAirTime: true, showAirDateTime: true }, cardOptions || {})); - var elem = page.querySelector("." + sectionClass); + var elem = page.querySelector('.' + sectionClass); elem.innerHTML = html; imageLoader.lazyChildren(elem); } function getTabs() { return [{ - name: globalize.translate("Programs") + name: globalize.translate('Programs') }, { - name: globalize.translate("TabGuide") + name: globalize.translate('TabGuide') }, { - name: globalize.translate("TabChannels") + name: globalize.translate('TabChannels') }, { - name: globalize.translate("TabRecordings") + name: globalize.translate('TabRecordings') }, { - name: globalize.translate("HeaderSchedule") + name: globalize.translate('HeaderSchedule') }, { - name: globalize.translate("TabSeries") + name: globalize.translate('TabSeries') }, { - name: globalize.translate("ButtonSearch"), - cssClass: "searchTabButton" + name: globalize.translate('ButtonSearch'), + cssClass: 'searchTabButton' }]; } function setScrollClasses(elem, scrollX) { if (scrollX) { - elem.classList.add("hiddenScrollX"); + elem.classList.add('hiddenScrollX'); if (layoutManager.tv) { - elem.classList.add("smoothScrollX"); + elem.classList.add('smoothScrollX'); } - elem.classList.add("scrollX"); - elem.classList.remove("vertical-wrap"); + elem.classList.add('scrollX'); + elem.classList.remove('vertical-wrap'); } else { - elem.classList.remove("hiddenScrollX"); - elem.classList.remove("smoothScrollX"); - elem.classList.remove("scrollX"); - elem.classList.add("vertical-wrap"); + elem.classList.remove('hiddenScrollX'); + elem.classList.remove('smoothScrollX'); + elem.classList.remove('scrollX'); + elem.classList.add('vertical-wrap'); } } function getDefaultTabIndex(folderId) { - if (userSettings.get("landing-" + folderId) === "guide") { + if (userSettings.get('landing-' + folderId) === 'guide') { return 1; } @@ -219,7 +219,7 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", } function getTabContainers() { - return view.querySelectorAll(".pageTabContent"); + return view.querySelectorAll('.pageTabContent'); } function initTabs() { @@ -235,27 +235,27 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", break; case 1: - depends.push("controllers/livetv/livetvguide"); + depends.push('controllers/livetv/livetvguide'); break; case 2: - depends.push("controllers/livetv/livetvchannels"); + depends.push('controllers/livetv/livetvchannels'); break; case 3: - depends.push("controllers/livetv/livetvrecordings"); + depends.push('controllers/livetv/livetvrecordings'); break; case 4: - depends.push("controllers/livetv/livetvschedule"); + depends.push('controllers/livetv/livetvschedule'); break; case 5: - depends.push("controllers/livetv/livetvseriestimers"); + depends.push('controllers/livetv/livetvseriestimers'); break; case 6: - depends.push("scripts/searchtab"); + depends.push('scripts/searchtab'); } require(depends, function (controllerFactory) { @@ -275,7 +275,7 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", controller = self; } else if (6 === index) { controller = new controllerFactory(view, tabContent, { - collectionType: "livetv" + collectionType: 'livetv' }); } else { controller = new controllerFactory(view, params, tabContent); @@ -320,28 +320,28 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", } function onInputCommand(evt) { - if (evt.detail.command === "search") { + if (evt.detail.command === 'search') { evt.preventDefault(); - Dashboard.navigate("search.html?collectionType=livetv"); + Dashboard.navigate('search.html?collectionType=livetv'); } } var isViewRestored; var self = this; - var currentTabIndex = parseInt(params.tab || getDefaultTabIndex("livetv")); + var currentTabIndex = parseInt(params.tab || getDefaultTabIndex('livetv')); var initialTabIndex = currentTabIndex; var lastFullRender = 0; - [].forEach.call(view.querySelectorAll(".sectionTitleTextButton-programs"), function (link) { + [].forEach.call(view.querySelectorAll('.sectionTitleTextButton-programs'), function (link) { var href = link.href; if (href) { - link.href = href + "&serverId=" + ApiClient.serverId(); + link.href = href + '&serverId=' + ApiClient.serverId(); } }); self.initTab = function () { var tabContent = view.querySelector(".pageTabContent[data-index='0']"); - var containers = tabContent.querySelectorAll(".itemsContainer"); + var containers = tabContent.querySelectorAll('.itemsContainer'); for (var i = 0, length = containers.length; i < length; i++) { setScrollClasses(containers[i], enableScrollX()); @@ -362,11 +362,11 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", var currentTabController; var tabControllers = []; var renderedTabs = []; - view.addEventListener("viewbeforeshow", function (evt) { + view.addEventListener('viewbeforeshow', function (evt) { isViewRestored = evt.detail.isRestored; initTabs(); }); - view.addEventListener("viewshow", function (evt) { + view.addEventListener('viewshow', function (evt) { isViewRestored = evt.detail.isRestored; if (!isViewRestored) { @@ -375,14 +375,14 @@ define(["layoutManager", "userSettings", "inputManager", "loading", "globalize", inputManager.on(window, onInputCommand); }); - view.addEventListener("viewbeforehide", function (e) { + view.addEventListener('viewbeforehide', function (e) { if (currentTabController && currentTabController.onHide) { currentTabController.onHide(); } inputManager.off(window, onInputCommand); }); - view.addEventListener("viewdestroy", function (evt) { + view.addEventListener('viewdestroy', function (evt) { tabControllers.forEach(function (tabController) { if (tabController.destroy) { tabController.destroy(); diff --git a/src/controllers/livetvguideprovider.js b/src/controllers/livetvguideprovider.js index a58917f22d..e83036992b 100644 --- a/src/controllers/livetvguideprovider.js +++ b/src/controllers/livetvguideprovider.js @@ -1,30 +1,30 @@ -define(["events", "loading"], function (events, loading) { - "use strict"; +define(['events', 'loading', 'globalize'], function (events, loading, globalize) { + 'use strict'; function onListingsSubmitted() { - Dashboard.navigate("livetvstatus.html"); + Dashboard.navigate('livetvstatus.html'); } function init(page, type, providerId) { - var url = "components/tvproviders/" + type + ".js"; + var url = 'components/tvproviders/' + type + '.js'; require([url], function (factory) { var instance = new factory(page, providerId, {}); - events.on(instance, "submitted", onListingsSubmitted); + events.on(instance, 'submitted', onListingsSubmitted); instance.init(); }); } function loadTemplate(page, type, providerId) { - require(["text!./components/tvproviders/" + type + ".template.html"], function (html) { - page.querySelector(".providerTemplate").innerHTML = Globalize.translateDocument(html); + require(['text!./components/tvproviders/' + type + '.template.html'], function (html) { + page.querySelector('.providerTemplate').innerHTML = globalize.translateDocument(html); init(page, type, providerId); }); } - pageIdOn("pageshow", "liveTvGuideProviderPage", function () { + pageIdOn('pageshow', 'liveTvGuideProviderPage', function () { loading.show(); - var providerId = getParameterByName("id"); - loadTemplate(this, getParameterByName("type"), providerId); + var providerId = getParameterByName('id'); + loadTemplate(this, getParameterByName('type'), providerId); }); }); diff --git a/src/controllers/livetvsettings.js b/src/controllers/livetvsettings.js index 2b11071c75..4c5fdc60de 100644 --- a/src/controllers/livetvsettings.js +++ b/src/controllers/livetvsettings.js @@ -1,38 +1,38 @@ -define(["jQuery", "loading", "fnchecked", "emby-button"], function ($, loading) { - "use strict"; +define(['jQuery', 'loading', 'globalize', 'emby-button'], function ($, loading, globalize) { + 'use strict'; function loadPage(page, config) { - $(".liveTvSettingsForm", page).show(); - $(".noLiveTvServices", page).hide(); - $("#selectGuideDays", page).val(config.GuideDays || ""); - $("#txtPrePaddingMinutes", page).val(config.PrePaddingSeconds / 60); - $("#txtPostPaddingMinutes", page).val(config.PostPaddingSeconds / 60); - page.querySelector("#txtRecordingPath").value = config.RecordingPath || ""; - page.querySelector("#txtMovieRecordingPath").value = config.MovieRecordingPath || ""; - page.querySelector("#txtSeriesRecordingPath").value = config.SeriesRecordingPath || ""; - page.querySelector("#txtPostProcessor").value = config.RecordingPostProcessor || ""; - page.querySelector("#txtPostProcessorArguments").value = config.RecordingPostProcessorArguments || ""; + $('.liveTvSettingsForm', page).show(); + $('.noLiveTvServices', page).hide(); + $('#selectGuideDays', page).val(config.GuideDays || ''); + $('#txtPrePaddingMinutes', page).val(config.PrePaddingSeconds / 60); + $('#txtPostPaddingMinutes', page).val(config.PostPaddingSeconds / 60); + page.querySelector('#txtRecordingPath').value = config.RecordingPath || ''; + page.querySelector('#txtMovieRecordingPath').value = config.MovieRecordingPath || ''; + page.querySelector('#txtSeriesRecordingPath').value = config.SeriesRecordingPath || ''; + page.querySelector('#txtPostProcessor').value = config.RecordingPostProcessor || ''; + page.querySelector('#txtPostProcessorArguments').value = config.RecordingPostProcessorArguments || ''; loading.hide(); } function onSubmit() { loading.show(); var form = this; - ApiClient.getNamedConfiguration("livetv").then(function (config) { - config.GuideDays = $("#selectGuideDays", form).val() || null; - var recordingPath = form.querySelector("#txtRecordingPath").value || null; - var movieRecordingPath = form.querySelector("#txtMovieRecordingPath").value || null; - var seriesRecordingPath = form.querySelector("#txtSeriesRecordingPath").value || null; + ApiClient.getNamedConfiguration('livetv').then(function (config) { + config.GuideDays = $('#selectGuideDays', form).val() || null; + var recordingPath = form.querySelector('#txtRecordingPath').value || null; + var movieRecordingPath = form.querySelector('#txtMovieRecordingPath').value || null; + var seriesRecordingPath = form.querySelector('#txtSeriesRecordingPath').value || null; var recordingPathChanged = recordingPath != config.RecordingPath || movieRecordingPath != config.MovieRecordingPath || seriesRecordingPath != config.SeriesRecordingPath; config.RecordingPath = recordingPath; config.MovieRecordingPath = movieRecordingPath; config.SeriesRecordingPath = seriesRecordingPath; - config.RecordingEncodingFormat = "mkv"; - config.PrePaddingSeconds = 60 * $("#txtPrePaddingMinutes", form).val(); - config.PostPaddingSeconds = 60 * $("#txtPostPaddingMinutes", form).val(); - config.RecordingPostProcessor = $("#txtPostProcessor", form).val(); - config.RecordingPostProcessorArguments = $("#txtPostProcessorArguments", form).val(); - ApiClient.updateNamedConfiguration("livetv", config).then(function () { + config.RecordingEncodingFormat = 'mkv'; + config.PrePaddingSeconds = 60 * $('#txtPrePaddingMinutes', form).val(); + config.PostPaddingSeconds = 60 * $('#txtPostPaddingMinutes', form).val(); + config.RecordingPostProcessor = $('#txtPostProcessor', form).val(); + config.RecordingPostProcessorArguments = $('#txtPostProcessorArguments', form).val(); + ApiClient.updateNamedConfiguration('livetv', config).then(function () { Dashboard.processServerConfigurationUpdateResult(); showSaveMessage(recordingPathChanged); }); @@ -41,29 +41,29 @@ define(["jQuery", "loading", "fnchecked", "emby-button"], function ($, loading) } function showSaveMessage(recordingPathChanged) { - var msg = ""; + var msg = ''; if (recordingPathChanged) { - msg += Globalize.translate("RecordingPathChangeMessage"); + msg += globalize.translate('RecordingPathChangeMessage'); } if (msg) { - require(["alert"], function (alert) { + require(['alert'], function (alert) { alert(msg); }); } } - $(document).on("pageinit", "#liveTvSettingsPage", function () { + $(document).on('pageinit', '#liveTvSettingsPage', function () { var page = this; - $(".liveTvSettingsForm").off("submit", onSubmit).on("submit", onSubmit); - $("#btnSelectRecordingPath", page).on("click.selectDirectory", function () { - require(["directorybrowser"], function (directoryBrowser) { + $('.liveTvSettingsForm').off('submit', onSubmit).on('submit', onSubmit); + $('#btnSelectRecordingPath', page).on('click.selectDirectory', function () { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ callback: function (path) { if (path) { - $("#txtRecordingPath", page).val(path); + $('#txtRecordingPath', page).val(path); } picker.close(); @@ -72,13 +72,13 @@ define(["jQuery", "loading", "fnchecked", "emby-button"], function ($, loading) }); }); }); - $("#btnSelectMovieRecordingPath", page).on("click.selectDirectory", function () { - require(["directorybrowser"], function (directoryBrowser) { + $('#btnSelectMovieRecordingPath', page).on('click.selectDirectory', function () { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ callback: function (path) { if (path) { - $("#txtMovieRecordingPath", page).val(path); + $('#txtMovieRecordingPath', page).val(path); } picker.close(); @@ -87,13 +87,13 @@ define(["jQuery", "loading", "fnchecked", "emby-button"], function ($, loading) }); }); }); - $("#btnSelectSeriesRecordingPath", page).on("click.selectDirectory", function () { - require(["directorybrowser"], function (directoryBrowser) { + $('#btnSelectSeriesRecordingPath', page).on('click.selectDirectory', function () { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ callback: function (path) { if (path) { - $("#txtSeriesRecordingPath", page).val(path); + $('#txtSeriesRecordingPath', page).val(path); } picker.close(); @@ -102,14 +102,14 @@ define(["jQuery", "loading", "fnchecked", "emby-button"], function ($, loading) }); }); }); - $("#btnSelectPostProcessorPath", page).on("click.selectDirectory", function () { - require(["directorybrowser"], function (directoryBrowser) { + $('#btnSelectPostProcessorPath', page).on('click.selectDirectory', function () { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ includeFiles: true, callback: function (path) { if (path) { - $("#txtPostProcessor", page).val(path); + $('#txtPostProcessor', page).val(path); } picker.close(); @@ -117,10 +117,10 @@ define(["jQuery", "loading", "fnchecked", "emby-button"], function ($, loading) }); }); }); - }).on("pageshow", "#liveTvSettingsPage", function () { + }).on('pageshow', '#liveTvSettingsPage', function () { loading.show(); var page = this; - ApiClient.getNamedConfiguration("livetv").then(function (config) { + ApiClient.getNamedConfiguration('livetv').then(function (config) { loadPage(page, config); }); }); diff --git a/src/controllers/livetvstatus.js b/src/controllers/livetvstatus.js index aee5876a45..82d0b697db 100644 --- a/src/controllers/livetvstatus.js +++ b/src/controllers/livetvstatus.js @@ -1,23 +1,23 @@ -define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layoutManager", "loading", "browser", "listViewStyle", "flexStyles", "emby-itemscontainer", "cardStyle", "material-icons", "emby-button"], function ($, globalize, taskButton, dom, libraryMenu, layoutManager, loading, browser) { - "use strict"; +define(['jQuery', 'globalize', 'scripts/taskbutton', 'dom', 'libraryMenu', 'layoutManager', 'loading', 'browser', 'listViewStyle', 'flexStyles', 'emby-itemscontainer', 'cardStyle', 'material-icons', 'emby-button'], function ($, globalize, taskButton, dom, libraryMenu, layoutManager, loading, browser) { + 'use strict'; var enableFocusTransform = !browser.slow && !browser.edge; function getDeviceHtml(device) { var padderClass; - var html = ""; - var cssClass = "card scalableCard"; - var cardBoxCssClass = "cardBox visualCardBox"; - cssClass += " backdropCard backdropCard-scalable"; - padderClass = "cardPadder-backdrop"; + var html = ''; + var cssClass = 'card scalableCard'; + var cardBoxCssClass = 'cardBox visualCardBox'; + cssClass += ' backdropCard backdropCard-scalable'; + padderClass = 'cardPadder-backdrop'; // TODO move card creation code to Card component if (layoutManager.tv) { - cssClass += " show-focus"; + cssClass += ' show-focus'; if (enableFocusTransform) { - cssClass += " show-animation"; + cssClass += ' show-animation'; } } @@ -26,34 +26,34 @@ define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layo html += '
'; html += '
'; html += '
'; - html += '
dvr
'; - html += "
"; - html += "
"; + html += '
'; + html += ''; + html += ''; html += '
'; - html += ''; - html += '
' + (device.FriendlyName || getTunerName(device.Type)) + "
"; + html += ''; + html += '
' + (device.FriendlyName || getTunerName(device.Type)) + '
'; html += '
'; - html += device.Url || " "; - html += "
"; - html += "
"; - html += ""; - return html += ""; + html += device.Url || ' '; + html += ''; + html += ''; + html += ''; + return html += ''; } function renderDevices(page, devices) { - var html = devices.map(getDeviceHtml).join(""); - page.querySelector(".devicesList").innerHTML = html; + var html = devices.map(getDeviceHtml).join(''); + page.querySelector('.devicesList').innerHTML = html; } function deleteDevice(page, id) { - var message = globalize.translate("MessageConfirmDeleteTunerDevice"); + var message = globalize.translate('MessageConfirmDeleteTunerDevice'); - require(["confirm"], function (confirm) { - confirm(message, globalize.translate("HeaderDeleteDevice")).then(function () { + require(['confirm'], function (confirm) { + confirm(message, globalize.translate('HeaderDeleteDevice')).then(function () { loading.show(); ApiClient.ajax({ - type: "DELETE", - url: ApiClient.getUrl("LiveTv/TunerHosts", { + type: 'DELETE', + url: ApiClient.getUrl('LiveTv/TunerHosts', { Id: id }) }).then(function () { @@ -65,7 +65,7 @@ define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layo function reload(page) { loading.show(); - ApiClient.getNamedConfiguration("livetv").then(function (config) { + ApiClient.getNamedConfiguration('livetv').then(function (config) { renderDevices(page, config.TunerHosts); renderProviders(page, config.ListingProviders); }); @@ -73,27 +73,27 @@ define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layo } function submitAddDeviceForm(page) { - page.querySelector(".dlgAddDevice").close(); + page.querySelector('.dlgAddDevice').close(); loading.show(); ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("LiveTv/TunerHosts"), + type: 'POST', + url: ApiClient.getUrl('LiveTv/TunerHosts'), data: JSON.stringify({ - Type: $("#selectTunerDeviceType", page).val(), - Url: $("#txtDevicePath", page).val() + Type: $('#selectTunerDeviceType', page).val(), + Url: $('#txtDevicePath', page).val() }), - contentType: "application/json" + contentType: 'application/json' }).then(function () { reload(page); }, function () { Dashboard.alert({ - message: globalize.translate("ErrorAddingTunerDevice") + message: globalize.translate('ErrorAddingTunerDevice') }); }); } function renderProviders(page, providers) { - var html = ""; + var html = ''; if (providers.length) { html += '
'; @@ -101,27 +101,27 @@ define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layo for (var i = 0, length = providers.length; i < length; i++) { var provider = providers[i]; html += '"; + html += provider.Path || provider.ListingsId || ''; + html += '
'; + html += ''; + html += ''; + html += ''; + html += ''; } - html += ""; + html += ''; } - var elem = $(".providerList", page).html(html); - $(".btnOptions", elem).on("click", function () { - var id = this.getAttribute("data-id"); + var elem = $('.providerList', page).html(html); + $('.btnOptions', elem).on('click', function () { + var id = this.getAttribute('data-id'); showProviderOptions(page, id, this); }); } @@ -129,25 +129,25 @@ define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layo function showProviderOptions(page, providerId, button) { var items = []; items.push({ - name: globalize.translate("ButtonDelete"), - id: "delete" + name: globalize.translate('ButtonDelete'), + id: 'delete' }); items.push({ - name: globalize.translate("MapChannels"), - id: "map" + name: globalize.translate('MapChannels'), + id: 'map' }); - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: items, positionTo: button }).then(function (id) { switch (id) { - case "delete": + case 'delete': deleteProvider(page, providerId); break; - case "map": + case 'map': mapChannels(page, providerId); } }); @@ -155,7 +155,7 @@ define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layo } function mapChannels(page, providerId) { - require(["components/channelmapper/channelmapper"], function (channelmapper) { + require(['components/channelMapper/channelMapper'], function (channelmapper) { new channelmapper({ serverId: ApiClient.serverInfo().Id, providerId: providerId @@ -164,14 +164,14 @@ define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layo } function deleteProvider(page, id) { - var message = globalize.translate("MessageConfirmDeleteGuideProvider"); + var message = globalize.translate('MessageConfirmDeleteGuideProvider'); - require(["confirm"], function (confirm) { - confirm(message, globalize.translate("HeaderDeleteProvider")).then(function () { + require(['confirm'], function (confirm) { + confirm(message, globalize.translate('HeaderDeleteProvider')).then(function () { loading.show(); ApiClient.ajax({ - type: "DELETE", - url: ApiClient.getUrl("LiveTv/ListingProviders", { + type: 'DELETE', + url: ApiClient.getUrl('LiveTv/ListingProviders', { Id: id }) }).then(function () { @@ -185,51 +185,51 @@ define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layo function getTunerName(providerId) { switch (providerId = providerId.toLowerCase()) { - case "m3u": - return "M3U"; - case "hdhomerun": - return "HDHomeRun"; - case "hauppauge": - return "Hauppauge"; - case "satip": - return "DVB"; + case 'm3u': + return 'M3U'; + case 'hdhomerun': + return 'HDHomeRun'; + case 'hauppauge': + return 'Hauppauge'; + case 'satip': + return 'DVB'; default: - return "Unknown"; + return 'Unknown'; } } function getProviderName(providerId) { switch (providerId = providerId.toLowerCase()) { - case "schedulesdirect": - return "Schedules Direct"; - case "xmltv": - return "XMLTV"; + case 'schedulesdirect': + return 'Schedules Direct'; + case 'xmltv': + return 'XMLTV'; default: - return "Unknown"; + return 'Unknown'; } } function getProviderConfigurationUrl(providerId) { switch (providerId = providerId.toLowerCase()) { - case "xmltv": - return "livetvguideprovider.html?type=xmltv"; - case "schedulesdirect": - return "livetvguideprovider.html?type=schedulesdirect"; + case 'xmltv': + return 'livetvguideprovider.html?type=xmltv'; + case 'schedulesdirect': + return 'livetvguideprovider.html?type=schedulesdirect'; } } function addProvider(button) { var menuItems = []; menuItems.push({ - name: "Schedules Direct", - id: "SchedulesDirect" + name: 'Schedules Direct', + id: 'SchedulesDirect' }); menuItems.push({ - name: "XMLTV", - id: "xmltv" + name: 'XMLTV', + id: 'xmltv' }); - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, positionTo: button, @@ -241,81 +241,81 @@ define(["jQuery", "globalize", "scripts/taskbutton", "dom", "libraryMenu", "layo } function addDevice(button) { - Dashboard.navigate("livetvtuner.html"); + Dashboard.navigate('livetvtuner.html'); } function showDeviceMenu(button, tunerDeviceId) { var items = []; items.push({ - name: globalize.translate("ButtonDelete"), - id: "delete" + name: globalize.translate('ButtonDelete'), + id: 'delete' }); items.push({ - name: globalize.translate("ButtonEdit"), - id: "edit" + name: globalize.translate('ButtonEdit'), + id: 'edit' }); - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: items, positionTo: button }).then(function (id) { switch (id) { - case "delete": - deleteDevice(dom.parentWithClass(button, "page"), tunerDeviceId); + case 'delete': + deleteDevice(dom.parentWithClass(button, 'page'), tunerDeviceId); break; - case "edit": - Dashboard.navigate("livetvtuner.html?id=" + tunerDeviceId); + case 'edit': + Dashboard.navigate('livetvtuner.html?id=' + tunerDeviceId); } }); }); } function onDevicesListClick(e) { - var card = dom.parentWithClass(e.target, "card"); + var card = dom.parentWithClass(e.target, 'card'); if (card) { - var id = card.getAttribute("data-id"); - var btnCardOptions = dom.parentWithClass(e.target, "btnCardOptions"); + var id = card.getAttribute('data-id'); + var btnCardOptions = dom.parentWithClass(e.target, 'btnCardOptions'); if (btnCardOptions) { showDeviceMenu(btnCardOptions, id); } else { - Dashboard.navigate("livetvtuner.html?id=" + id); + Dashboard.navigate('livetvtuner.html?id=' + id); } } } - $(document).on("pageinit", "#liveTvStatusPage", function () { + $(document).on('pageinit', '#liveTvStatusPage', function () { var page = this; - $(".btnAddDevice", page).on("click", function () { + $('.btnAddDevice', page).on('click', function () { addDevice(this); }); - $(".formAddDevice", page).on("submit", function () { + $('.formAddDevice', page).on('submit', function () { submitAddDeviceForm(page); return false; }); - $(".btnAddProvider", page).on("click", function () { + $('.btnAddProvider', page).on('click', function () { addProvider(this); }); - page.querySelector(".devicesList").addEventListener("click", onDevicesListClick); - }).on("pageshow", "#liveTvStatusPage", function () { + page.querySelector('.devicesList').addEventListener('click', onDevicesListClick); + }).on('pageshow', '#liveTvStatusPage', function () { var page = this; reload(page); taskButton({ - mode: "on", - progressElem: page.querySelector(".refreshGuideProgress"), - taskKey: "RefreshGuide", - button: page.querySelector(".btnRefresh") + mode: 'on', + progressElem: page.querySelector('.refreshGuideProgress'), + taskKey: 'RefreshGuide', + button: page.querySelector('.btnRefresh') }); - }).on("pagehide", "#liveTvStatusPage", function () { + }).on('pagehide', '#liveTvStatusPage', function () { var page = this; taskButton({ - mode: "off", - progressElem: page.querySelector(".refreshGuideProgress"), - taskKey: "RefreshGuide", - button: page.querySelector(".btnRefresh") + mode: 'off', + progressElem: page.querySelector('.refreshGuideProgress'), + taskKey: 'RefreshGuide', + button: page.querySelector('.btnRefresh') }); }); }); diff --git a/src/controllers/livetvtuner.js b/src/controllers/livetvtuner.js index 55a86d4be7..d7a4d92db2 100644 --- a/src/controllers/livetvtuner.js +++ b/src/controllers/livetvtuner.js @@ -1,34 +1,34 @@ -define(["globalize", "loading", "libraryMenu", "dom", "emby-input", "emby-button", "emby-checkbox", "emby-select"], function (globalize, loading, libraryMenu, dom) { - "use strict"; +define(['globalize', 'loading', 'libraryMenu', 'dom', 'emby-input', 'emby-button', 'emby-checkbox', 'emby-select'], function (globalize, loading, libraryMenu, dom) { + 'use strict'; function isM3uVariant(type) { - return ["nextpvr"].indexOf(type || "") !== -1; + return ['nextpvr'].indexOf(type || '') !== -1; } function fillTypes(view, currentId) { - return ApiClient.getJSON(ApiClient.getUrl("LiveTv/TunerHosts/Types")).then(function (types) { - var selectType = view.querySelector(".selectType"); - var html = ""; + return ApiClient.getJSON(ApiClient.getUrl('LiveTv/TunerHosts/Types')).then(function (types) { + var selectType = view.querySelector('.selectType'); + var html = ''; html += types.map(function (tuner) { - return '"; - }).join(""); + return ''; + }).join(''); html += '"; + html += globalize.translate('TabOther'); + html += ''; selectType.innerHTML = html; selectType.disabled = null != currentId; - selectType.value = ""; + selectType.value = ''; onTypeChange.call(selectType); }); } function reload(view, providerId) { - view.querySelector(".txtDevicePath").value = ""; - view.querySelector(".chkFavorite").checked = false; - view.querySelector(".txtDevicePath").value = ""; + view.querySelector('.txtDevicePath').value = ''; + view.querySelector('.chkFavorite').checked = false; + view.querySelector('.txtDevicePath').value = ''; if (providerId) { - ApiClient.getNamedConfiguration("livetv").then(function (config) { + ApiClient.getNamedConfiguration('livetv').then(function (config) { var info = config.TunerHosts.filter(function (i) { return i.Id === providerId; })[0]; @@ -38,8 +38,8 @@ define(["globalize", "loading", "libraryMenu", "dom", "emby-input", "emby-button } function fillTunerHostInfo(view, info) { - var selectType = view.querySelector(".selectType"); - var type = info.Type || ""; + var selectType = view.querySelector('.selectType'); + var type = info.Type || ''; if (info.Source && isM3uVariant(info.Source)) { type = info.Source; @@ -47,54 +47,53 @@ define(["globalize", "loading", "libraryMenu", "dom", "emby-input", "emby-button selectType.value = type; onTypeChange.call(selectType); - view.querySelector(".txtDevicePath").value = info.Url || ""; - view.querySelector(".txtFriendlyName").value = info.FriendlyName || ""; - view.querySelector(".txtUserAgent").value = info.UserAgent || ""; - view.querySelector(".fldDeviceId").value = info.DeviceId || ""; - view.querySelector(".chkFavorite").checked = info.ImportFavoritesOnly; - view.querySelector(".chkTranscode").checked = info.AllowHWTranscoding; - view.querySelector(".chkStreamLoop").checked = info.EnableStreamLooping; - view.querySelector(".txtTunerCount").value = info.TunerCount || "0"; + view.querySelector('.txtDevicePath').value = info.Url || ''; + view.querySelector('.txtFriendlyName').value = info.FriendlyName || ''; + view.querySelector('.txtUserAgent').value = info.UserAgent || ''; + view.querySelector('.fldDeviceId').value = info.DeviceId || ''; + view.querySelector('.chkFavorite').checked = info.ImportFavoritesOnly; + view.querySelector('.chkTranscode').checked = info.AllowHWTranscoding; + view.querySelector('.chkStreamLoop').checked = info.EnableStreamLooping; + view.querySelector('.txtTunerCount').value = info.TunerCount || '0'; } function submitForm(page) { loading.show(); var info = { - Type: page.querySelector(".selectType").value, - Url: page.querySelector(".txtDevicePath").value || null, - UserAgent: page.querySelector(".txtUserAgent").value || null, - FriendlyName: page.querySelector(".txtFriendlyName").value || null, - DeviceId: page.querySelector(".fldDeviceId").value || null, - TunerCount: page.querySelector(".txtTunerCount").value || 0, - ImportFavoritesOnly: page.querySelector(".chkFavorite").checked, - AllowHWTranscoding: page.querySelector(".chkTranscode").checked, - EnableStreamLooping: page.querySelector(".chkStreamLoop").checked + Type: page.querySelector('.selectType').value, + Url: page.querySelector('.txtDevicePath').value || null, + UserAgent: page.querySelector('.txtUserAgent').value || null, + FriendlyName: page.querySelector('.txtFriendlyName').value || null, + DeviceId: page.querySelector('.fldDeviceId').value || null, + TunerCount: page.querySelector('.txtTunerCount').value || 0, + ImportFavoritesOnly: page.querySelector('.chkFavorite').checked, + AllowHWTranscoding: page.querySelector('.chkTranscode').checked, + EnableStreamLooping: page.querySelector('.chkStreamLoop').checked }; if (isM3uVariant(info.Type)) { info.Source = info.Type; - info.Type = "m3u"; + info.Type = 'm3u'; } - var id = getParameterByName("id"); + var id = getParameterByName('id'); if (id) { info.Id = id; } - info.Id; ApiClient.ajax({ - type: "POST", - url: ApiClient.getUrl("LiveTv/TunerHosts"), + type: 'POST', + url: ApiClient.getUrl('LiveTv/TunerHosts'), data: JSON.stringify(info), - contentType: "application/json" + contentType: 'application/json' }).then(function (result) { Dashboard.processServerConfigurationUpdateResult(); - Dashboard.navigate("livetvstatus.html"); + Dashboard.navigate('livetvstatus.html'); }, function () { loading.hide(); Dashboard.alert({ - message: globalize.translate("ErrorSavingTvProvider") + message: globalize.translate('ErrorSavingTvProvider') }); }); } @@ -106,7 +105,7 @@ define(["globalize", "loading", "libraryMenu", "dom", "emby-input", "emby-button } function getDetectedDevice() { - return getRequirePromise(["tunerPicker"]).then(function (tunerPicker) { + return getRequirePromise(['tunerPicker']).then(function (tunerPicker) { return new tunerPicker().show({ serverId: ApiClient.serverId() }); @@ -115,113 +114,113 @@ define(["globalize", "loading", "libraryMenu", "dom", "emby-input", "emby-button function onTypeChange() { var value = this.value; - var view = dom.parentWithClass(this, "page"); - var mayIncludeUnsupportedDrmChannels = "hdhomerun" === value; - var supportsTranscoding = "hdhomerun" === value; - var supportsFavorites = "hdhomerun" === value; - var supportsTunerIpAddress = "hdhomerun" === value; - var supportsTunerFileOrUrl = "m3u" === value; - var supportsStreamLooping = "m3u" === value; - var supportsTunerCount = "m3u" === value; - var supportsUserAgent = "m3u" === value; - var suppportsSubmit = "other" !== value; + var view = dom.parentWithClass(this, 'page'); + var mayIncludeUnsupportedDrmChannels = 'hdhomerun' === value; + var supportsTranscoding = 'hdhomerun' === value; + var supportsFavorites = 'hdhomerun' === value; + var supportsTunerIpAddress = 'hdhomerun' === value; + var supportsTunerFileOrUrl = 'm3u' === value; + var supportsStreamLooping = 'm3u' === value; + var supportsTunerCount = 'm3u' === value; + var supportsUserAgent = 'm3u' === value; + var suppportsSubmit = 'other' !== value; var supportsSelectablePath = supportsTunerFileOrUrl; - var txtDevicePath = view.querySelector(".txtDevicePath"); + var txtDevicePath = view.querySelector('.txtDevicePath'); if (supportsTunerIpAddress) { - txtDevicePath.label(globalize.translate("LabelTunerIpAddress")); - view.querySelector(".fldPath").classList.remove("hide"); + txtDevicePath.label(globalize.translate('LabelTunerIpAddress')); + view.querySelector('.fldPath').classList.remove('hide'); } else if (supportsTunerFileOrUrl) { - txtDevicePath.label(globalize.translate("LabelFileOrUrl")); - view.querySelector(".fldPath").classList.remove("hide"); + txtDevicePath.label(globalize.translate('LabelFileOrUrl')); + view.querySelector('.fldPath').classList.remove('hide'); } else { - view.querySelector(".fldPath").classList.add("hide"); + view.querySelector('.fldPath').classList.add('hide'); } if (supportsSelectablePath) { - view.querySelector(".btnSelectPath").classList.remove("hide"); - view.querySelector(".txtDevicePath").setAttribute("required", "required"); + view.querySelector('.btnSelectPath').classList.remove('hide'); + view.querySelector('.txtDevicePath').setAttribute('required', 'required'); } else { - view.querySelector(".btnSelectPath").classList.add("hide"); - view.querySelector(".txtDevicePath").removeAttribute("required"); + view.querySelector('.btnSelectPath').classList.add('hide'); + view.querySelector('.txtDevicePath').removeAttribute('required'); } if (supportsUserAgent) { - view.querySelector(".fldUserAgent").classList.remove("hide"); + view.querySelector('.fldUserAgent').classList.remove('hide'); } else { - view.querySelector(".fldUserAgent").classList.add("hide"); + view.querySelector('.fldUserAgent').classList.add('hide'); } if (supportsFavorites) { - view.querySelector(".fldFavorites").classList.remove("hide"); + view.querySelector('.fldFavorites').classList.remove('hide'); } else { - view.querySelector(".fldFavorites").classList.add("hide"); + view.querySelector('.fldFavorites').classList.add('hide'); } if (supportsTranscoding) { - view.querySelector(".fldTranscode").classList.remove("hide"); + view.querySelector('.fldTranscode').classList.remove('hide'); } else { - view.querySelector(".fldTranscode").classList.add("hide"); + view.querySelector('.fldTranscode').classList.add('hide'); } if (supportsStreamLooping) { - view.querySelector(".fldStreamLoop").classList.remove("hide"); + view.querySelector('.fldStreamLoop').classList.remove('hide'); } else { - view.querySelector(".fldStreamLoop").classList.add("hide"); + view.querySelector('.fldStreamLoop').classList.add('hide'); } if (supportsTunerCount) { - view.querySelector(".fldTunerCount").classList.remove("hide"); - view.querySelector(".txtTunerCount").setAttribute("required", "required"); + view.querySelector('.fldTunerCount').classList.remove('hide'); + view.querySelector('.txtTunerCount').setAttribute('required', 'required'); } else { - view.querySelector(".fldTunerCount").classList.add("hide"); - view.querySelector(".txtTunerCount").removeAttribute("required"); + view.querySelector('.fldTunerCount').classList.add('hide'); + view.querySelector('.txtTunerCount').removeAttribute('required'); } if (mayIncludeUnsupportedDrmChannels) { - view.querySelector(".drmMessage").classList.remove("hide"); + view.querySelector('.drmMessage').classList.remove('hide'); } else { - view.querySelector(".drmMessage").classList.add("hide"); + view.querySelector('.drmMessage').classList.add('hide'); } if (suppportsSubmit) { - view.querySelector(".button-submit").classList.remove("hide"); + view.querySelector('.button-submit').classList.remove('hide'); } else { - view.querySelector(".button-submit").classList.add("hide"); + view.querySelector('.button-submit').classList.add('hide'); } } return function (view, params) { if (!params.id) { - view.querySelector(".btnDetect").classList.remove("hide"); + view.querySelector('.btnDetect').classList.remove('hide'); } - view.addEventListener("viewshow", function () { + view.addEventListener('viewshow', function () { var currentId = params.id; fillTypes(view, currentId).then(function () { reload(view, currentId); }); }); - view.querySelector("form").addEventListener("submit", function (e) { + view.querySelector('form').addEventListener('submit', function (e) { submitForm(view); e.preventDefault(); e.stopPropagation(); return false; }); - view.querySelector(".selectType").addEventListener("change", onTypeChange); - view.querySelector(".btnDetect").addEventListener("click", function () { + view.querySelector('.selectType').addEventListener('change', onTypeChange); + view.querySelector('.btnDetect').addEventListener('click', function () { getDetectedDevice().then(function (info) { fillTunerHostInfo(view, info); }); }); - view.querySelector(".btnSelectPath").addEventListener("click", function () { - require(["directorybrowser"], function (directoryBrowser) { + view.querySelector('.btnSelectPath').addEventListener('click', function () { + require(['directorybrowser'], function (directoryBrowser) { var picker = new directoryBrowser(); picker.show({ includeFiles: true, callback: function (path) { if (path) { - view.querySelector(".txtDevicePath").value = path; + view.querySelector('.txtDevicePath').value = path; } picker.close(); diff --git a/src/controllers/metadataimagespage.js b/src/controllers/metadataimagespage.js deleted file mode 100644 index 39b4ca6fef..0000000000 --- a/src/controllers/metadataimagespage.js +++ /dev/null @@ -1,64 +0,0 @@ -define(["jQuery", "dom", "loading", "libraryMenu", "listViewStyle"], function($, dom, loading, libraryMenu) { - "use strict"; - - function populateLanguages(select) { - return ApiClient.getCultures().then(function(languages) { - var html = ""; - html += ""; - for (var i = 0, length = languages.length; i < length; i++) { - var culture = languages[i]; - html += ""; - } - select.innerHTML = html; - }); - } - - function populateCountries(select) { - return ApiClient.getCountries().then(function(allCountries) { - var html = ""; - html += ""; - for (var i = 0, length = allCountries.length; i < length; i++) { - var culture = allCountries[i]; - html += ""; - } - select.innerHTML = html; - }); - } - - function loadPage(page) { - var promises = [ApiClient.getServerConfiguration(), populateLanguages(page.querySelector("#selectLanguage")), populateCountries(page.querySelector("#selectCountry"))]; - Promise.all(promises).then(function(responses) { - var config = responses[0]; - page.querySelector("#selectLanguage").value = config.PreferredMetadataLanguage || "", page.querySelector("#selectCountry").value = config.MetadataCountryCode || "", loading.hide(); - }); - } - - function onSubmit() { - var form = this; - return loading.show(), ApiClient.getServerConfiguration().then(function(config) { - config.PreferredMetadataLanguage = form.querySelector("#selectLanguage").value, config.MetadataCountryCode = form.querySelector("#selectCountry").value, ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult); - }), !1; - } - - function getTabs() { - return [{ - href: "library.html", - name: Globalize.translate("HeaderLibraries") - }, { - href: "librarydisplay.html", - name: Globalize.translate("TabDisplay") - }, { - href: "metadataimages.html", - name: Globalize.translate("TabMetadata") - }, { - href: "metadatanfo.html", - name: Globalize.translate("TabNfoSettings") - }]; - } - - $(document).on("pageinit", "#metadataImagesConfigurationPage", function() { - $(".metadataImagesConfigurationForm").off("submit", onSubmit).on("submit", onSubmit); - }).on("pageshow", "#metadataImagesConfigurationPage", function() { - libraryMenu.setTabs("metadata", 2, getTabs), loading.show(), loadPage(this); - }); -}); diff --git a/src/controllers/metadatanfo.js b/src/controllers/metadatanfo.js deleted file mode 100644 index 20049837dd..0000000000 --- a/src/controllers/metadatanfo.js +++ /dev/null @@ -1,74 +0,0 @@ -define(["jQuery", "loading", "libraryMenu"], function ($, loading, libraryMenu) { - "use strict"; - - function loadPage(page, config, users) { - var html = '"; - html += users.map(function (user) { - return '"; - }).join(""); - $("#selectUser", page).html(html).val(config.UserId || ""); - $("#selectReleaseDateFormat", page).val(config.ReleaseDateFormat); - page.querySelector("#chkSaveImagePaths").checked = config.SaveImagePathsInNfo; - page.querySelector("#chkEnablePathSubstitution").checked = config.EnablePathSubstitution; - page.querySelector("#chkEnableExtraThumbs").checked = config.EnableExtraThumbsDuplication; - loading.hide(); - } - - function onSubmit() { - loading.show(); - var form = this; - ApiClient.getNamedConfiguration(metadataKey).then(function (config) { - config.UserId = $("#selectUser", form).val() || null; - config.ReleaseDateFormat = $("#selectReleaseDateFormat", form).val(); - config.SaveImagePathsInNfo = form.querySelector("#chkSaveImagePaths").checked; - config.EnablePathSubstitution = form.querySelector("#chkEnablePathSubstitution").checked; - config.EnableExtraThumbsDuplication = form.querySelector("#chkEnableExtraThumbs").checked; - ApiClient.updateNamedConfiguration(metadataKey, config).then(function () { - Dashboard.processServerConfigurationUpdateResult(); - showConfirmMessage(config); - }); - }); - return false; - } - - function showConfirmMessage(config) { - var msg = []; - msg.push(Globalize.translate("MetadataSettingChangeHelp")); - - require(["alert"], function (alert) { - alert({ - text: msg.join("

") - }); - }); - } - - function getTabs() { - return [{ - href: "library.html", - name: Globalize.translate("HeaderLibraries") - }, { - href: "librarydisplay.html", - name: Globalize.translate("TabDisplay") - }, { - href: "metadataimages.html", - name: Globalize.translate("TabMetadata") - }, { - href: "metadatanfo.html", - name: Globalize.translate("TabNfoSettings") - }]; - } - - var metadataKey = "xbmcmetadata"; - $(document).on("pageinit", "#metadataNfoPage", function () { - $(".metadataNfoForm").off("submit", onSubmit).on("submit", onSubmit); - }).on("pageshow", "#metadataNfoPage", function () { - libraryMenu.setTabs("metadata", 3, getTabs); - loading.show(); - var page = this; - var promise1 = ApiClient.getUsers(); - var promise2 = ApiClient.getNamedConfiguration(metadataKey); - Promise.all([promise1, promise2]).then(function (responses) { - loadPage(page, responses[1], responses[0]); - }); - }); -}); diff --git a/src/controllers/movies/moviecollections.js b/src/controllers/movies/moviecollections.js index f17f64b7c8..e9ae599b92 100644 --- a/src/controllers/movies/moviecollections.js +++ b/src/controllers/movies/moviecollections.js @@ -1,5 +1,5 @@ -define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardBuilder", "apphost", "emby-itemscontainer"], function (loading, events, libraryBrowser, imageLoader, listView, cardBuilder, appHost) { - "use strict"; +define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (loading, events, libraryBrowser, imageLoader, listView, cardBuilder, userSettings, globalize) { + 'use strict'; return function (view, params, tabContent) { function getPageData(context) { @@ -9,18 +9,22 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB if (!pageData) { pageData = data[key] = { query: { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "BoxSet", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'BoxSet', Recursive: true, - Fields: "PrimaryImageAspectRatio,SortName", + Fields: 'PrimaryImageAspectRatio,SortName', ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", - StartIndex: 0, - Limit: pageSize + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', + StartIndex: 0 }, - view: libraryBrowser.getSavedView(key) || "Poster" + view: libraryBrowser.getSavedView(key) || 'Poster' }; + + if (userSettings.libraryPageSize() > 0) { + pageData.query['Limit'] = userSettings.libraryPageSize(); + } + pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); } @@ -34,7 +38,7 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB function getSavedQueryKey(context) { if (!context.savedQueryKey) { - context.savedQueryKey = libraryBrowser.getSavedQueryKey("moviecollections"); + context.savedQueryKey = libraryBrowser.getSavedQueryKey('moviecollections'); } return context.savedQueryKey; @@ -42,17 +46,17 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB function onViewStyleChange() { var viewStyle = self.getCurrentViewStyle(); - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); - if ("List" == viewStyle) { - itemsContainer.classList.add("vertical-list"); - itemsContainer.classList.remove("vertical-wrap"); + if ('List' == viewStyle) { + itemsContainer.classList.add('vertical-list'); + itemsContainer.classList.remove('vertical-wrap'); } else { - itemsContainer.classList.remove("vertical-list"); - itemsContainer.classList.add("vertical-wrap"); + itemsContainer.classList.remove('vertical-list'); + itemsContainer.classList.add('vertical-wrap'); } - itemsContainer.innerHTML = ""; + itemsContainer.innerHTML = ''; } function reloadItems(page) { @@ -65,7 +69,9 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB return; } - query.StartIndex += query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } reloadItems(tabContent); } @@ -74,7 +80,9 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB return; } - query.StartIndex -= query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } reloadItems(tabContent); } @@ -91,45 +99,45 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB filterButton: false }); var viewStyle = self.getCurrentViewStyle(); - if (viewStyle == "Thumb") { + if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, - context: "movies", + context: 'movies', overlayPlayButton: true, centerText: true, showTitle: true }); - } else if (viewStyle == "ThumbCard") { + } else if (viewStyle == 'ThumbCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, - context: "movies", + context: 'movies', lazy: true, cardLayout: true, showTitle: true }); - } else if (viewStyle == "Banner") { + } else if (viewStyle == 'Banner') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "banner", + shape: 'banner', preferBanner: true, - context: "movies", + context: 'movies', lazy: true }); - } else if (viewStyle == "List") { + } else if (viewStyle == 'List') { html = listView.getListViewHtml({ items: result.Items, - context: "movies", + context: 'movies', sortBy: query.SortBy }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "auto", - context: "movies", + shape: 'auto', + context: 'movies', showTitle: true, centerText: false, cardLayout: true @@ -137,8 +145,8 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB } else { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "auto", - context: "movies", + shape: 'auto', + context: 'movies', centerText: true, overlayPlayButton: true, showTitle: true @@ -146,41 +154,40 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB } var i; var length; - var elems = tabContent.querySelectorAll(".paging"); + var elems = tabContent.querySelectorAll('.paging'); for (i = 0, length = elems.length; i < length; i++) { elems[i].innerHTML = pagingHtml; } - elems = tabContent.querySelectorAll(".btnNextPage"); + elems = tabContent.querySelectorAll('.btnNextPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onNextPageClick); + elems[i].addEventListener('click', onNextPageClick); } - elems = tabContent.querySelectorAll(".btnPreviousPage"); + elems = tabContent.querySelectorAll('.btnPreviousPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onPreviousPageClick); + elems[i].addEventListener('click', onPreviousPageClick); } if (!result.Items.length) { - html = '

' + Globalize.translate("MessageNoCollectionsAvailable") + "

"; + html = '

' + globalize.translate('MessageNoCollectionsAvailable') + '

'; } - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); libraryBrowser.saveQueryValues(getSavedQueryKey(page), query); loading.hide(); isLoading = false; - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); }); } var self = this; - var pageSize = 100; var data = {}; var isLoading = false; @@ -189,23 +196,23 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB }; function initPage(tabContent) { - tabContent.querySelector(".btnSort").addEventListener("click", function (e) { + tabContent.querySelector('.btnSort').addEventListener('click', function (e) { libraryBrowser.showSortMenu({ items: [{ - name: Globalize.translate("OptionNameSort"), - id: "SortName" + name: globalize.translate('OptionNameSort'), + id: 'SortName' }, { - name: Globalize.translate("OptionImdbRating"), - id: "CommunityRating,SortName" + name: globalize.translate('OptionImdbRating'), + id: 'CommunityRating,SortName' }, { - name: Globalize.translate("OptionDateAdded"), - id: "DateCreated,SortName" + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName' }, { - name: Globalize.translate("OptionParentalRating"), - id: "OfficialRating,SortName" + name: globalize.translate('OptionParentalRating'), + id: 'OfficialRating,SortName' }, { - name: Globalize.translate("OptionReleaseDate"), - id: "PremiereDate,SortName" + name: globalize.translate('OptionReleaseDate'), + id: 'PremiereDate,SortName' }], callback: function () { getQuery(tabContent).StartIndex = 0; @@ -215,11 +222,11 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB button: e.target }); }); - var btnSelectView = tabContent.querySelector(".btnSelectView"); - btnSelectView.addEventListener("click", function (e) { - libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), "List,Poster,PosterCard,Thumb,ThumbCard".split(",")); + var btnSelectView = tabContent.querySelector('.btnSelectView'); + btnSelectView.addEventListener('click', function (e) { + libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'List,Poster,PosterCard,Thumb,ThumbCard'.split(',')); }); - btnSelectView.addEventListener("layoutchange", function (e) { + btnSelectView.addEventListener('layoutchange', function (e) { var viewStyle = e.detail.viewStyle; getPageData(tabContent).view = viewStyle; libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle); @@ -227,8 +234,8 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB onViewStyleChange(); reloadItems(tabContent); }); - tabContent.querySelector(".btnNewCollection").addEventListener("click", function () { - require(["collectionEditor"], function (collectionEditor) { + tabContent.querySelector('.btnNewCollection').addEventListener('click', function () { + require(['collectionEditor'], function (collectionEditor) { var serverId = ApiClient.serverInfo().Id; new collectionEditor().show({ items: [], diff --git a/src/controllers/movies/moviegenres.js b/src/controllers/movies/moviegenres.js index d51a2e4789..e8e49ff9da 100644 --- a/src/controllers/movies/moviegenres.js +++ b/src/controllers/movies/moviegenres.js @@ -1,5 +1,5 @@ -define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader", "apphost", "globalize", "appRouter", "dom", "emby-button"], function (layoutManager, loading, libraryBrowser, cardBuilder, lazyLoader, appHost, globalize, appRouter, dom) { - "use strict"; +define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader', 'apphost', 'globalize', 'appRouter', 'dom', 'emby-button'], function (layoutManager, loading, libraryBrowser, cardBuilder, lazyLoader, appHost, globalize, appRouter, dom) { + 'use strict'; return function (view, params, tabContent) { function getPageData() { @@ -9,13 +9,13 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader if (!pageData) { pageData = data[key] = { query: { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Movie", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Movie', Recursive: true, EnableTotalRecordCount: false }, - view: "Poster" + view: 'Poster' }; pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); @@ -29,7 +29,7 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader } function getSavedQueryKey() { - return libraryBrowser.getSavedQueryKey("moviegenres"); + return libraryBrowser.getSavedQueryKey('moviegenres'); } function getPromise() { @@ -43,29 +43,30 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader } function getThumbShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop"; + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function getPortraitShape() { - return enableScrollX() ? "overflowPortrait" : "portrait"; + return enableScrollX() ? 'overflowPortrait' : 'portrait'; } - function fillItemsContainer(elem) { - var id = elem.getAttribute("data-id"); + function fillItemsContainer(entry) { + var elem = entry.target; + var id = elem.getAttribute('data-id'); var viewStyle = self.getCurrentViewStyle(); - var limit = "Thumb" == viewStyle || "ThumbCard" == viewStyle ? 5 : 9; + var limit = 'Thumb' == viewStyle || 'ThumbCard' == viewStyle ? 5 : 9; if (enableScrollX()) { limit = 10; } - var enableImageTypes = "Thumb" == viewStyle || "ThumbCard" == viewStyle ? "Primary,Backdrop,Thumb" : "Primary"; + var enableImageTypes = 'Thumb' == viewStyle || 'ThumbCard' == viewStyle ? 'Primary,Backdrop,Thumb' : 'Primary'; var query = { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Movie", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Movie', Recursive: true, - Fields: "PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', ImageTypeLimit: 1, EnableImageTypes: enableImageTypes, Limit: limit, @@ -74,9 +75,9 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader ParentId: params.topParentId }; ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) { - var supportsImageAnalysis = appHost.supports("imageanalysis"); + var supportsImageAnalysis = appHost.supports('imageanalysis'); - if (viewStyle == "Thumb") { + if (viewStyle == 'Thumb') { cardBuilder.buildCards(result.Items, { itemsContainer: elem, shape: getThumbShape(), @@ -87,7 +88,7 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader overlayMoreButton: true, allowBottomPadding: false }); - } else if (viewStyle == "ThumbCard") { + } else if (viewStyle == 'ThumbCard') { cardBuilder.buildCards(result.Items, { itemsContainer: elem, shape: getThumbShape(), @@ -98,7 +99,7 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader cardLayout: true, showYear: true }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { cardBuilder.buildCards(result.Items, { itemsContainer: elem, shape: getPortraitShape(), @@ -108,7 +109,7 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader cardLayout: true, showYear: true }); - } else if (viewStyle == "Poster") { + } else if (viewStyle == 'Poster') { cardBuilder.buildCards(result.Items, { itemsContainer: elem, shape: getPortraitShape(), @@ -121,7 +122,7 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader }); } if (result.Items.length >= query.Limit) { - tabContent.querySelector(".btnMoreFromGenre" + id + " i").classList.remove("hide"); + tabContent.querySelector('.btnMoreFromGenre' + id + ' .material-icons').classList.remove('hide'); } }); } @@ -129,8 +130,8 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader function reloadItems(context, promise) { var query = getQuery(); promise.then(function (result) { - var elem = context.querySelector("#items"); - var html = ""; + var elem = context.querySelector('#items'); + var html = ''; var items = result.Items; for (var i = 0, length = items.length; i < length; i++) { @@ -139,20 +140,20 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader html += '
'; html += '"; + html += ''; + html += ''; + html += ''; + html += '
'; if (enableScrollX()) { - var scrollXClass = "scrollX hiddenScrollX"; + var scrollXClass = 'scrollX hiddenScrollX'; if (layoutManager.tv) { - scrollXClass += "smoothScrollX padded-top-focusscale padded-bottom-focusscale"; + scrollXClass += 'smoothScrollX padded-top-focusscale padded-bottom-focusscale'; } html += '
'; @@ -160,8 +161,8 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader html += '
'; } - html += "
"; - html += "
"; + html += ''; + html += ''; } elem.innerHTML = html; @@ -180,16 +181,16 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader var data = {}; self.getViewStyles = function () { - return "Poster,PosterCard,Thumb,ThumbCard".split(","); + return 'Poster,PosterCard,Thumb,ThumbCard'.split(','); }; self.getCurrentViewStyle = function () { - return getPageData(tabContent).view; + return getPageData().view; }; self.setCurrentViewStyle = function (viewStyle) { - getPageData(tabContent).view = viewStyle; - libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle); + getPageData().view = viewStyle; + libraryBrowser.saveViewSetting(getSavedQueryKey(), viewStyle); fullyReload(); }; diff --git a/src/controllers/movies/movies.js b/src/controllers/movies/movies.js index ce077bd179..c22b52c47e 100644 --- a/src/controllers/movies/movies.js +++ b/src/controllers/movies/movies.js @@ -1,17 +1,17 @@ -define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", "alphaPicker", "listView", "cardBuilder", "emby-itemscontainer"], function (loading, layoutManager, userSettings, events, libraryBrowser, alphaPicker, listView, cardBuilder) { - "use strict"; +define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', 'alphaPicker', 'listView', 'cardBuilder', 'globalize', 'emby-itemscontainer'], function (loading, layoutManager, userSettings, events, libraryBrowser, alphaPicker, listView, cardBuilder, globalize) { + 'use strict'; return function (view, params, tabContent, options) { function onViewStyleChange() { - if (self.getCurrentViewStyle() == "List") { - itemsContainer.classList.add("vertical-list"); - itemsContainer.classList.remove("vertical-wrap"); + if (self.getCurrentViewStyle() == 'List') { + itemsContainer.classList.add('vertical-list'); + itemsContainer.classList.remove('vertical-wrap'); } else { - itemsContainer.classList.remove("vertical-list"); - itemsContainer.classList.add("vertical-wrap"); + itemsContainer.classList.remove('vertical-list'); + itemsContainer.classList.add('vertical-wrap'); } - itemsContainer.innerHTML = ""; + itemsContainer.innerHTML = ''; } function updateFilterControls() { @@ -32,7 +32,9 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", return; } - query.StartIndex += query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } itemsContainer.refreshItems(); } @@ -41,7 +43,9 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", return; } - query.StartIndex -= query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } itemsContainer.refreshItems(); } @@ -59,26 +63,26 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", }); var i; var length; - var elems = tabContent.querySelectorAll(".paging"); + var elems = tabContent.querySelectorAll('.paging'); for (i = 0, length = elems.length; i < length; i++) { elems[i].innerHTML = pagingHtml; } - elems = tabContent.querySelectorAll(".btnNextPage"); + elems = tabContent.querySelectorAll('.btnNextPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onNextPageClick); + elems[i].addEventListener('click', onNextPageClick); } - elems = tabContent.querySelectorAll(".btnPreviousPage"); + elems = tabContent.querySelectorAll('.btnPreviousPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onPreviousPageClick); + elems[i].addEventListener('click', onPreviousPageClick); } isLoading = false; loading.hide(); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(tabContent); }); } @@ -87,49 +91,49 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", var html; var viewStyle = self.getCurrentViewStyle(); - if (viewStyle == "Thumb") { + if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ items: items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, - context: "movies", + context: 'movies', lazy: true, overlayPlayButton: true, showTitle: true, showYear: true, centerText: true }); - } else if (viewStyle == "ThumbCard") { + } else if (viewStyle == 'ThumbCard') { html = cardBuilder.getCardsHtml({ items: items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, - context: "movies", + context: 'movies', lazy: true, cardLayout: true, showTitle: true, showYear: true, centerText: true }); - } else if (viewStyle == "Banner") { + } else if (viewStyle == 'Banner') { html = cardBuilder.getCardsHtml({ items: items, - shape: "banner", + shape: 'banner', preferBanner: true, - context: "movies", + context: 'movies', lazy: true }); - } else if (viewStyle == "List") { + } else if (viewStyle == 'List') { html = listView.getListViewHtml({ items: items, - context: "movies", + context: 'movies', sortBy: query.SortBy }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml({ items: items, - shape: "portrait", - context: "movies", + shape: 'portrait', + context: 'movies', showTitle: true, showYear: true, centerText: true, @@ -139,8 +143,8 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", } else { html = cardBuilder.getCardsHtml({ items: items, - shape: "portrait", - context: "movies", + shape: 'portrait', + context: 'movies', overlayPlayButton: true, showTitle: true, showYear: true, @@ -155,10 +159,10 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", itemsContainer.fetchData = fetchData; itemsContainer.getItemsHtml = getItemsHtml; itemsContainer.afterRefresh = afterRefresh; - var alphaPickerElement = tabContent.querySelector(".alphaPicker"); + var alphaPickerElement = tabContent.querySelector('.alphaPicker'); if (alphaPickerElement) { - alphaPickerElement.addEventListener("alphavaluechanged", function (e) { + alphaPickerElement.addEventListener('alphavaluechanged', function (e) { var newValue = e.detail.value; query.NameStartsWithOrGreater = newValue; query.StartIndex = 0; @@ -166,53 +170,53 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", }); self.alphaPicker = new alphaPicker({ element: alphaPickerElement, - valueChangeEvent: "click" + valueChangeEvent: 'click' }); - tabContent.querySelector(".alphaPicker").classList.add("alphabetPicker-right"); - alphaPickerElement.classList.add("alphaPicker-fixed-right"); - itemsContainer.classList.add("padded-right-withalphapicker"); + tabContent.querySelector('.alphaPicker').classList.add('alphabetPicker-right'); + alphaPickerElement.classList.add('alphaPicker-fixed-right'); + itemsContainer.classList.add('padded-right-withalphapicker'); } - var btnFilter = tabContent.querySelector(".btnFilter"); + var btnFilter = tabContent.querySelector('.btnFilter'); if (btnFilter) { - btnFilter.addEventListener("click", function () { + btnFilter.addEventListener('click', function () { self.showFilterMenu(); }); } - var btnSort = tabContent.querySelector(".btnSort"); + var btnSort = tabContent.querySelector('.btnSort'); if (btnSort) { - btnSort.addEventListener("click", function (e) { + btnSort.addEventListener('click', function (e) { libraryBrowser.showSortMenu({ items: [{ - name: Globalize.translate("OptionNameSort"), - id: "SortName,ProductionYear" + name: globalize.translate('OptionNameSort'), + id: 'SortName,ProductionYear' }, { - name: Globalize.translate("OptionImdbRating"), - id: "CommunityRating,SortName,ProductionYear" + name: globalize.translate('OptionImdbRating'), + id: 'CommunityRating,SortName,ProductionYear' }, { - name: Globalize.translate("OptionCriticRating"), - id: "CriticRating,SortName,ProductionYear" + name: globalize.translate('OptionCriticRating'), + id: 'CriticRating,SortName,ProductionYear' }, { - name: Globalize.translate("OptionDateAdded"), - id: "DateCreated,SortName,ProductionYear" + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName,ProductionYear' }, { - name: Globalize.translate("OptionDatePlayed"), - id: "DatePlayed,SortName,ProductionYear" + name: globalize.translate('OptionDatePlayed'), + id: 'DatePlayed,SortName,ProductionYear' }, { - name: Globalize.translate("OptionParentalRating"), - id: "OfficialRating,SortName,ProductionYear" + name: globalize.translate('OptionParentalRating'), + id: 'OfficialRating,SortName,ProductionYear' }, { - name: Globalize.translate("OptionPlayCount"), - id: "PlayCount,SortName,ProductionYear" + name: globalize.translate('OptionPlayCount'), + id: 'PlayCount,SortName,ProductionYear' }, { - name: Globalize.translate("OptionReleaseDate"), - id: "PremiereDate,SortName,ProductionYear" + name: globalize.translate('OptionReleaseDate'), + id: 'PremiereDate,SortName,ProductionYear' }, { - name: Globalize.translate("OptionRuntime"), - id: "Runtime,SortName,ProductionYear" + name: globalize.translate('OptionRuntime'), + id: 'Runtime,SortName,ProductionYear' }], callback: function () { query.StartIndex = 0; @@ -224,11 +228,11 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", }); }); } - var btnSelectView = tabContent.querySelector(".btnSelectView"); - btnSelectView.addEventListener("click", function (e) { - libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), "Banner,List,Poster,PosterCard,Thumb,ThumbCard".split(",")); + var btnSelectView = tabContent.querySelector('.btnSelectView'); + btnSelectView.addEventListener('click', function (e) { + libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(',')); }); - btnSelectView.addEventListener("layoutchange", function (e) { + btnSelectView.addEventListener('layoutchange', function (e) { var viewStyle = e.detail.viewStyle; userSettings.set(savedViewKey, viewStyle); query.StartIndex = 0; @@ -238,37 +242,41 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", } var self = this; - var itemsContainer = tabContent.querySelector(".itemsContainer"); - var savedQueryKey = params.topParentId + "-" + options.mode; - var savedViewKey = savedQueryKey + "-view"; + var itemsContainer = tabContent.querySelector('.itemsContainer'); + var savedQueryKey = params.topParentId + '-' + options.mode; + var savedViewKey = savedQueryKey + '-view'; var query = { - SortBy: "SortName,ProductionYear", - SortOrder: "Ascending", - IncludeItemTypes: "Movie", + SortBy: 'SortName,ProductionYear', + SortOrder: 'Ascending', + IncludeItemTypes: 'Movie', Recursive: true, - Fields: "PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', StartIndex: 0, - Limit: 100, ParentId: params.topParentId }; + + if (userSettings.libraryPageSize() > 0) { + query['Limit'] = userSettings.libraryPageSize(); + } + var isLoading = false; - if (options.mode === "favorites") { + if (options.mode === 'favorites') { query.IsFavorite = true; } query = userSettings.loadQuerySettings(savedQueryKey, query); self.showFilterMenu = function () { - require(["components/filterdialog/filterdialog"], function (filterDialogFactory) { + require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { var filterDialog = new filterDialogFactory({ query: query, - mode: "movies", + mode: 'movies', serverId: ApiClient.serverId() }); - events.on(filterDialog, "filterchange", function () { + events.on(filterDialog, 'filterchange', function () { query.StartIndex = 0; itemsContainer.refreshItems(); }); @@ -277,7 +285,7 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser", }; self.getCurrentViewStyle = function () { - return userSettings.get(savedViewKey) || "Poster"; + return userSettings.get(savedViewKey) || 'Poster'; }; self.initTab = function () { diff --git a/src/controllers/movies/moviesrecommended.js b/src/controllers/movies/moviesrecommended.js index 98e0871479..d948c1cef7 100644 --- a/src/controllers/movies/moviesrecommended.js +++ b/src/controllers/movies/moviesrecommended.js @@ -1,31 +1,31 @@ -define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu", "mainTabsManager", "cardBuilder", "dom", "imageLoader", "playbackManager", "emby-scroller", "emby-itemscontainer", "emby-tabs", "emby-button"], function (events, layoutManager, inputManager, userSettings, libraryMenu, mainTabsManager, cardBuilder, dom, imageLoader, playbackManager) { - "use strict"; +define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu', 'mainTabsManager', 'cardBuilder', 'dom', 'imageLoader', 'playbackManager', 'globalize', 'emby-scroller', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (events, layoutManager, inputManager, userSettings, libraryMenu, mainTabsManager, cardBuilder, dom, imageLoader, playbackManager, globalize) { + 'use strict'; function enableScrollX() { return !layoutManager.desktop; } function getPortraitShape() { - return enableScrollX() ? "overflowPortrait" : "portrait"; + return enableScrollX() ? 'overflowPortrait' : 'portrait'; } function getThumbShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop"; + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function loadLatest(page, userId, parentId) { var options = { - IncludeItemTypes: "Movie", + IncludeItemTypes: 'Movie', Limit: 18, - Fields: "PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', ParentId: parentId, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', EnableTotalRecordCount: false }; - ApiClient.getJSON(ApiClient.getUrl("Users/" + userId + "/Items/Latest", options)).then(function (items) { + ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) { var allowBottomPadding = !enableScrollX(); - var container = page.querySelector("#recentlyAddedItems"); + var container = page.querySelector('#recentlyAddedItems'); cardBuilder.buildCards(items, { itemsContainer: container, shape: getPortraitShape(), @@ -45,28 +45,28 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" function loadResume(page, userId, parentId) { var screenWidth = dom.getWindowSize().innerWidth; var options = { - SortBy: "DatePlayed", - SortOrder: "Descending", - IncludeItemTypes: "Movie", - Filters: "IsResumable", + SortBy: 'DatePlayed', + SortOrder: 'Descending', + IncludeItemTypes: 'Movie', + Filters: 'IsResumable', Limit: screenWidth >= 1920 ? 5 : screenWidth >= 1600 ? 5 : 3, Recursive: true, - Fields: "PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', CollapseBoxSetItems: false, ParentId: parentId, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', EnableTotalRecordCount: false }; ApiClient.getItems(userId, options).then(function (result) { if (result.Items.length) { - page.querySelector("#resumableSection").classList.remove("hide"); + page.querySelector('#resumableSection').classList.remove('hide'); } else { - page.querySelector("#resumableSection").classList.add("hide"); + page.querySelector('#resumableSection').classList.add('hide'); } var allowBottomPadding = !enableScrollX(); - var container = page.querySelector("#resumableItems"); + var container = page.querySelector('#resumableItems'); cardBuilder.buildCards(result.Items, { itemsContainer: container, preferThumb: true, @@ -86,31 +86,31 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" } function getRecommendationHtml(recommendation) { - var html = ""; - var title = ""; + var html = ''; + var title = ''; switch (recommendation.RecommendationType) { - case "SimilarToRecentlyPlayed": - title = Globalize.translate("RecommendationBecauseYouWatched", recommendation.BaselineItemName); + case 'SimilarToRecentlyPlayed': + title = globalize.translate('RecommendationBecauseYouWatched', recommendation.BaselineItemName); break; - case "SimilarToLikedItem": - title = Globalize.translate("RecommendationBecauseYouLike", recommendation.BaselineItemName); + case 'SimilarToLikedItem': + title = globalize.translate('RecommendationBecauseYouLike', recommendation.BaselineItemName); break; - case "HasDirectorFromRecentlyPlayed": - case "HasLikedDirector": - title = Globalize.translate("RecommendationDirectedBy", recommendation.BaselineItemName); + case 'HasDirectorFromRecentlyPlayed': + case 'HasLikedDirector': + title = globalize.translate('RecommendationDirectedBy', recommendation.BaselineItemName); break; - case "HasActorFromRecentlyPlayed": - case "HasLikedActor": - title = Globalize.translate("RecommendationStarring", recommendation.BaselineItemName); + case 'HasActorFromRecentlyPlayed': + case 'HasLikedActor': + title = globalize.translate('RecommendationStarring', recommendation.BaselineItemName); break; } html += '
'; - html += '

' + title + "

"; + html += '

' + title + '

'; var allowBottomPadding = true; if (enableScrollX()) { @@ -133,31 +133,31 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" if (enableScrollX()) { html += '
'; } - html += ""; - html += ""; + html += ''; + html += ''; return html; } function loadSuggestions(page, userId, parentId) { var screenWidth = dom.getWindowSize().innerWidth; - var url = ApiClient.getUrl("Movies/Recommendations", { + var url = ApiClient.getUrl('Movies/Recommendations', { userId: userId, categoryLimit: 6, ItemLimit: screenWidth >= 1920 ? 8 : screenWidth >= 1600 ? 8 : screenWidth >= 1200 ? 6 : 5, - Fields: "PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb" + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb' }); ApiClient.getJSON(url).then(function (recommendations) { if (!recommendations.length) { - page.querySelector(".noItemsMessage").classList.remove("hide"); - page.querySelector(".recommendations").innerHTML = ""; + page.querySelector('.noItemsMessage').classList.remove('hide'); + page.querySelector('.recommendations').innerHTML = ''; return; } - var html = recommendations.map(getRecommendationHtml).join(""); - page.querySelector(".noItemsMessage").classList.add("hide"); - var recs = page.querySelector(".recommendations"); + var html = recommendations.map(getRecommendationHtml).join(''); + page.querySelector('.noItemsMessage').classList.add('hide'); + var recs = page.querySelector('.recommendations'); recs.innerHTML = html; imageLoader.lazyChildren(recs); @@ -167,33 +167,33 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" } function autoFocus(page) { - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); } function setScrollClasses(elem, scrollX) { if (scrollX) { - elem.classList.add("hiddenScrollX"); + elem.classList.add('hiddenScrollX'); if (layoutManager.tv) { - elem.classList.add("smoothScrollX"); - elem.classList.add("padded-top-focusscale"); - elem.classList.add("padded-bottom-focusscale"); + elem.classList.add('smoothScrollX'); + elem.classList.add('padded-top-focusscale'); + elem.classList.add('padded-bottom-focusscale'); } - elem.classList.add("scrollX"); - elem.classList.remove("vertical-wrap"); + elem.classList.add('scrollX'); + elem.classList.remove('vertical-wrap'); } else { - elem.classList.remove("hiddenScrollX"); - elem.classList.remove("smoothScrollX"); - elem.classList.remove("scrollX"); - elem.classList.add("vertical-wrap"); + elem.classList.remove('hiddenScrollX'); + elem.classList.remove('smoothScrollX'); + elem.classList.remove('scrollX'); + elem.classList.add('vertical-wrap'); } } function initSuggestedTab(page, tabContent) { - var containers = tabContent.querySelectorAll(".itemsContainer"); + var containers = tabContent.querySelectorAll('.itemsContainer'); for (var i = 0, length = containers.length; i < length; i++) { setScrollClasses(containers[i], enableScrollX()); @@ -203,7 +203,7 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" function loadSuggestionsTab(view, params, tabContent) { var parentId = params.topParentId; var userId = ApiClient.getCurrentUserId(); - console.debug("loadSuggestionsTab"); + console.debug('loadSuggestionsTab'); loadResume(tabContent, userId, parentId); loadLatest(tabContent, userId, parentId); loadSuggestions(tabContent, userId, parentId); @@ -211,35 +211,35 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" function getTabs() { return [{ - name: Globalize.translate("Movies") + name: globalize.translate('Movies') }, { - name: Globalize.translate("TabSuggestions") + name: globalize.translate('TabSuggestions') }, { - name: Globalize.translate("TabTrailers") + name: globalize.translate('TabTrailers') }, { - name: Globalize.translate("TabFavorites") + name: globalize.translate('TabFavorites') }, { - name: Globalize.translate("TabCollections") + name: globalize.translate('TabCollections') }, { - name: Globalize.translate("TabGenres") + name: globalize.translate('TabGenres') }, { - name: Globalize.translate("ButtonSearch"), - cssClass: "searchTabButton" + name: globalize.translate('ButtonSearch'), + cssClass: 'searchTabButton' }]; } function getDefaultTabIndex(folderId) { - switch (userSettings.get("landing-" + folderId)) { - case "suggestions": + switch (userSettings.get('landing-' + folderId)) { + case 'suggestions': return 1; - case "favorites": + case 'favorites': return 3; - case "collections": + case 'collections': return 4; - case "genres": + case 'genres': return 5; default: @@ -258,7 +258,7 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" } function getTabContainers() { - return view.querySelectorAll(".pageTabContent"); + return view.querySelectorAll('.pageTabContent'); } function initTabs() { @@ -270,30 +270,30 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" switch (index) { case 0: - depends.push("controllers/movies/movies"); + depends.push('controllers/movies/movies'); break; case 1: break; case 2: - depends.push("controllers/movies/movietrailers"); + depends.push('controllers/movies/movietrailers'); break; case 3: - depends.push("controllers/movies/movies"); + depends.push('controllers/movies/movies'); break; case 4: - depends.push("controllers/movies/moviecollections"); + depends.push('controllers/movies/moviecollections'); break; case 5: - depends.push("controllers/movies/moviegenres"); + depends.push('controllers/movies/moviegenres'); break; case 6: - depends.push("scripts/searchtab"); + depends.push('scripts/searchtab'); } require(depends, function (controllerFactory) { @@ -313,12 +313,12 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" controller = self; } else if (index === 6) { controller = new controllerFactory(view, tabContent, { - collectionType: "movies", + collectionType: 'movies', parentId: params.topParentId }); } else if (index == 0 || index == 3) { controller = new controllerFactory(view, params, tabContent, { - mode: index ? "favorites" : "movies" + mode: index ? 'favorites' : 'movies' }); } else { controller = new controllerFactory(view, params, tabContent); @@ -356,7 +356,7 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" } function onPlaybackStop(e, state) { - if (state.NowPlayingItem && state.NowPlayingItem.MediaType == "Video") { + if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { renderedTabs = []; mainTabsManager.getTabsElement().triggerTabChange(); } @@ -364,9 +364,9 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" function onInputCommand(e) { switch (e.detail.command) { - case "search": + case 'search': e.preventDefault(); - Dashboard.navigate("search.html?collectionType=movies&parentId=" + params.topParentId); + Dashboard.navigate('search.html?collectionType=movies&parentId=' + params.topParentId); } } @@ -388,28 +388,28 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu" var tabControllers = []; var renderedTabs = []; - view.addEventListener("viewshow", function (e) { - if (isViewRestored = e.detail.isRestored, initTabs(), !view.getAttribute("data-title")) { + view.addEventListener('viewshow', function (e) { + if (isViewRestored = e.detail.isRestored, initTabs(), !view.getAttribute('data-title')) { var parentId = params.topParentId; if (parentId) { ApiClient.getItem(ApiClient.getCurrentUserId(), parentId).then(function (item) { - view.setAttribute("data-title", item.Name); + view.setAttribute('data-title', item.Name); libraryMenu.setTitle(item.Name); }); } else { - view.setAttribute("data-title", Globalize.translate("TabMovies")); - libraryMenu.setTitle(Globalize.translate("TabMovies")); + view.setAttribute('data-title', globalize.translate('TabMovies')); + libraryMenu.setTitle(globalize.translate('TabMovies')); } } - events.on(playbackManager, "playbackstop", onPlaybackStop); + events.on(playbackManager, 'playbackstop', onPlaybackStop); inputManager.on(window, onInputCommand); }); - view.addEventListener("viewbeforehide", function (e) { + view.addEventListener('viewbeforehide', function (e) { inputManager.off(window, onInputCommand); }); - view.addEventListener("viewdestroy", function (e) { + view.addEventListener('viewdestroy', function (e) { tabControllers.forEach(function (t) { if (t.destroy) { t.destroy(); diff --git a/src/controllers/movies/movietrailers.js b/src/controllers/movies/movietrailers.js index c764190ee0..99152275b9 100644 --- a/src/controllers/movies/movietrailers.js +++ b/src/controllers/movies/movietrailers.js @@ -1,5 +1,5 @@ -define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "alphaPicker", "listView", "cardBuilder", "apphost", "emby-itemscontainer"], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost) { - "use strict"; +define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, userSettings, globalize) { + 'use strict'; return function (view, params, tabContent) { function getPageData(context) { @@ -9,18 +9,22 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " if (!pageData) { pageData = data[key] = { query: { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Trailer", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Trailer', Recursive: true, - Fields: "PrimaryImageAspectRatio,SortName,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,SortName,BasicSyncInfo', ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", - StartIndex: 0, - Limit: pageSize + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', + StartIndex: 0 }, - view: libraryBrowser.getSavedView(key) || "Poster" + view: libraryBrowser.getSavedView(key) || 'Poster' }; + + if (userSettings.libraryPageSize() > 0) { + pageData.query['Limit'] = userSettings.libraryPageSize(); + } + libraryBrowser.loadSavedQueryValues(key, pageData.query); } @@ -33,7 +37,7 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " function getSavedQueryKey(context) { if (!context.savedQueryKey) { - context.savedQueryKey = libraryBrowser.getSavedQueryKey("trailers"); + context.savedQueryKey = libraryBrowser.getSavedQueryKey('trailers'); } return context.savedQueryKey; @@ -49,7 +53,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " return; } - query.StartIndex += query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } reloadItems(); } @@ -58,7 +64,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " return; } - query.StartIndex -= query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } reloadItems(); } @@ -77,43 +85,43 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " var html; var viewStyle = self.getCurrentViewStyle(); - if (viewStyle == "Thumb") { + if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, - context: "movies", + context: 'movies', overlayPlayButton: true }); - } else if (viewStyle == "ThumbCard") { + } else if (viewStyle == 'ThumbCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, - context: "movies", + context: 'movies', cardLayout: true, showTitle: true, showYear: true, centerText: true }); - } else if (viewStyle == "Banner") { + } else if (viewStyle == 'Banner') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "banner", + shape: 'banner', preferBanner: true, - context: "movies" + context: 'movies' }); - } else if (viewStyle == "List") { + } else if (viewStyle == 'List') { html = listView.getListViewHtml({ items: result.Items, - context: "movies", + context: 'movies', sortBy: query.SortBy }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "portrait", - context: "movies", + shape: 'portrait', + context: 'movies', showTitle: true, showYear: true, cardLayout: true, @@ -122,8 +130,8 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " } else { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "portrait", - context: "movies", + shape: 'portrait', + context: 'movies', centerText: true, overlayPlayButton: true, showTitle: true, @@ -133,27 +141,27 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " var i; var length; - var elems = tabContent.querySelectorAll(".paging"); + var elems = tabContent.querySelectorAll('.paging'); for (i = 0, length = elems.length; i < length; i++) { elems[i].innerHTML = pagingHtml; } - elems = tabContent.querySelectorAll(".btnNextPage"); + elems = tabContent.querySelectorAll('.btnNextPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onNextPageClick); + elems[i].addEventListener('click', onNextPageClick); } - elems = tabContent.querySelectorAll(".btnPreviousPage"); + elems = tabContent.querySelectorAll('.btnPreviousPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onPreviousPageClick); + elems[i].addEventListener('click', onPreviousPageClick); } if (!result.Items.length) { - html = '

' + Globalize.translate("MessageNoTrailersFound") + "

"; + html = '

' + globalize.translate('MessageNoTrailersFound') + '

'; } - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); libraryBrowser.saveQueryValues(getSavedQueryKey(tabContent), query); @@ -168,18 +176,17 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " } var self = this; - var pageSize = 100; var data = {}; var isLoading = false; self.showFilterMenu = function () { - require(["components/filterdialog/filterdialog"], function (filterDialogFactory) { + require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { var filterDialog = new filterDialogFactory({ query: getQuery(tabContent), - mode: "movies", + mode: 'movies', serverId: ApiClient.serverId() }); - events.on(filterDialog, "filterchange", function () { + events.on(filterDialog, 'filterchange', function () { getQuery(tabContent).StartIndex = 0; reloadItems(); }); @@ -192,9 +199,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " }; function initPage(tabContent) { - var alphaPickerElement = tabContent.querySelector(".alphaPicker"); - var itemsContainer = tabContent.querySelector(".itemsContainer"); - alphaPickerElement.addEventListener("alphavaluechanged", function (e) { + var alphaPickerElement = tabContent.querySelector('.alphaPicker'); + var itemsContainer = tabContent.querySelector('.itemsContainer'); + alphaPickerElement.addEventListener('alphavaluechanged', function (e) { var newValue = e.detail.value; var query = getQuery(tabContent); query.NameStartsWithOrGreater = newValue; @@ -203,39 +210,39 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " }); self.alphaPicker = new alphaPicker({ element: alphaPickerElement, - valueChangeEvent: "click" + valueChangeEvent: 'click' }); - tabContent.querySelector(".alphaPicker").classList.add("alphabetPicker-right"); - alphaPickerElement.classList.add("alphaPicker-fixed-right"); - itemsContainer.classList.add("padded-right-withalphapicker"); + tabContent.querySelector('.alphaPicker').classList.add('alphabetPicker-right'); + alphaPickerElement.classList.add('alphaPicker-fixed-right'); + itemsContainer.classList.add('padded-right-withalphapicker'); - tabContent.querySelector(".btnFilter").addEventListener("click", function () { + tabContent.querySelector('.btnFilter').addEventListener('click', function () { self.showFilterMenu(); }); - tabContent.querySelector(".btnSort").addEventListener("click", function (e) { + tabContent.querySelector('.btnSort').addEventListener('click', function (e) { libraryBrowser.showSortMenu({ items: [{ - name: Globalize.translate("OptionNameSort"), - id: "SortName" + name: globalize.translate('OptionNameSort'), + id: 'SortName' }, { - name: Globalize.translate("OptionImdbRating"), - id: "CommunityRating,SortName" + name: globalize.translate('OptionImdbRating'), + id: 'CommunityRating,SortName' }, { - name: Globalize.translate("OptionDateAdded"), - id: "DateCreated,SortName" + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName' }, { - name: Globalize.translate("OptionDatePlayed"), - id: "DatePlayed,SortName" + name: globalize.translate('OptionDatePlayed'), + id: 'DatePlayed,SortName' }, { - name: Globalize.translate("OptionParentalRating"), - id: "OfficialRating,SortName" + name: globalize.translate('OptionParentalRating'), + id: 'OfficialRating,SortName' }, { - name: Globalize.translate("OptionPlayCount"), - id: "PlayCount,SortName" + name: globalize.translate('OptionPlayCount'), + id: 'PlayCount,SortName' }, { - name: Globalize.translate("OptionReleaseDate"), - id: "PremiereDate,SortName" + name: globalize.translate('OptionReleaseDate'), + id: 'PremiereDate,SortName' }], callback: function () { getQuery(tabContent).StartIndex = 0; diff --git a/src/controllers/music/musicalbums.js b/src/controllers/music/musicalbums.js index 58d30e71da..645daf4ad9 100644 --- a/src/controllers/music/musicalbums.js +++ b/src/controllers/music/musicalbums.js @@ -1,5 +1,5 @@ -define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser", "imageLoader", "alphaPicker", "listView", "cardBuilder", "apphost", "emby-itemscontainer"], function (layoutManager, playbackManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost) { - "use strict"; +define(['layoutManager', 'playbackManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, playbackManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, userSettings, globalize) { + 'use strict'; return function (view, params, tabContent) { function playAll() { @@ -23,18 +23,22 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser if (!pageData) { pageData = { query: { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "MusicAlbum", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'MusicAlbum', Recursive: true, - Fields: "PrimaryImageAspectRatio,SortName,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,SortName,BasicSyncInfo', ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", - StartIndex: 0, - Limit: pageSize + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', + StartIndex: 0 }, - view: libraryBrowser.getSavedView(key) || "Poster" + view: libraryBrowser.getSavedView(key) || 'Poster' }; + + if (userSettings.libraryPageSize() > 0) { + pageData.query['Limit'] = userSettings.libraryPageSize(); + } + pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); } @@ -48,7 +52,7 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser function getSavedQueryKey() { if (!savedQueryKey) { - savedQueryKey = libraryBrowser.getSavedQueryKey("musicalbums"); + savedQueryKey = libraryBrowser.getSavedQueryKey('musicalbums'); } return savedQueryKey; @@ -56,17 +60,17 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser function onViewStyleChange() { var viewStyle = self.getCurrentViewStyle(); - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); - if ("List" == viewStyle) { - itemsContainer.classList.add("vertical-list"); - itemsContainer.classList.remove("vertical-wrap"); + if ('List' == viewStyle) { + itemsContainer.classList.add('vertical-list'); + itemsContainer.classList.remove('vertical-wrap'); } else { - itemsContainer.classList.remove("vertical-list"); - itemsContainer.classList.add("vertical-wrap"); + itemsContainer.classList.remove('vertical-list'); + itemsContainer.classList.add('vertical-wrap'); } - itemsContainer.innerHTML = ""; + itemsContainer.innerHTML = ''; } function reloadItems(page) { @@ -79,7 +83,9 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser return; } - query.StartIndex += query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } reloadItems(tabContent); } @@ -88,7 +94,9 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser return; } - query.StartIndex -= query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } reloadItems(tabContent); } @@ -106,18 +114,18 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser filterButton: false }); var viewStyle = self.getCurrentViewStyle(); - if (viewStyle == "List") { + if (viewStyle == 'List') { html = listView.getListViewHtml({ items: result.Items, - context: "music", + context: 'music', sortBy: query.SortBy, addToListButton: true }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "square", - context: "music", + shape: 'square', + context: 'music', showTitle: true, coverImage: true, showParentTitle: true, @@ -127,8 +135,8 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser } else { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "square", - context: "music", + shape: 'square', + context: 'music', showTitle: true, showParentTitle: true, lazy: true, @@ -138,30 +146,30 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser } var i; var length; - var elems = tabContent.querySelectorAll(".paging"); + var elems = tabContent.querySelectorAll('.paging'); for (i = 0, length = elems.length; i < length; i++) { elems[i].innerHTML = pagingHtml; } - elems = tabContent.querySelectorAll(".btnNextPage"); + elems = tabContent.querySelectorAll('.btnNextPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onNextPageClick); + elems[i].addEventListener('click', onNextPageClick); } - elems = tabContent.querySelectorAll(".btnPreviousPage"); + elems = tabContent.querySelectorAll('.btnPreviousPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onPreviousPageClick); + elems[i].addEventListener('click', onPreviousPageClick); } - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); libraryBrowser.saveQueryValues(getSavedQueryKey(), query); loading.hide(); isLoading = false; - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(tabContent); }); }); @@ -175,17 +183,16 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser var savedQueryKey; var pageData; var self = this; - var pageSize = 100; var isLoading = false; self.showFilterMenu = function () { - require(["components/filterdialog/filterdialog"], function (filterDialogFactory) { + require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { var filterDialog = new filterDialogFactory({ query: getQuery(), - mode: "albums", + mode: 'albums', serverId: ApiClient.serverId() }); - events.on(filterDialog, "filterchange", function () { + events.on(filterDialog, 'filterchange', function () { getQuery().StartIndex = 0; reloadItems(tabContent); }); @@ -198,10 +205,10 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser }; function initPage(tabContent) { - var alphaPickerElement = tabContent.querySelector(".alphaPicker"); - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var alphaPickerElement = tabContent.querySelector('.alphaPicker'); + var itemsContainer = tabContent.querySelector('.itemsContainer'); - alphaPickerElement.addEventListener("alphavaluechanged", function (e) { + alphaPickerElement.addEventListener('alphavaluechanged', function (e) { var newValue = e.detail.value; var query = getQuery(); query.NameStartsWithOrGreater = newValue; @@ -210,39 +217,39 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser }); self.alphaPicker = new alphaPicker({ element: alphaPickerElement, - valueChangeEvent: "click" + valueChangeEvent: 'click' }); - tabContent.querySelector(".alphaPicker").classList.add("alphabetPicker-right"); - alphaPickerElement.classList.add("alphaPicker-fixed-right"); - itemsContainer.classList.add("padded-right-withalphapicker"); + tabContent.querySelector('.alphaPicker').classList.add('alphabetPicker-right'); + alphaPickerElement.classList.add('alphaPicker-fixed-right'); + itemsContainer.classList.add('padded-right-withalphapicker'); - tabContent.querySelector(".btnFilter").addEventListener("click", function () { + tabContent.querySelector('.btnFilter').addEventListener('click', function () { self.showFilterMenu(); }); - tabContent.querySelector(".btnSort").addEventListener("click", function (e) { + tabContent.querySelector('.btnSort').addEventListener('click', function (e) { libraryBrowser.showSortMenu({ items: [{ - name: Globalize.translate("OptionNameSort"), - id: "SortName" + name: globalize.translate('OptionNameSort'), + id: 'SortName' }, { - name: Globalize.translate("OptionAlbumArtist"), - id: "AlbumArtist,SortName" + name: globalize.translate('OptionAlbumArtist'), + id: 'AlbumArtist,SortName' }, { - name: Globalize.translate("OptionCommunityRating"), - id: "CommunityRating,SortName" + name: globalize.translate('OptionCommunityRating'), + id: 'CommunityRating,SortName' }, { - name: Globalize.translate("OptionCriticRating"), - id: "CriticRating,SortName" + name: globalize.translate('OptionCriticRating'), + id: 'CriticRating,SortName' }, { - name: Globalize.translate("OptionDateAdded"), - id: "DateCreated,SortName" + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName' }, { - name: Globalize.translate("OptionReleaseDate"), - id: "ProductionYear,PremiereDate,SortName" + name: globalize.translate('OptionReleaseDate'), + id: 'ProductionYear,PremiereDate,SortName' }, { - name: Globalize.translate("OptionRandom"), - id: "Random,SortName" + name: globalize.translate('OptionRandom'), + id: 'Random,SortName' }], callback: function () { getQuery().StartIndex = 0; @@ -252,11 +259,11 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser button: e.target }); }); - var btnSelectView = tabContent.querySelector(".btnSelectView"); - btnSelectView.addEventListener("click", function (e) { - libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), "List,Poster,PosterCard".split(",")); + var btnSelectView = tabContent.querySelector('.btnSelectView'); + btnSelectView.addEventListener('click', function (e) { + libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'List,Poster,PosterCard'.split(',')); }); - btnSelectView.addEventListener("layoutchange", function (e) { + btnSelectView.addEventListener('layoutchange', function (e) { var viewStyle = e.detail.viewStyle; getPageData().view = viewStyle; libraryBrowser.saveViewSetting(getSavedQueryKey(), viewStyle); @@ -264,8 +271,8 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser onViewStyleChange(); reloadItems(tabContent); }); - tabContent.querySelector(".btnPlayAll").addEventListener("click", playAll); - tabContent.querySelector(".btnShuffle").addEventListener("click", shuffle); + tabContent.querySelector('.btnPlayAll').addEventListener('click', playAll); + tabContent.querySelector('.btnShuffle').addEventListener('click', shuffle); } initPage(tabContent); diff --git a/src/controllers/music/musicartists.js b/src/controllers/music/musicartists.js index ceed448a06..7a889ff8b9 100644 --- a/src/controllers/music/musicartists.js +++ b/src/controllers/music/musicartists.js @@ -1,5 +1,5 @@ -define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "alphaPicker", "listView", "cardBuilder", "apphost", "emby-itemscontainer"], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost) { - "use strict"; +define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'apphost', 'userSettings', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost, userSettings) { + 'use strict'; return function (view, params, tabContent) { function getPageData(context) { @@ -7,18 +7,23 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " var pageData = data[key]; if (!pageData) { + var queryValues = { + SortBy: 'SortName', + SortOrder: 'Ascending', + Recursive: true, + Fields: 'PrimaryImageAspectRatio,SortName,BasicSyncInfo', + StartIndex: 0, + ImageTypeLimit: 1, + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb' + }; + + if (userSettings.libraryPageSize() > 0) { + queryValues['Limit'] = userSettings.libraryPageSize(); + } + pageData = data[key] = { - query: { - SortBy: "SortName", - SortOrder: "Ascending", - Recursive: true, - Fields: "PrimaryImageAspectRatio,SortName,BasicSyncInfo", - StartIndex: 0, - ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", - Limit: 100 - }, - view: libraryBrowser.getSavedView(key) || "Poster" + query: queryValues, + view: libraryBrowser.getSavedView(key) || 'Poster' }; pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); @@ -41,17 +46,17 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " function onViewStyleChange() { var viewStyle = self.getCurrentViewStyle(); - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); - if ("List" == viewStyle) { - itemsContainer.classList.add("vertical-list"); - itemsContainer.classList.remove("vertical-wrap"); + if ('List' == viewStyle) { + itemsContainer.classList.add('vertical-list'); + itemsContainer.classList.remove('vertical-wrap'); } else { - itemsContainer.classList.remove("vertical-list"); - itemsContainer.classList.add("vertical-wrap"); + itemsContainer.classList.remove('vertical-list'); + itemsContainer.classList.add('vertical-wrap'); } - itemsContainer.innerHTML = ""; + itemsContainer.innerHTML = ''; } function reloadItems(page) { @@ -67,7 +72,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " return; } - query.StartIndex += query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } reloadItems(tabContent); } @@ -76,7 +83,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " return; } - query.StartIndex -= query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } reloadItems(tabContent); } @@ -94,16 +103,16 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " filterButton: false }); var viewStyle = self.getCurrentViewStyle(); - if (viewStyle == "List") { + if (viewStyle == 'List') { html = listView.getListViewHtml({ items: result.Items, sortBy: query.SortBy }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "square", - context: "music", + shape: 'square', + context: 'music', showTitle: true, coverImage: true, cardLayout: true @@ -111,8 +120,8 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " } else { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "square", - context: "music", + shape: 'square', + context: 'music', showTitle: true, coverImage: true, lazy: true, @@ -122,30 +131,30 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " } var i; var length; - var elems = tabContent.querySelectorAll(".paging"); + var elems = tabContent.querySelectorAll('.paging'); for (i = 0, length = elems.length; i < length; i++) { elems[i].innerHTML = pagingHtml; } - elems = tabContent.querySelectorAll(".btnNextPage"); + elems = tabContent.querySelectorAll('.btnNextPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onNextPageClick); + elems[i].addEventListener('click', onNextPageClick); } - elems = tabContent.querySelectorAll(".btnPreviousPage"); + elems = tabContent.querySelectorAll('.btnPreviousPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onPreviousPageClick); + elems[i].addEventListener('click', onPreviousPageClick); } - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); libraryBrowser.saveQueryValues(getSavedQueryKey(page), query); loading.hide(); isLoading = false; - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(tabContent); }); }); @@ -161,13 +170,13 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " var isLoading = false; self.showFilterMenu = function () { - require(["components/filterdialog/filterdialog"], function (filterDialogFactory) { + require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { var filterDialog = new filterDialogFactory({ query: getQuery(tabContent), mode: self.mode, serverId: ApiClient.serverId() }); - events.on(filterDialog, "filterchange", function () { + events.on(filterDialog, 'filterchange', function () { getQuery(tabContent).StartIndex = 0; reloadItems(tabContent); }); @@ -180,10 +189,10 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " }; function initPage(tabContent) { - var alphaPickerElement = tabContent.querySelector(".alphaPicker"); - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var alphaPickerElement = tabContent.querySelector('.alphaPicker'); + var itemsContainer = tabContent.querySelector('.itemsContainer'); - alphaPickerElement.addEventListener("alphavaluechanged", function (e) { + alphaPickerElement.addEventListener('alphavaluechanged', function (e) { var newValue = e.detail.value; var query = getQuery(tabContent); query.NameStartsWithOrGreater = newValue; @@ -192,21 +201,21 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " }); self.alphaPicker = new alphaPicker({ element: alphaPickerElement, - valueChangeEvent: "click" + valueChangeEvent: 'click' }); - tabContent.querySelector(".alphaPicker").classList.add("alphabetPicker-right"); - alphaPickerElement.classList.add("alphaPicker-fixed-right"); - itemsContainer.classList.add("padded-right-withalphapicker"); + tabContent.querySelector('.alphaPicker').classList.add('alphabetPicker-right'); + alphaPickerElement.classList.add('alphaPicker-fixed-right'); + itemsContainer.classList.add('padded-right-withalphapicker'); - tabContent.querySelector(".btnFilter").addEventListener("click", function () { + tabContent.querySelector('.btnFilter').addEventListener('click', function () { self.showFilterMenu(); }); - var btnSelectView = tabContent.querySelector(".btnSelectView"); - btnSelectView.addEventListener("click", function (e) { - libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), "List,Poster,PosterCard".split(",")); + var btnSelectView = tabContent.querySelector('.btnSelectView'); + btnSelectView.addEventListener('click', function (e) { + libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'List,Poster,PosterCard'.split(',')); }); - btnSelectView.addEventListener("layoutchange", function (e) { + btnSelectView.addEventListener('layoutchange', function (e) { var viewStyle = e.detail.viewStyle; getPageData(tabContent).view = viewStyle; libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle); diff --git a/src/controllers/music/musicgenres.js b/src/controllers/music/musicgenres.js index b465a4d350..82f2eba574 100644 --- a/src/controllers/music/musicgenres.js +++ b/src/controllers/music/musicgenres.js @@ -1,5 +1,5 @@ -define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], function (libraryBrowser, cardBuilder, appHost, imageLoader, loading) { - "use strict"; +define(['libraryBrowser', 'cardBuilder', 'apphost', 'imageLoader', 'loading'], function (libraryBrowser, cardBuilder, appHost, imageLoader, loading) { + 'use strict'; return function (view, params, tabContent) { function getPageData() { @@ -9,13 +9,13 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f if (!pageData) { pageData = data[key] = { query: { - SortBy: "SortName", - SortOrder: "Ascending", + SortBy: 'SortName', + SortOrder: 'Ascending', Recursive: true, - Fields: "PrimaryImageAspectRatio,ItemCounts", + Fields: 'PrimaryImageAspectRatio,ItemCounts', StartIndex: 0 }, - view: libraryBrowser.getSavedView(key) || "Poster" + view: libraryBrowser.getSavedView(key) || 'Poster' }; pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); @@ -29,7 +29,7 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f } function getSavedQueryKey() { - return libraryBrowser.getSavedQueryKey("genres"); + return libraryBrowser.getSavedQueryKey('genres'); } function getPromise() { @@ -41,40 +41,40 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f function reloadItems(context, promise) { var query = getQuery(); promise.then(function (result) { - var html = ""; + var html = ''; var viewStyle = self.getCurrentViewStyle(); - if (viewStyle == "Thumb") { + if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, context: 'music', centerText: true, overlayMoreButton: true, showTitle: true }); - } else if (viewStyle == "ThumbCard") { + } else if (viewStyle == 'ThumbCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, context: 'music', cardLayout: true, showTitle: true }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "auto", + shape: 'auto', context: 'music', cardLayout: true, showTitle: true }); - } else if (viewStyle == "Poster") { + } else if (viewStyle == 'Poster') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "auto", + shape: 'auto', context: 'music', centerText: true, overlayMoreButton: true, @@ -82,13 +82,13 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f }); } - var elem = context.querySelector("#items"); + var elem = context.querySelector('#items'); elem.innerHTML = html; imageLoader.lazyChildren(elem); libraryBrowser.saveQueryValues(getSavedQueryKey(), query); loading.hide(); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(context); }); }); @@ -103,16 +103,16 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f var data = {}; self.getViewStyles = function () { - return "Poster,PosterCard,Thumb,ThumbCard".split(","); + return 'Poster,PosterCard,Thumb,ThumbCard'.split(','); }; self.getCurrentViewStyle = function () { - return getPageData(tabContent).view; + return getPageData().view; }; self.setCurrentViewStyle = function (viewStyle) { - getPageData(tabContent).view = viewStyle; - libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle); + getPageData().view = viewStyle; + libraryBrowser.saveViewSetting(getSavedQueryKey(), viewStyle); fullyReload(); }; diff --git a/src/controllers/music/musicplaylists.js b/src/controllers/music/musicplaylists.js index fd458c88ac..f508489216 100644 --- a/src/controllers/music/musicplaylists.js +++ b/src/controllers/music/musicplaylists.js @@ -1,5 +1,5 @@ -define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], function (libraryBrowser, cardBuilder, appHost, imageLoader, loading) { - "use strict"; +define(['libraryBrowser', 'cardBuilder', 'apphost', 'imageLoader', 'loading'], function (libraryBrowser, cardBuilder, appHost, imageLoader, loading) { + 'use strict'; return function (view, params, tabContent) { function getPageData() { @@ -9,14 +9,14 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f if (!pageData) { pageData = data[key] = { query: { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Playlist", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Playlist', Recursive: true, - Fields: "PrimaryImageAspectRatio,SortName,CanDelete", + Fields: 'PrimaryImageAspectRatio,SortName,CanDelete', StartIndex: 0 }, - view: libraryBrowser.getSavedView(key) || "Poster" + view: libraryBrowser.getSavedView(key) || 'Poster' }; pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); @@ -30,7 +30,7 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f } function getSavedQueryKey() { - return libraryBrowser.getSavedQueryKey("genres"); + return libraryBrowser.getSavedQueryKey('genres'); } function getPromise() { @@ -42,10 +42,10 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f function reloadItems(context, promise) { var query = getQuery(); promise.then(function (result) { - var html = ""; + var html = ''; html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "square", + shape: 'square', showTitle: true, coverImage: true, centerText: true, @@ -53,13 +53,13 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f allowBottomPadding: true, cardLayout: false }); - var elem = context.querySelector("#items"); + var elem = context.querySelector('#items'); elem.innerHTML = html; imageLoader.lazyChildren(elem); libraryBrowser.saveQueryValues(getSavedQueryKey(), query); loading.hide(); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(context); }); }); @@ -69,7 +69,7 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f var data = {}; self.getCurrentViewStyle = function () { - return getPageData(tabContent).view; + return getPageData().view; }; var promise; diff --git a/src/controllers/music/musicrecommended.js b/src/controllers/music/musicrecommended.js index 8b87dff265..3f025799f6 100644 --- a/src/controllers/music/musicrecommended.js +++ b/src/controllers/music/musicrecommended.js @@ -1,5 +1,5 @@ -define(["browser", "layoutManager", "userSettings", "inputManager", "loading", "cardBuilder", "dom", "apphost", "imageLoader", "libraryMenu", "playbackManager", "mainTabsManager", "scrollStyles", "emby-itemscontainer", "emby-tabs", "emby-button", "flexStyles"], function (browser, layoutManager, userSettings, inputManager, loading, cardBuilder, dom, appHost, imageLoader, libraryMenu, playbackManager, mainTabsManager) { - "use strict"; +define(['browser', 'layoutManager', 'userSettings', 'inputManager', 'loading', 'cardBuilder', 'dom', 'apphost', 'imageLoader', 'libraryMenu', 'playbackManager', 'mainTabsManager', 'globalize', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button', 'flexStyles'], function (browser, layoutManager, userSettings, inputManager, loading, cardBuilder, dom, appHost, imageLoader, libraryMenu, playbackManager, mainTabsManager, globalize) { + 'use strict'; function itemsPerRow() { var screenWidth = dom.getWindowSize().innerWidth; @@ -24,24 +24,24 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " } function getSquareShape() { - return enableScrollX() ? "overflowSquare" : "square"; + return enableScrollX() ? 'overflowSquare' : 'square'; } function loadLatest(page, parentId) { loading.show(); var userId = ApiClient.getCurrentUserId(); var options = { - IncludeItemTypes: "Audio", + IncludeItemTypes: 'Audio', Limit: enableScrollX() ? 3 * itemsPerRow() : 2 * itemsPerRow(), - Fields: "PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo', ParentId: parentId, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', EnableTotalRecordCount: false }; - ApiClient.getJSON(ApiClient.getUrl("Users/" + userId + "/Items/Latest", options)).then(function (items) { - var elem = page.querySelector("#recentlyAddedSongs"); - var supportsImageAnalysis = appHost.supports("imageanalysis"); + ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) { + var elem = page.querySelector('#recentlyAddedSongs'); + var supportsImageAnalysis = appHost.supports('imageanalysis'); supportsImageAnalysis = false; elem.innerHTML = cardBuilder.getCardsHtml({ items: items, @@ -60,7 +60,7 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " imageLoader.lazyChildren(elem); loading.hide(); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); }); @@ -68,29 +68,29 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " function loadRecentlyPlayed(page, parentId) { var options = { - SortBy: "DatePlayed", - SortOrder: "Descending", - IncludeItemTypes: "Audio", + SortBy: 'DatePlayed', + SortOrder: 'Descending', + IncludeItemTypes: 'Audio', Limit: itemsPerRow(), Recursive: true, - Fields: "PrimaryImageAspectRatio,AudioInfo", - Filters: "IsPlayed", + Fields: 'PrimaryImageAspectRatio,AudioInfo', + Filters: 'IsPlayed', ParentId: parentId, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', EnableTotalRecordCount: false }; ApiClient.getItems(ApiClient.getCurrentUserId(), options).then(function (result) { - var elem = page.querySelector("#recentlyPlayed"); + var elem = page.querySelector('#recentlyPlayed'); if (result.Items.length) { - elem.classList.remove("hide"); + elem.classList.remove('hide'); } else { - elem.classList.add("hide"); + elem.classList.add('hide'); } - var itemsContainer = elem.querySelector(".itemsContainer"); - var supportsImageAnalysis = appHost.supports("imageanalysis"); + var itemsContainer = elem.querySelector('.itemsContainer'); + var supportsImageAnalysis = appHost.supports('imageanalysis'); supportsImageAnalysis = false; itemsContainer.innerHTML = cardBuilder.getCardsHtml({ items: result.Items, @@ -98,7 +98,7 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " shape: getSquareShape(), showTitle: true, showParentTitle: true, - action: "instantmix", + action: 'instantmix', lazy: true, centerText: !supportsImageAnalysis, overlayMoreButton: !supportsImageAnalysis, @@ -112,29 +112,29 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " function loadFrequentlyPlayed(page, parentId) { var options = { - SortBy: "PlayCount", - SortOrder: "Descending", - IncludeItemTypes: "Audio", + SortBy: 'PlayCount', + SortOrder: 'Descending', + IncludeItemTypes: 'Audio', Limit: itemsPerRow(), Recursive: true, - Fields: "PrimaryImageAspectRatio,AudioInfo", - Filters: "IsPlayed", + Fields: 'PrimaryImageAspectRatio,AudioInfo', + Filters: 'IsPlayed', ParentId: parentId, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', EnableTotalRecordCount: false }; ApiClient.getItems(ApiClient.getCurrentUserId(), options).then(function (result) { - var elem = page.querySelector("#topPlayed"); + var elem = page.querySelector('#topPlayed'); if (result.Items.length) { - elem.classList.remove("hide"); + elem.classList.remove('hide'); } else { - elem.classList.add("hide"); + elem.classList.add('hide'); } - var itemsContainer = elem.querySelector(".itemsContainer"); - var supportsImageAnalysis = appHost.supports("imageanalysis"); + var itemsContainer = elem.querySelector('.itemsContainer'); + var supportsImageAnalysis = appHost.supports('imageanalysis'); supportsImageAnalysis = false; itemsContainer.innerHTML = cardBuilder.getCardsHtml({ items: result.Items, @@ -142,7 +142,7 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " shape: getSquareShape(), showTitle: true, showParentTitle: true, - action: "instantmix", + action: 'instantmix', lazy: true, centerText: !supportsImageAnalysis, overlayMoreButton: !supportsImageAnalysis, @@ -155,55 +155,55 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " } function loadSuggestionsTab(page, tabContent, parentId) { - console.debug("loadSuggestionsTab"); + console.debug('loadSuggestionsTab'); loadLatest(tabContent, parentId); loadRecentlyPlayed(tabContent, parentId); loadFrequentlyPlayed(tabContent, parentId); - require(["components/favoriteitems"], function (favoriteItems) { - favoriteItems.render(tabContent, ApiClient.getCurrentUserId(), parentId, ["favoriteArtists", "favoriteAlbums", "favoriteSongs"]); + require(['components/favoriteitems'], function (favoriteItems) { + favoriteItems.render(tabContent, ApiClient.getCurrentUserId(), parentId, ['favoriteArtists', 'favoriteAlbums', 'favoriteSongs']); }); } function getTabs() { return [{ - name: Globalize.translate("TabSuggestions") + name: globalize.translate('TabSuggestions') }, { - name: Globalize.translate("TabAlbums") + name: globalize.translate('TabAlbums') }, { - name: Globalize.translate("TabAlbumArtists") + name: globalize.translate('TabAlbumArtists') }, { - name: Globalize.translate("TabArtists") + name: globalize.translate('TabArtists') }, { - name: Globalize.translate("TabPlaylists") + name: globalize.translate('TabPlaylists') }, { - name: Globalize.translate("TabSongs") + name: globalize.translate('TabSongs') }, { - name: Globalize.translate("TabGenres") + name: globalize.translate('TabGenres') }, { - name: Globalize.translate("ButtonSearch"), - cssClass: "searchTabButton" + name: globalize.translate('ButtonSearch'), + cssClass: 'searchTabButton' }]; } function getDefaultTabIndex(folderId) { - switch (userSettings.get("landing-" + folderId)) { - case "albums": + switch (userSettings.get('landing-' + folderId)) { + case 'albums': return 1; - case "albumartists": + case 'albumartists': return 2; - case "artists": + case 'artists': return 3; - case "playlists": + case 'playlists': return 4; - case "songs": + case 'songs': return 5; - case "genres": + case 'genres': return 6; default: @@ -224,19 +224,19 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " function setScrollClasses(elem, scrollX) { if (scrollX) { - elem.classList.add("hiddenScrollX"); + elem.classList.add('hiddenScrollX'); if (layoutManager.tv) { - elem.classList.add("smoothScrollX"); + elem.classList.add('smoothScrollX'); } - elem.classList.add("scrollX"); - elem.classList.remove("vertical-wrap"); + elem.classList.add('scrollX'); + elem.classList.remove('vertical-wrap'); } else { - elem.classList.remove("hiddenScrollX"); - elem.classList.remove("smoothScrollX"); - elem.classList.remove("scrollX"); - elem.classList.add("vertical-wrap"); + elem.classList.remove('hiddenScrollX'); + elem.classList.remove('smoothScrollX'); + elem.classList.remove('scrollX'); + elem.classList.add('vertical-wrap'); } } @@ -249,7 +249,7 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " } function getTabContainers() { - return view.querySelectorAll(".pageTabContent"); + return view.querySelectorAll('.pageTabContent'); } function initTabs() { @@ -264,28 +264,28 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " break; case 1: - depends.push("controllers/music/musicalbums"); + depends.push('controllers/music/musicalbums'); break; case 2: case 3: - depends.push("controllers/music/musicartists"); + depends.push('controllers/music/musicartists'); break; case 4: - depends.push("controllers/music/musicplaylists"); + depends.push('controllers/music/musicplaylists'); break; case 5: - depends.push("controllers/music/songs"); + depends.push('controllers/music/songs'); break; case 6: - depends.push("controllers/music/musicgenres"); + depends.push('controllers/music/musicgenres'); break; case 7: - depends.push("scripts/searchtab"); + depends.push('scripts/searchtab'); } require(depends, function (controllerFactory) { @@ -305,7 +305,7 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " controller = self; } else if (index === 7) { controller = new controllerFactory(view, tabContent, { - collectionType: "music", + collectionType: 'music', parentId: params.topParentId }); } else { @@ -313,9 +313,9 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " } if (index == 2) { - controller.mode = "albumartists"; + controller.mode = 'albumartists'; } else if (index == 3) { - controller.mode = "artists"; + controller.mode = 'artists'; } tabControllers[index] = controller; @@ -350,9 +350,9 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " function onInputCommand(e) { switch (e.detail.command) { - case "search": + case 'search': e.preventDefault(); - Dashboard.navigate("search.html?collectionType=music&parentId=" + params.topParentId); + Dashboard.navigate('search.html?collectionType=music&parentId=' + params.topParentId); } } @@ -363,7 +363,7 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " self.initTab = function () { var tabContent = view.querySelector(".pageTabContent[data-index='0']"); - var containers = tabContent.querySelectorAll(".itemsContainer"); + var containers = tabContent.querySelectorAll('.itemsContainer'); for (var i = 0, length = containers.length; i < length; i++) { setScrollClasses(containers[i], enableScrollX()); @@ -376,29 +376,29 @@ define(["browser", "layoutManager", "userSettings", "inputManager", "loading", " var tabControllers = []; var renderedTabs = []; - view.addEventListener("viewshow", function (e) { + view.addEventListener('viewshow', function (e) { isViewRestored = e.detail.isRestored; initTabs(); - if (!view.getAttribute("data-title")) { + if (!view.getAttribute('data-title')) { var parentId = params.topParentId; if (parentId) { ApiClient.getItem(ApiClient.getCurrentUserId(), parentId).then(function (item) { - view.setAttribute("data-title", item.Name); + view.setAttribute('data-title', item.Name); libraryMenu.setTitle(item.Name); }); } else { - view.setAttribute("data-title", Globalize.translate("TabMusic")); - libraryMenu.setTitle(Globalize.translate("TabMusic")); + view.setAttribute('data-title', globalize.translate('TabMusic')); + libraryMenu.setTitle(globalize.translate('TabMusic')); } } inputManager.on(window, onInputCommand); }); - view.addEventListener("viewbeforehide", function (e) { + view.addEventListener('viewbeforehide', function (e) { inputManager.off(window, onInputCommand); }); - view.addEventListener("viewdestroy", function (e) { + view.addEventListener('viewdestroy', function (e) { tabControllers.forEach(function (t) { if (t.destroy) { t.destroy(); diff --git a/src/controllers/music/songs.js b/src/controllers/music/songs.js index 47263be0d2..aa63ec51fe 100644 --- a/src/controllers/music/songs.js +++ b/src/controllers/music/songs.js @@ -1,5 +1,5 @@ -define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby-itemscontainer"], function (events, libraryBrowser, imageLoader, listView, loading) { - "use strict"; +define(['events', 'libraryBrowser', 'imageLoader', 'listView', 'loading', 'userSettings', 'globalize', 'emby-itemscontainer'], function (events, libraryBrowser, imageLoader, listView, loading, userSettings, globalize) { + 'use strict'; return function (view, params, tabContent) { function getPageData(context) { @@ -9,17 +9,21 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby- if (!pageData) { pageData = data[key] = { query: { - SortBy: "Album,SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Audio", + SortBy: 'Album,SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Audio', Recursive: true, - Fields: "AudioInfo,ParentId", - Limit: 100, + Fields: 'AudioInfo,ParentId', StartIndex: 0, ImageTypeLimit: 1, - EnableImageTypes: "Primary" + EnableImageTypes: 'Primary' } }; + + if (userSettings.libraryPageSize() > 0) { + pageData.query['Limit'] = userSettings.libraryPageSize(); + } + pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); } @@ -33,7 +37,7 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby- function getSavedQueryKey(context) { if (!context.savedQueryKey) { - context.savedQueryKey = libraryBrowser.getSavedQueryKey("songs"); + context.savedQueryKey = libraryBrowser.getSavedQueryKey('songs'); } return context.savedQueryKey; @@ -49,7 +53,9 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby- return; } - query.StartIndex += query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } reloadItems(tabContent); } @@ -58,7 +64,9 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby- return; } - query.StartIndex -= query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } reloadItems(tabContent); } @@ -77,35 +85,35 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby- }); var html = listView.getListViewHtml({ items: result.Items, - action: "playallfromhere", + action: 'playallfromhere', smallIcon: true, artist: true, addToListButton: true }); - var elems = tabContent.querySelectorAll(".paging"); + var elems = tabContent.querySelectorAll('.paging'); for (i = 0, length = elems.length; i < length; i++) { elems[i].innerHTML = pagingHtml; } - elems = tabContent.querySelectorAll(".btnNextPage"); + elems = tabContent.querySelectorAll('.btnNextPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onNextPageClick); + elems[i].addEventListener('click', onNextPageClick); } - elems = tabContent.querySelectorAll(".btnPreviousPage"); + elems = tabContent.querySelectorAll('.btnPreviousPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onPreviousPageClick); + elems[i].addEventListener('click', onPreviousPageClick); } - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); libraryBrowser.saveQueryValues(getSavedQueryKey(page), query); loading.hide(); isLoading = false; - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); }); @@ -116,13 +124,13 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby- var isLoading = false; self.showFilterMenu = function () { - require(["components/filterdialog/filterdialog"], function (filterDialogFactory) { + require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { var filterDialog = new filterDialogFactory({ query: getQuery(tabContent), - mode: "songs", + mode: 'songs', serverId: ApiClient.serverId() }); - events.on(filterDialog, "filterchange", function () { + events.on(filterDialog, 'filterchange', function () { getQuery(tabContent).StartIndex = 0; reloadItems(tabContent); }); @@ -135,38 +143,38 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby- }; function initPage(tabContent) { - tabContent.querySelector(".btnFilter").addEventListener("click", function () { + tabContent.querySelector('.btnFilter').addEventListener('click', function () { self.showFilterMenu(); }); - tabContent.querySelector(".btnSort").addEventListener("click", function (e) { + tabContent.querySelector('.btnSort').addEventListener('click', function (e) { libraryBrowser.showSortMenu({ items: [{ - name: Globalize.translate("OptionTrackName"), - id: "Name" + name: globalize.translate('OptionTrackName'), + id: 'Name' }, { - name: Globalize.translate("OptionAlbum"), - id: "Album,SortName" + name: globalize.translate('OptionAlbum'), + id: 'Album,SortName' }, { - name: Globalize.translate("OptionAlbumArtist"), - id: "AlbumArtist,Album,SortName" + name: globalize.translate('OptionAlbumArtist'), + id: 'AlbumArtist,Album,SortName' }, { - name: Globalize.translate("OptionArtist"), - id: "Artist,Album,SortName" + name: globalize.translate('OptionArtist'), + id: 'Artist,Album,SortName' }, { - name: Globalize.translate("OptionDateAdded"), - id: "DateCreated,SortName" + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName' }, { - name: Globalize.translate("OptionDatePlayed"), - id: "DatePlayed,SortName" + name: globalize.translate('OptionDatePlayed'), + id: 'DatePlayed,SortName' }, { - name: Globalize.translate("OptionPlayCount"), - id: "PlayCount,SortName" + name: globalize.translate('OptionPlayCount'), + id: 'PlayCount,SortName' }, { - name: Globalize.translate("OptionReleaseDate"), - id: "PremiereDate,AlbumArtist,Album,SortName" + name: globalize.translate('OptionReleaseDate'), + id: 'PremiereDate,AlbumArtist,Album,SortName' }, { - name: Globalize.translate("OptionRuntime"), - id: "Runtime,AlbumArtist,Album,SortName" + name: globalize.translate('OptionRuntime'), + id: 'Runtime,AlbumArtist,Album,SortName' }], callback: function () { getQuery(tabContent).StartIndex = 0; diff --git a/src/controllers/playback/nowplaying.js b/src/controllers/playback/nowplaying.js index 6fcdb2a79c..98c9945e84 100644 --- a/src/controllers/playback/nowplaying.js +++ b/src/controllers/playback/nowplaying.js @@ -1,17 +1,17 @@ -define(["components/remotecontrol/remotecontrol", "libraryMenu", "emby-button"], function (remotecontrolFactory, libraryMenu) { - "use strict"; +define(['components/remotecontrol/remotecontrol', 'libraryMenu', 'emby-button'], function (remotecontrolFactory, libraryMenu) { + 'use strict'; return function (view, params) { var remoteControl = new remotecontrolFactory(); - remoteControl.init(view, view.querySelector(".remoteControlContent")); - view.addEventListener("viewshow", function (e) { + remoteControl.init(view, view.querySelector('.remoteControlContent')); + view.addEventListener('viewshow', function (e) { libraryMenu.setTransparentMenu(true); if (remoteControl) { remoteControl.onShow(); } }); - view.addEventListener("viewbeforehide", function (e) { + view.addEventListener('viewbeforehide', function (e) { libraryMenu.setTransparentMenu(false); if (remoteControl) { diff --git a/src/controllers/playback/videoosd.js b/src/controllers/playback/videoosd.js index c61fd14a7a..d6caf24e81 100644 --- a/src/controllers/playback/videoosd.js +++ b/src/controllers/playback/videoosd.js @@ -1,19 +1,19 @@ -define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "mediaInfo", "focusManager", "imageLoader", "scrollHelper", "events", "connectionManager", "browser", "globalize", "apphost", "layoutManager", "userSettings", "keyboardnavigation", "scrollStyles", "emby-slider", "paper-icon-button-light", "css!assets/css/videoosd"], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost, layoutManager, userSettings, keyboardnavigation) { - "use strict"; +define(['playbackManager', 'dom', 'inputManager', 'datetime', 'itemHelper', 'mediaInfo', 'focusManager', 'imageLoader', 'scrollHelper', 'events', 'connectionManager', 'browser', 'globalize', 'apphost', 'layoutManager', 'userSettings', 'keyboardnavigation', 'scrollStyles', 'emby-slider', 'paper-icon-button-light', 'css!assets/css/videoosd'], function (playbackManager, dom, inputManager, datetime, itemHelper, mediaInfo, focusManager, imageLoader, scrollHelper, events, connectionManager, browser, globalize, appHost, layoutManager, userSettings, keyboardnavigation) { + 'use strict'; function seriesImageUrl(item, options) { - if ("Episode" !== item.Type) { + if ('Episode' !== item.Type) { return null; } options = options || {}; - options.type = options.type || "Primary"; - if ("Primary" === options.type && item.SeriesPrimaryImageTag) { + options.type = options.type || 'Primary'; + if ('Primary' === options.type && item.SeriesPrimaryImageTag) { options.tag = item.SeriesPrimaryImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); } - if ("Thumb" === options.type) { + if ('Thumb' === options.type) { if (item.SeriesThumbImageTag) { options.tag = item.SeriesThumbImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); @@ -30,14 +30,14 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med function imageUrl(item, options) { options = options || {}; - options.type = options.type || "Primary"; + options.type = options.type || 'Primary'; if (item.ImageTags && item.ImageTags[options.type]) { options.tag = item.ImageTags[options.type]; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options); } - if ("Primary" === options.type && item.AlbumId && item.AlbumPrimaryImageTag) { + if ('Primary' === options.type && item.AlbumId && item.AlbumPrimaryImageTag) { options.tag = item.AlbumPrimaryImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); } @@ -45,23 +45,6 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med return null; } - function logoImageUrl(item, apiClient, options) { - options = options || {}; - options.type = "Logo"; - - if (item.ImageTags && item.ImageTags.Logo) { - options.tag = item.ImageTags.Logo; - return apiClient.getScaledImageUrl(item.Id, options); - } - - if (item.ParentLogoImageTag) { - options.tag = item.ParentLogoImageTag; - return apiClient.getScaledImageUrl(item.ParentLogoItemId, options); - } - - return null; - } - return function (view, params) { function onVerticalSwipe(e, elem, data) { var player = currentPlayer; @@ -110,7 +93,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function getDisplayItem(item) { - if ("TvChannel" === item.Type) { + if ('TvChannel' === item.Type) { var apiClient = connectionManager.getApiClient(item.ServerId); return apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (refreshedItem) { return { @@ -126,27 +109,27 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function updateRecordingButton(item) { - if (!item || "Program" !== item.Type) { + if (!item || 'Program' !== item.Type) { if (recordingButtonManager) { recordingButtonManager.destroy(); recordingButtonManager = null; } - return void view.querySelector(".btnRecord").classList.add("hide"); + return void view.querySelector('.btnRecord').classList.add('hide'); } connectionManager.getApiClient(item.ServerId).getCurrentUser().then(function (user) { if (user.Policy.EnableLiveTvManagement) { - require(["recordingButton"], function (RecordingButton) { + require(['recordingButton'], function (RecordingButton) { if (recordingButtonManager) { return void recordingButtonManager.refreshItem(item); } recordingButtonManager = new RecordingButton({ item: item, - button: view.querySelector(".btnRecord") + button: view.querySelector('.btnRecord') }); - view.querySelector(".btnRecord").classList.remove("hide"); + view.querySelector('.btnRecord').classList.remove('hide'); }); } }); @@ -166,23 +149,23 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med setTitle(displayItem, parentName); var titleElement; - var osdTitle = view.querySelector(".osdTitle"); + var osdTitle = view.querySelector('.osdTitle'); titleElement = osdTitle; var displayName = itemHelper.getDisplayName(displayItem, { - includeParentInfo: "Program" !== displayItem.Type, - includeIndexNumber: "Program" !== displayItem.Type + includeParentInfo: 'Program' !== displayItem.Type, + includeIndexNumber: 'Program' !== displayItem.Type }); if (!displayName) { - displayItem.Type; + displayName = displayItem.Type; } titleElement.innerHTML = displayName; if (displayName) { - titleElement.classList.remove("hide"); + titleElement.classList.remove('hide'); } else { - titleElement.classList.add("hide"); + titleElement.classList.add('hide'); } var mediaInfoHtml = mediaInfo.getPrimaryMediaInfoHtml(displayItem, { @@ -191,20 +174,20 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med tomatoes: false, endsAt: false, episodeTitle: false, - originalAirDate: "Program" !== displayItem.Type, - episodeTitleIndexNumber: "Program" !== displayItem.Type, + originalAirDate: 'Program' !== displayItem.Type, + episodeTitleIndexNumber: 'Program' !== displayItem.Type, programIndicator: false }); - var osdMediaInfo = view.querySelector(".osdMediaInfo"); + var osdMediaInfo = view.querySelector('.osdMediaInfo'); osdMediaInfo.innerHTML = mediaInfoHtml; if (mediaInfoHtml) { - osdMediaInfo.classList.remove("hide"); + osdMediaInfo.classList.remove('hide'); } else { - osdMediaInfo.classList.add("hide"); + osdMediaInfo.classList.add('hide'); } - var secondaryMediaInfo = view.querySelector(".osdSecondaryMediaInfo"); + var secondaryMediaInfo = view.querySelector('.osdSecondaryMediaInfo'); var secondaryMediaInfoHtml = mediaInfo.getSecondaryMediaInfoHtml(displayItem, { startDate: false, programTime: false @@ -212,29 +195,29 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med secondaryMediaInfo.innerHTML = secondaryMediaInfoHtml; if (secondaryMediaInfoHtml) { - secondaryMediaInfo.classList.remove("hide"); + secondaryMediaInfo.classList.remove('hide'); } else { - secondaryMediaInfo.classList.add("hide"); + secondaryMediaInfo.classList.add('hide'); } if (displayName) { - view.querySelector(".osdMainTextContainer").classList.remove("hide"); + view.querySelector('.osdMainTextContainer').classList.remove('hide'); } else { - view.querySelector(".osdMainTextContainer").classList.add("hide"); + view.querySelector('.osdMainTextContainer').classList.add('hide'); } if (enableProgressByTimeOfDay) { setDisplayTime(startTimeText, displayItem.StartDate); setDisplayTime(endTimeText, displayItem.EndDate); - startTimeText.classList.remove("hide"); - endTimeText.classList.remove("hide"); + startTimeText.classList.remove('hide'); + endTimeText.classList.remove('hide'); programStartDateMs = displayItem.StartDate ? datetime.parseISO8601Date(displayItem.StartDate).getTime() : 0; programEndDateMs = displayItem.EndDate ? datetime.parseISO8601Date(displayItem.EndDate).getTime() : 0; } else { - startTimeText.classList.add("hide"); - endTimeText.classList.add("hide"); - startTimeText.innerHTML = ""; - endTimeText.innerHTML = ""; + startTimeText.classList.add('hide'); + endTimeText.classList.add('hide'); + startTimeText.innerHTML = ''; + endTimeText.innerHTML = ''; programStartDateMs = 0; programEndDateMs = 0; } @@ -243,13 +226,13 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med function getDisplayTimeWithoutAmPm(date, showSeconds) { if (showSeconds) { return datetime.toLocaleTimeString(date, { - hour: "numeric", - minute: "2-digit", - second: "2-digit" - }).toLowerCase().replace("am", "").replace("pm", "").trim(); + hour: 'numeric', + minute: '2-digit', + second: '2-digit' + }).toLowerCase().replace('am', '').replace('pm', '').trim(); } - return datetime.getDisplayTime(date).toLowerCase().replace("am", "").replace("pm", "").trim(); + return datetime.getDisplayTime(date).toLowerCase().replace('am', '').replace('pm', '').trim(); } function setDisplayTime(elem, date) { @@ -260,11 +243,11 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med html = getDisplayTimeWithoutAmPm(date); } - elem.innerHTML = html || ""; + elem.innerHTML = html || ''; } function shouldEnableProgressByTimeOfDay(item) { - return !("TvChannel" !== item.Type || !item.CurrentProgram); + return !('TvChannel' !== item.Type || !item.CurrentProgram); } function updateNowPlayingInfo(player, state) { @@ -274,15 +257,15 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med if (!item) { setPoster(null); updateRecordingButton(null); - Emby.Page.setTitle(""); + Emby.Page.setTitle(''); nowPlayingVolumeSlider.disabled = true; nowPlayingPositionSlider.disabled = true; btnFastForward.disabled = true; btnRewind.disabled = true; - view.querySelector(".btnSubtitles").classList.add("hide"); - view.querySelector(".btnAudio").classList.add("hide"); - view.querySelector(".osdTitle").innerHTML = ""; - view.querySelector(".osdMediaInfo").innerHTML = ""; + view.querySelector('.btnSubtitles').classList.add('hide'); + view.querySelector('.btnAudio').classList.add('hide'); + view.querySelector('.osdTitle').innerHTML = ''; + view.querySelector('.osdMediaInfo').innerHTML = ''; return; } @@ -294,33 +277,22 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med btnRewind.disabled = false; if (playbackManager.subtitleTracks(player).length) { - view.querySelector(".btnSubtitles").classList.remove("hide"); + view.querySelector('.btnSubtitles').classList.remove('hide'); toggleSubtitleSync(); } else { - view.querySelector(".btnSubtitles").classList.add("hide"); - toggleSubtitleSync("forceToHide"); + view.querySelector('.btnSubtitles').classList.add('hide'); + toggleSubtitleSync('forceToHide'); } if (playbackManager.audioTracks(player).length > 1) { - view.querySelector(".btnAudio").classList.remove("hide"); + view.querySelector('.btnAudio').classList.remove('hide'); } else { - view.querySelector(".btnAudio").classList.add("hide"); + view.querySelector('.btnAudio').classList.add('hide'); } } function setTitle(item, parentName) { - var url = logoImageUrl(item, connectionManager.getApiClient(item.ServerId), {}); - - if (url) { - Emby.Page.setTitle(""); - var pageTitle = document.querySelector(".pageTitle"); - pageTitle.style.backgroundImage = "url('" + url + "')"; - pageTitle.classList.add("pageTitleWithLogo"); - pageTitle.classList.remove("pageTitleWithDefaultLogo"); - pageTitle.innerHTML = ""; - } else { - Emby.Page.setTitle(parentName || ""); - } + Emby.Page.setTitle(parentName || ''); var documentTitle = parentName || (item ? item.Name : null); @@ -330,35 +302,35 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function setPoster(item, secondaryItem) { - var osdPoster = view.querySelector(".osdPoster"); + var osdPoster = view.querySelector('.osdPoster'); if (item) { var imgUrl = seriesImageUrl(item, { maxWidth: osdPoster.clientWidth * 2, - type: "Primary" + type: 'Primary' }) || seriesImageUrl(item, { maxWidth: osdPoster.clientWidth * 2, - type: "Thumb" + type: 'Thumb' }) || imageUrl(item, { maxWidth: osdPoster.clientWidth * 2, - type: "Primary" + type: 'Primary' }); if (!imgUrl && secondaryItem && (imgUrl = seriesImageUrl(secondaryItem, { maxWidth: osdPoster.clientWidth * 2, - type: "Primary" + type: 'Primary' }) || seriesImageUrl(secondaryItem, { maxWidth: osdPoster.clientWidth * 2, - type: "Thumb" + type: 'Thumb' }) || imageUrl(secondaryItem, { maxWidth: osdPoster.clientWidth * 2, - type: "Primary" + type: 'Primary' })), imgUrl) { return void (osdPoster.innerHTML = ''); } } - osdPoster.innerHTML = ""; + osdPoster.innerHTML = ''; } function showOsd() { @@ -373,7 +345,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function toggleOsd() { - if ("osd" === currentVisibleMenu) { + if ('osd' === currentVisibleMenu) { hideOsd(); } else if (!currentVisibleMenu) { showOsd(); @@ -393,11 +365,11 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function slideDownToShow(elem) { - elem.classList.remove("osdHeader-hidden"); + elem.classList.remove('osdHeader-hidden'); } function slideUpToHide(elem) { - elem.classList.add("osdHeader-hidden"); + elem.classList.add('osdHeader-hidden'); } function clearHideAnimationEventListeners(elem) { @@ -410,7 +382,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var elem = e.target; if (elem != osdBottomElement) return; - elem.classList.add("hide"); + elem.classList.add('hide'); dom.removeEventListener(elem, transitionEndEventName, onHideAnimationComplete, { once: true }); @@ -419,14 +391,14 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med function showMainOsdControls() { if (!currentVisibleMenu) { var elem = osdBottomElement; - currentVisibleMenu = "osd"; + currentVisibleMenu = 'osd'; clearHideAnimationEventListeners(elem); - elem.classList.remove("hide"); - elem.classList.remove("videoOsdBottom-hidden"); + elem.classList.remove('hide'); + elem.classList.remove('videoOsdBottom-hidden'); if (!layoutManager.mobile) { setTimeout(function () { - focusManager.focus(elem.querySelector(".btnPause")); + focusManager.focus(elem.querySelector('.btnPause')); }, 50); } toggleSubtitleSync(); @@ -434,15 +406,15 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function hideMainOsdControls() { - if ("osd" === currentVisibleMenu) { + if ('osd' === currentVisibleMenu) { var elem = osdBottomElement; clearHideAnimationEventListeners(elem); - elem.classList.add("videoOsdBottom-hidden"); + elem.classList.add('videoOsdBottom-hidden'); dom.addEventListener(elem, transitionEndEventName, onHideAnimationComplete, { once: true }); currentVisibleMenu = null; - toggleSubtitleSync("hide"); + toggleSubtitleSync('hide'); // Firefox does not blur by itself if (document.activeElement) { @@ -452,7 +424,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function onPointerMove(e) { - if ("mouse" === (e.pointerType || (layoutManager.mobile ? "touch" : "mouse"))) { + if ('mouse' === (e.pointerType || (layoutManager.mobile ? 'touch' : 'mouse'))) { var eventX = e.screenX || 0; var eventY = e.screenY || 0; var obj = lastPointerMoveData; @@ -479,8 +451,8 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var player = currentPlayer; switch (e.detail.command) { - case "left": - if ("osd" === currentVisibleMenu) { + case 'left': + if ('osd' === currentVisibleMenu) { showOsd(); } else { if (!currentVisibleMenu) { @@ -491,8 +463,8 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med break; - case "right": - if ("osd" === currentVisibleMenu) { + case 'right': + if ('osd' === currentVisibleMenu) { showOsd(); } else if (!currentVisibleMenu) { e.preventDefault(); @@ -501,54 +473,59 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med break; - case "pageup": + case 'pageup': playbackManager.nextChapter(player); break; - case "pagedown": + case 'pagedown': playbackManager.previousChapter(player); break; - case "up": - case "down": - case "select": - case "menu": - case "info": - case "play": - case "playpause": - case "pause": - case "fastforward": - case "rewind": - case "next": - case "previous": + case 'up': + case 'down': + case 'select': + case 'menu': + case 'info': + case 'play': + case 'playpause': + case 'pause': + case 'fastforward': + case 'rewind': + case 'next': + case 'previous': showOsd(); break; - case "record": + case 'record': onRecordingCommand(); showOsd(); break; - case "togglestats": + case 'togglestats': toggleStats(); } } function onRecordingCommand() { - var btnRecord = view.querySelector(".btnRecord"); + var btnRecord = view.querySelector('.btnRecord'); - if (!btnRecord.classList.contains("hide")) { + if (!btnRecord.classList.contains('hide')) { btnRecord.click(); } } function updateFullscreenIcon() { + const button = view.querySelector('.btnFullscreen'); + const icon = button.querySelector('.material-icons'); + + icon.classList.remove('fullscreen_exit', 'fullscreen'); + if (playbackManager.isFullscreen(currentPlayer)) { - view.querySelector(".btnFullscreen").setAttribute("title", globalize.translate("ExitFullscreen")); - view.querySelector(".btnFullscreen i").innerHTML = ""; + button.setAttribute('title', globalize.translate('ExitFullscreen') + ' (f)'); + icon.classList.add('fullscreen_exit'); } else { - view.querySelector(".btnFullscreen").setAttribute("title", globalize.translate("Fullscreen") + " (f)"); - view.querySelector(".btnFullscreen i").innerHTML = "fullscreen"; + button.setAttribute('title', globalize.translate('Fullscreen') + ' (f)'); + icon.classList.add('fullscreen'); } } @@ -581,7 +558,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function onPlaybackStart(e, state) { - console.debug("nowplaying event: " + e.type); + console.debug('nowplaying event: ' + e.type); var player = this; onStateChanged.call(player, e, state); resetUpNextDialog(); @@ -600,10 +577,10 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med function onPlaybackStopped(e, state) { currentRuntimeTicks = null; resetUpNextDialog(); - console.debug("nowplaying event: " + e.type); + console.debug('nowplaying event: ' + e.type); - if ("Video" !== state.NextMediaType) { - view.removeEventListener("viewbeforehide", onViewHideStopPlayback); + if ('Video' !== state.NextMediaType) { + view.removeEventListener('viewbeforehide', onViewHideStopPlayback); Emby.Page.back(); } } @@ -612,16 +589,16 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var player = this; var state = playbackManager.getPlayerState(player); onStateChanged.call(player, { - type: "init" + type: 'init' }, state); } function onBeginFetch() { - document.querySelector(".osdMediaStatus").classList.remove("hide"); + document.querySelector('.osdMediaStatus').classList.remove('hide'); } function onEndFetch() { - document.querySelector(".osdMediaStatus").classList.add("hide"); + document.querySelector('.osdMediaStatus').classList.add('hide'); } function bindToPlayer(player) { @@ -632,18 +609,18 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } var state = playbackManager.getPlayerState(player); onStateChanged.call(player, { - type: "init" + type: 'init' }, state); - events.on(player, "playbackstart", onPlaybackStart); - events.on(player, "playbackstop", onPlaybackStopped); - events.on(player, "volumechange", onVolumeChanged); - events.on(player, "pause", onPlayPauseStateChanged); - events.on(player, "unpause", onPlayPauseStateChanged); - events.on(player, "timeupdate", onTimeUpdate); - events.on(player, "fullscreenchange", updateFullscreenIcon); - events.on(player, "mediastreamschange", onMediaStreamsChanged); - events.on(player, "beginFetch", onBeginFetch); - events.on(player, "endFetch", onEndFetch); + events.on(player, 'playbackstart', onPlaybackStart); + events.on(player, 'playbackstop', onPlaybackStopped); + events.on(player, 'volumechange', onVolumeChanged); + events.on(player, 'pause', onPlayPauseStateChanged); + events.on(player, 'unpause', onPlayPauseStateChanged); + events.on(player, 'timeupdate', onTimeUpdate); + events.on(player, 'fullscreenchange', updateFullscreenIcon); + events.on(player, 'mediastreamschange', onMediaStreamsChanged); + events.on(player, 'beginFetch', onBeginFetch); + events.on(player, 'endFetch', onEndFetch); resetUpNextDialog(); if (player.isFetching) { @@ -658,14 +635,14 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var player = currentPlayer; if (player) { - events.off(player, "playbackstart", onPlaybackStart); - events.off(player, "playbackstop", onPlaybackStopped); - events.off(player, "volumechange", onVolumeChanged); - events.off(player, "pause", onPlayPauseStateChanged); - events.off(player, "unpause", onPlayPauseStateChanged); - events.off(player, "timeupdate", onTimeUpdate); - events.off(player, "fullscreenchange", updateFullscreenIcon); - events.off(player, "mediastreamschange", onMediaStreamsChanged); + events.off(player, 'playbackstart', onPlaybackStart); + events.off(player, 'playbackstop', onPlaybackStopped); + events.off(player, 'volumechange', onVolumeChanged); + events.off(player, 'pause', onPlayPauseStateChanged); + events.off(player, 'unpause', onPlayPauseStateChanged); + events.off(player, 'timeupdate', onTimeUpdate); + events.off(player, 'fullscreenchange', updateFullscreenIcon); + events.off(player, 'mediastreamschange', onMediaStreamsChanged); currentPlayer = null; } } @@ -689,7 +666,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function showComingUpNextIfNeeded(player, currentItem, currentTimeTicks, runtimeTicks) { - if (runtimeTicks && currentTimeTicks && !comingUpNextDisplayed && !currentVisibleMenu && "Episode" === currentItem.Type && userSettings.enableNextVideoInfoOverlay()) { + if (runtimeTicks && currentTimeTicks && !comingUpNextDisplayed && !currentVisibleMenu && 'Episode' === currentItem.Type && userSettings.enableNextVideoInfoOverlay()) { var showAtSecondsLeft = runtimeTicks >= 3e10 ? 40 : runtimeTicks >= 24e9 ? 35 : 30; var showAtTicks = runtimeTicks - 1e3 * showAtSecondsLeft * 1e4; var timeRemainingTicks = runtimeTicks - currentTimeTicks; @@ -701,30 +678,30 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function onUpNextHidden() { - if ("upnext" === currentVisibleMenu) { + if ('upnext' === currentVisibleMenu) { currentVisibleMenu = null; } } function showComingUpNext(player) { - require(["upNextDialog"], function (UpNextDialog) { + require(['upNextDialog'], function (UpNextDialog) { if (!(currentVisibleMenu || currentUpNextDialog)) { - currentVisibleMenu = "upnext"; + currentVisibleMenu = 'upnext'; comingUpNextDisplayed = true; playbackManager.nextItem(player).then(function (nextItem) { currentUpNextDialog = new UpNextDialog({ - parent: view.querySelector(".upNextContainer"), + parent: view.querySelector('.upNextContainer'), player: player, nextItem: nextItem }); - events.on(currentUpNextDialog, "hide", onUpNextHidden); + events.on(currentUpNextDialog, 'hide', onUpNextHidden); }, onUpNextHidden); } }); } function refreshProgramInfoIfNeeded(player, item) { - if ("TvChannel" === item.Type) { + if ('TvChannel' === item.Type) { var program = item.CurrentProgram; if (program && program.EndDate) { @@ -732,27 +709,31 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var endDate = datetime.parseISO8601Date(program.EndDate); if (new Date().getTime() >= endDate.getTime()) { - console.debug("program info needs to be refreshed"); + console.debug('program info needs to be refreshed'); var state = playbackManager.getPlayerState(player); onStateChanged.call(player, { - type: "init" + type: 'init' }, state); } } catch (e) { - console.error("error parsing date: " + program.EndDate); + console.error('error parsing date: ' + program.EndDate); } } } } function updatePlayPauseState(isPaused) { - var button = view.querySelector(".btnPause i"); + const btnPlayPause = view.querySelector('.btnPause'); + const btnPlayPauseIcon = btnPlayPause.querySelector('.material-icons'); + + btnPlayPauseIcon.classList.remove('play_arrow', 'pause'); + if (isPaused) { - button.innerHTML = ""; - button.setAttribute("title", globalize.translate("ButtonPlay") + " (k)"); + btnPlayPauseIcon.classList.add('play_arrow'); + btnPlayPause.setAttribute('title', globalize.translate('ButtonPlay') + ' (k)'); } else { - button.innerHTML = "pause"; - button.setAttribute("title", globalize.translate("ButtonPause") + " (k)"); + btnPlayPauseIcon.classList.add('pause'); + btnPlayPause.setAttribute('title', globalize.translate('ButtonPause') + ' (k)'); } } @@ -761,7 +742,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med updatePlayPauseState(playState.IsPaused); var supportedCommands = playbackManager.getSupportedCommands(player); currentPlayerSupportedCommands = supportedCommands; - supportsBrightnessChange = -1 !== supportedCommands.indexOf("SetBrightness"); + supportsBrightnessChange = -1 !== supportedCommands.indexOf('SetBrightness'); updatePlayerVolumeState(player, playState.IsMuted, playState.VolumeLevel); if (nowPlayingPositionSlider && !nowPlayingPositionSlider.dragging) { @@ -775,10 +756,10 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks, playState.PlaybackStartTimeTicks, playState.BufferedRanges || []); updateNowPlayingInfo(player, state); - if (state.MediaSource && state.MediaSource.SupportsTranscoding && -1 !== supportedCommands.indexOf("SetMaxStreamingBitrate")) { - view.querySelector(".btnVideoOsdSettings").classList.remove("hide"); + if (state.MediaSource && state.MediaSource.SupportsTranscoding && -1 !== supportedCommands.indexOf('SetMaxStreamingBitrate')) { + view.querySelector('.btnVideoOsdSettings').classList.remove('hide'); } else { - view.querySelector(".btnVideoOsdSettings").classList.add("hide"); + view.querySelector('.btnVideoOsdSettings').classList.add('hide'); } var isProgressClear = state.MediaSource && null == state.MediaSource.RunTimeTicks; @@ -789,22 +770,22 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med userSettings.skipForwardLength() * 1000000 / nowPlayingItem.RunTimeTicks); } - if (-1 === supportedCommands.indexOf("ToggleFullscreen") || player.isLocalPlayer && layoutManager.tv && playbackManager.isFullscreen(player)) { - view.querySelector(".btnFullscreen").classList.add("hide"); + if (-1 === supportedCommands.indexOf('ToggleFullscreen') || player.isLocalPlayer && layoutManager.tv && playbackManager.isFullscreen(player)) { + view.querySelector('.btnFullscreen').classList.add('hide'); } else { - view.querySelector(".btnFullscreen").classList.remove("hide"); + view.querySelector('.btnFullscreen').classList.remove('hide'); } - if (-1 === supportedCommands.indexOf("PictureInPicture")) { - view.querySelector(".btnPip").classList.add("hide"); + if (-1 === supportedCommands.indexOf('PictureInPicture')) { + view.querySelector('.btnPip').classList.add('hide'); } else { - view.querySelector(".btnPip").classList.remove("hide"); + view.querySelector('.btnPip').classList.remove('hide'); } - if (-1 === supportedCommands.indexOf("AirPlay")) { - view.querySelector(".btnAirPlay").classList.add("hide"); + if (-1 === supportedCommands.indexOf('AirPlay')) { + view.querySelector('.btnAirPlay').classList.add('hide'); } else { - view.querySelector(".btnAirPlay").classList.remove("hide"); + view.querySelector('.btnAirPlay').classList.remove('hide'); } updateFullscreenIcon(); @@ -837,8 +818,8 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } } - nowPlayingPositionText.innerHTML = ""; - nowPlayingDurationText.innerHTML = ""; + nowPlayingPositionText.innerHTML = ''; + nowPlayingDurationText.innerHTML = ''; } else { if (nowPlayingPositionSlider && !nowPlayingPositionSlider.dragging) { if (runtimeTicks) { @@ -849,10 +830,10 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med nowPlayingPositionSlider.value = 0; } - if (runtimeTicks && null != positionTicks && currentRuntimeTicks && !enableProgressByTimeOfDay && currentItem.RunTimeTicks && "Recording" !== currentItem.Type) { - endsAtText.innerHTML = "  -  " + mediaInfo.getEndsAtFromPosition(runtimeTicks, positionTicks, true); + if (runtimeTicks && null != positionTicks && currentRuntimeTicks && !enableProgressByTimeOfDay && currentItem.RunTimeTicks && 'Recording' !== currentItem.Type) { + endsAtText.innerHTML = '  -  ' + mediaInfo.getEndsAtFromPosition(runtimeTicks, positionTicks, true); } else { - endsAtText.innerHTML = ""; + endsAtText.innerHTML = ''; } } @@ -870,38 +851,43 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var showMuteButton = true; var showVolumeSlider = true; - if (-1 === supportedCommands.indexOf("Mute")) { + if (-1 === supportedCommands.indexOf('Mute')) { showMuteButton = false; } - if (-1 === supportedCommands.indexOf("SetVolume")) { + if (-1 === supportedCommands.indexOf('SetVolume')) { showVolumeSlider = false; } - if (player.isLocalPlayer && appHost.supports("physicalvolumecontrol")) { + if (player.isLocalPlayer && appHost.supports('physicalvolumecontrol')) { showMuteButton = false; showVolumeSlider = false; } + const buttonMute = view.querySelector('.buttonMute'); + const buttonMuteIcon = buttonMute.querySelector('.material-icons'); + + buttonMuteIcon.classList.remove('volume_off', 'volume_up'); + if (isMuted) { - view.querySelector(".buttonMute").setAttribute("title", globalize.translate("Unmute") + " (m)"); - view.querySelector(".buttonMute i").innerHTML = ""; + buttonMute.setAttribute('title', globalize.translate('Unmute') + ' (m)'); + buttonMuteIcon.classList.add('volume_off'); } else { - view.querySelector(".buttonMute").setAttribute("title", globalize.translate("Mute") + " (m)"); - view.querySelector(".buttonMute i").innerHTML = ""; + buttonMute.setAttribute('title', globalize.translate('Mute') + ' (m)'); + buttonMuteIcon.classList.add('volume_up'); } if (showMuteButton) { - view.querySelector(".buttonMute").classList.remove("hide"); + buttonMute.classList.remove('hide'); } else { - view.querySelector(".buttonMute").classList.add("hide"); + buttonMute.classList.add('hide'); } if (nowPlayingVolumeSlider) { if (showVolumeSlider) { - nowPlayingVolumeSliderContainer.classList.remove("hide"); + nowPlayingVolumeSliderContainer.classList.remove('hide'); } else { - nowPlayingVolumeSliderContainer.classList.add("hide"); + nowPlayingVolumeSliderContainer.classList.add('hide'); } if (!nowPlayingVolumeSlider.dragging) { @@ -911,24 +897,24 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function updatePlaylist(player) { - var btnPreviousTrack = view.querySelector(".btnPreviousTrack"); - var btnNextTrack = view.querySelector(".btnNextTrack"); - btnPreviousTrack.classList.remove("hide"); - btnNextTrack.classList.remove("hide"); + var btnPreviousTrack = view.querySelector('.btnPreviousTrack'); + var btnNextTrack = view.querySelector('.btnNextTrack'); + btnPreviousTrack.classList.remove('hide'); + btnNextTrack.classList.remove('hide'); btnNextTrack.disabled = false; btnPreviousTrack.disabled = false; } function updateTimeText(elem, ticks, divider) { if (null == ticks) { - elem.innerHTML = ""; + elem.innerHTML = ''; return; } var html = datetime.getDisplayRunningTime(ticks); if (divider) { - html = " / " + html; + html = ' / ' + html; } elem.innerHTML = html; @@ -937,7 +923,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med function onSettingsButtonClick(e) { var btn = this; - require(["playerSettingsMenu"], function (playerSettingsMenu) { + require(['playerSettingsMenu'], function (playerSettingsMenu) { var player = currentPlayer; if (player) { @@ -947,7 +933,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med playbackManager.canHandleOffsetOnCurrentSubtitle(player); playerSettingsMenu.show({ - mediaType: "Video", + mediaType: 'Video', player: player, positionTo: btn, stats: true, @@ -959,9 +945,9 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function onSettingsOption(selectedOption) { - if ("stats" === selectedOption) { + if ('stats' === selectedOption) { toggleStats(); - } else if ("suboffset" === selectedOption) { + } else if ('suboffset' === selectedOption) { var player = currentPlayer; if (player) { playbackManager.enableShowingSubtitleOffset(player); @@ -971,7 +957,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function toggleStats() { - require(["playerStats"], function (PlayerStats) { + require(['playerStats'], function (PlayerStats) { var player = currentPlayer; if (player) { @@ -1011,10 +997,10 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med }); var positionTo = this; - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, - title: globalize.translate("Audio"), + title: globalize.translate('Audio'), positionTo: positionTo }).then(function (id) { var index = parseInt(id); @@ -1037,7 +1023,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med streams.unshift({ Index: -1, - DisplayTitle: globalize.translate("Off") + DisplayTitle: globalize.translate('Off') }); var menuItems = streams.map(function (stream) { var opt = { @@ -1053,9 +1039,9 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med }); var positionTo = this; - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ - title: globalize.translate("Subtitles"), + title: globalize.translate('Subtitles'), items: menuItems, positionTo: positionTo }).then(function (id) { @@ -1071,7 +1057,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } function toggleSubtitleSync(action) { - require(["subtitleSync"], function (SubtitleSync) { + require(['subtitleSync'], function (SubtitleSync) { var player = currentPlayer; if (subtitleSyncOverlay) { subtitleSyncOverlay.toggle(action); @@ -1111,60 +1097,100 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } switch (key) { - case "Enter": + case 'Enter': showOsd(); break; - case "Escape": - case "Back": + case 'Escape': + case 'Back': // Ignore key when some dialog is opened - if (currentVisibleMenu === "osd" && !document.querySelector(".dialogContainer")) { + if (currentVisibleMenu === 'osd' && !document.querySelector('.dialogContainer')) { hideOsd(); e.stopPropagation(); } break; - case "k": + case 'k': playbackManager.playPause(currentPlayer); showOsd(); break; - case "l": - case "ArrowRight": - case "Right": + case 'ArrowUp': + case 'Up': + playbackManager.volumeUp(currentPlayer); + break; + case 'ArrowDown': + case 'Down': + playbackManager.volumeDown(currentPlayer); + break; + case 'l': + case 'ArrowRight': + case 'Right': playbackManager.fastForward(currentPlayer); showOsd(); break; - case "j": - case "ArrowLeft": - case "Left": + case 'j': + case 'ArrowLeft': + case 'Left': playbackManager.rewind(currentPlayer); showOsd(); break; - case "f": + case 'f': if (!e.ctrlKey && !e.metaKey) { playbackManager.toggleFullscreen(currentPlayer); showOsd(); } break; - case "m": + case 'm': playbackManager.toggleMute(currentPlayer); showOsd(); break; - case "NavigationLeft": - case "GamepadDPadLeft": - case "GamepadLeftThumbstickLeft": + case 'p': + case 'P': + if (e.shiftKey) { + playbackManager.previousTrack(currentPlayer); + } + break; + case 'n': + case 'N': + if (e.shiftKey) { + playbackManager.nextTrack(currentPlayer); + } + break; + case 'NavigationLeft': + case 'GamepadDPadLeft': + case 'GamepadLeftThumbstickLeft': // Ignores gamepad events that are always triggered, even when not focused. - if (document.hasFocus()) { + if (document.hasFocus()) { /* eslint-disable-line compat/compat */ playbackManager.rewind(currentPlayer); showOsd(); } break; - case "NavigationRight": - case "GamepadDPadRight": - case "GamepadLeftThumbstickRight": + case 'NavigationRight': + case 'GamepadDPadRight': + case 'GamepadLeftThumbstickRight': // Ignores gamepad events that are always triggered, even when not focused. - if (document.hasFocus()) { + if (document.hasFocus()) { /* eslint-disable-line compat/compat */ playbackManager.fastForward(currentPlayer); showOsd(); } + break; + case 'Home': + playbackManager.seekPercent(0, currentPlayer); + break; + case 'End': + playbackManager.seekPercent(100, currentPlayer); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + var percent = parseInt(key, 10) * 10; + playbackManager.seekPercent(percent, currentPlayer); + break; } } @@ -1181,7 +1207,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med return apiClient.getScaledImageUrl(item.Id, { maxWidth: maxWidth, tag: chapter.ImageTag, - type: "Chapter", + type: 'Chapter', index: index }); } @@ -1214,12 +1240,12 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med html += '
'; html += '
'; html += chapter.Name; - html += "
"; + html += '
'; html += '

'; html += datetime.getDisplayRunningTime(positionTicks); - html += "

"; - html += ""; - return html + ""; + html += ''; + html += ''; + return html + ''; } return null; @@ -1234,17 +1260,17 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med clearTimeout(playPauseClickTimeout); var player = currentPlayer; - view.removeEventListener("viewbeforehide", onViewHideStopPlayback); + view.removeEventListener('viewbeforehide', onViewHideStopPlayback); releaseCurrentPlayer(); playbackManager.stop(player); } } function enableStopOnBack(enabled) { - view.removeEventListener("viewbeforehide", onViewHideStopPlayback); + view.removeEventListener('viewbeforehide', onViewHideStopPlayback); if (enabled && playbackManager.isPlayingVideo(currentPlayer)) { - view.addEventListener("viewbeforehide", onViewHideStopPlayback); + view.addEventListener('viewbeforehide', onViewHideStopPlayback); } } @@ -1272,46 +1298,45 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var programEndDateMs = 0; var playbackStartTimeTicks = 0; var subtitleSyncOverlay; - var volumeSliderTimer; - var nowPlayingVolumeSlider = view.querySelector(".osdVolumeSlider"); - var nowPlayingVolumeSliderContainer = view.querySelector(".osdVolumeSliderContainer"); - var nowPlayingPositionSlider = view.querySelector(".osdPositionSlider"); - var nowPlayingPositionText = view.querySelector(".osdPositionText"); - var nowPlayingDurationText = view.querySelector(".osdDurationText"); - var startTimeText = view.querySelector(".startTimeText"); - var endTimeText = view.querySelector(".endTimeText"); - var endsAtText = view.querySelector(".endsAtText"); - var btnRewind = view.querySelector(".btnRewind"); - var btnFastForward = view.querySelector(".btnFastForward"); + var nowPlayingVolumeSlider = view.querySelector('.osdVolumeSlider'); + var nowPlayingVolumeSliderContainer = view.querySelector('.osdVolumeSliderContainer'); + var nowPlayingPositionSlider = view.querySelector('.osdPositionSlider'); + var nowPlayingPositionText = view.querySelector('.osdPositionText'); + var nowPlayingDurationText = view.querySelector('.osdDurationText'); + var startTimeText = view.querySelector('.startTimeText'); + var endTimeText = view.querySelector('.endTimeText'); + var endsAtText = view.querySelector('.endsAtText'); + var btnRewind = view.querySelector('.btnRewind'); + var btnFastForward = view.querySelector('.btnFastForward'); var transitionEndEventName = dom.whichTransitionEvent(); - var headerElement = document.querySelector(".skinHeader"); - var osdBottomElement = document.querySelector(".videoOsdBottom-maincontrols"); + var headerElement = document.querySelector('.skinHeader'); + var osdBottomElement = document.querySelector('.videoOsdBottom-maincontrols'); if (layoutManager.tv) { - nowPlayingPositionSlider.classList.add("focusable"); + nowPlayingPositionSlider.classList.add('focusable'); nowPlayingPositionSlider.enableKeyboardDragging(); } - view.addEventListener("viewbeforeshow", function (e) { - headerElement.classList.add("osdHeader"); - Emby.Page.setTransparency("full"); + view.addEventListener('viewbeforeshow', function (e) { + headerElement.classList.add('osdHeader'); + Emby.Page.setTransparency('full'); }); - view.addEventListener("viewshow", function (e) { + view.addEventListener('viewshow', function (e) { try { - events.on(playbackManager, "playerchange", onPlayerChange); + events.on(playbackManager, 'playerchange', onPlayerChange); bindToPlayer(playbackManager.getCurrentPlayer()); - dom.addEventListener(document, window.PointerEvent ? "pointermove" : "mousemove", onPointerMove, { + dom.addEventListener(document, window.PointerEvent ? 'pointermove' : 'mousemove', onPointerMove, { passive: true }); showOsd(); inputManager.on(window, onInputCommand); - dom.addEventListener(window, "keydown", onWindowKeyDown, { + dom.addEventListener(window, 'keydown', onWindowKeyDown, { capture: true }); - dom.addEventListener(window, window.PointerEvent ? "pointerdown" : "mousedown", onWindowMouseDown, { + dom.addEventListener(window, window.PointerEvent ? 'pointerdown' : 'mousedown', onWindowMouseDown, { passive: true }); - dom.addEventListener(window, "touchstart", onWindowTouchStart, { + dom.addEventListener(window, 'touchstart', onWindowTouchStart, { passive: true }); } catch (e) { @@ -1320,44 +1345,44 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med }); } }); - view.addEventListener("viewbeforehide", function () { + view.addEventListener('viewbeforehide', function () { if (statsOverlay) { statsOverlay.enabled(false); } - dom.removeEventListener(window, "keydown", onWindowKeyDown, { + dom.removeEventListener(window, 'keydown', onWindowKeyDown, { capture: true }); - dom.removeEventListener(window, window.PointerEvent ? "pointerdown" : "mousedown", onWindowMouseDown, { + dom.removeEventListener(window, window.PointerEvent ? 'pointerdown' : 'mousedown', onWindowMouseDown, { passive: true }); - dom.removeEventListener(window, "touchstart", onWindowTouchStart, { + dom.removeEventListener(window, 'touchstart', onWindowTouchStart, { passive: true }); stopOsdHideTimer(); - headerElement.classList.remove("osdHeader"); - headerElement.classList.remove("osdHeader-hidden"); - dom.removeEventListener(document, window.PointerEvent ? "pointermove" : "mousemove", onPointerMove, { + headerElement.classList.remove('osdHeader'); + headerElement.classList.remove('osdHeader-hidden'); + dom.removeEventListener(document, window.PointerEvent ? 'pointermove' : 'mousemove', onPointerMove, { passive: true }); inputManager.off(window, onInputCommand); - events.off(playbackManager, "playerchange", onPlayerChange); + events.off(playbackManager, 'playerchange', onPlayerChange); releaseCurrentPlayer(); }); - view.querySelector(".btnFullscreen").addEventListener("click", function () { + view.querySelector('.btnFullscreen').addEventListener('click', function () { playbackManager.toggleFullscreen(currentPlayer); }); - view.querySelector(".btnPip").addEventListener("click", function () { + view.querySelector('.btnPip').addEventListener('click', function () { playbackManager.togglePictureInPicture(currentPlayer); }); - view.querySelector(".btnAirPlay").addEventListener("click", function () { + view.querySelector('.btnAirPlay').addEventListener('click', function () { playbackManager.toggleAirPlay(currentPlayer); }); - view.querySelector(".btnVideoOsdSettings").addEventListener("click", onSettingsButtonClick); - view.addEventListener("viewhide", function () { - headerElement.classList.remove("hide"); + view.querySelector('.btnVideoOsdSettings').addEventListener('click', onSettingsButtonClick); + view.addEventListener('viewhide', function () { + headerElement.classList.remove('hide'); }); - view.addEventListener("viewdestroy", function () { + view.addEventListener('viewdestroy', function () { if (self.touchHelper) { self.touchHelper.destroy(); self.touchHelper = null; @@ -1372,16 +1397,16 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med destroySubtitleSync(); }); var lastPointerDown = 0; - dom.addEventListener(view, window.PointerEvent ? "pointerdown" : "click", function (e) { - if (dom.parentWithClass(e.target, ["videoOsdBottom", "upNextContainer"])) { + dom.addEventListener(view, window.PointerEvent ? 'pointerdown' : 'click', function (e) { + if (dom.parentWithClass(e.target, ['videoOsdBottom', 'upNextContainer'])) { return void showOsd(); } - var pointerType = e.pointerType || (layoutManager.mobile ? "touch" : "mouse"); + var pointerType = e.pointerType || (layoutManager.mobile ? 'touch' : 'mouse'); var now = new Date().getTime(); switch (pointerType) { - case "touch": + case 'touch': if (now - lastPointerDown > 300) { lastPointerDown = now; toggleOsd(); @@ -1389,7 +1414,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med break; - case "mouse": + case 'mouse': if (!e.button) { if (playPauseClickTimeout) { clearTimeout(playPauseClickTimeout); @@ -1414,38 +1439,26 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med }); if (browser.touch) { - dom.addEventListener(view, "dblclick", onDoubleClick, {}); + dom.addEventListener(view, 'dblclick', onDoubleClick, {}); } else { var options = { passive: true }; - dom.addEventListener(view, "dblclick", function () { + dom.addEventListener(view, 'dblclick', function () { playbackManager.toggleFullscreen(currentPlayer); }, options); } function setVolume() { - clearTimeout(volumeSliderTimer); - volumeSliderTimer = null; - playbackManager.setVolume(this.value, currentPlayer); } - function setVolumeDelayed() { - if (!volumeSliderTimer) { - var that = this; - volumeSliderTimer = setTimeout(function () { - setVolume.call(that); - }, 700); - } - } - - view.querySelector(".buttonMute").addEventListener("click", function () { + view.querySelector('.buttonMute').addEventListener('click', function () { playbackManager.toggleMute(currentPlayer); }); - nowPlayingVolumeSlider.addEventListener("change", setVolume); - nowPlayingVolumeSlider.addEventListener("mousemove", setVolumeDelayed); - nowPlayingVolumeSlider.addEventListener("touchmove", setVolumeDelayed); + nowPlayingVolumeSlider.addEventListener('change', setVolume); + nowPlayingVolumeSlider.addEventListener('mousemove', setVolume); + nowPlayingVolumeSlider.addEventListener('touchmove', setVolume); - nowPlayingPositionSlider.addEventListener("change", function () { + nowPlayingPositionSlider.addEventListener('change', function () { var player = currentPlayer; if (player) { @@ -1470,14 +1483,14 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med ms /= 100; ms *= value; ms += programStartDateMs; - return '

' + getDisplayTimeWithoutAmPm(new Date(parseInt(ms)), true) + "

"; + return '

' + getDisplayTimeWithoutAmPm(new Date(parseInt(ms)), true) + '

'; } - return "--:--"; + return '--:--'; } if (!currentRuntimeTicks) { - return "--:--"; + return '--:--'; } var ticks = currentRuntimeTicks; @@ -1493,41 +1506,41 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } } - return '

' + datetime.getDisplayRunningTime(ticks) + "

"; + return '

' + datetime.getDisplayRunningTime(ticks) + '

'; }; - view.querySelector(".btnPreviousTrack").addEventListener("click", function () { + view.querySelector('.btnPreviousTrack').addEventListener('click', function () { playbackManager.previousTrack(currentPlayer); }); - view.querySelector(".btnPause").addEventListener("click", function () { + view.querySelector('.btnPause').addEventListener('click', function () { // Ignore 'click' if another element was originally clicked (Firefox/Edge issue) if (this.contains(clickedElement)) { playbackManager.playPause(currentPlayer); } }); - view.querySelector(".btnNextTrack").addEventListener("click", function () { + view.querySelector('.btnNextTrack').addEventListener('click', function () { playbackManager.nextTrack(currentPlayer); }); - btnRewind.addEventListener("click", function () { + btnRewind.addEventListener('click', function () { playbackManager.rewind(currentPlayer); }); - btnFastForward.addEventListener("click", function () { + btnFastForward.addEventListener('click', function () { playbackManager.fastForward(currentPlayer); }); - view.querySelector(".btnAudio").addEventListener("click", showAudioTrackSelection); - view.querySelector(".btnSubtitles").addEventListener("click", showSubtitleTrackSelection); + view.querySelector('.btnAudio').addEventListener('click', showAudioTrackSelection); + view.querySelector('.btnSubtitles').addEventListener('click', showSubtitleTrackSelection); if (browser.touch) { (function () { - require(["touchHelper"], function (TouchHelper) { + require(['touchHelper'], function (TouchHelper) { self.touchHelper = new TouchHelper(view, { swipeYThreshold: 30, triggerOnMove: true, preventDefaultOnMove: true, - ignoreTagNames: ["BUTTON", "INPUT", "TEXTAREA"] + ignoreTagNames: ['BUTTON', 'INPUT', 'TEXTAREA'] }); - events.on(self.touchHelper, "swipeup", onVerticalSwipe); - events.on(self.touchHelper, "swipedown", onVerticalSwipe); + events.on(self.touchHelper, 'swipeup', onVerticalSwipe); + events.on(self.touchHelper, 'swipedown', onVerticalSwipe); }); })(); } diff --git a/src/controllers/searchpage.js b/src/controllers/searchpage.js index b260ef5751..8a138b7516 100644 --- a/src/controllers/searchpage.js +++ b/src/controllers/searchpage.js @@ -1,5 +1,5 @@ -define(["focusManager", "searchFields", "searchResults", "events"], function (focusManager, SearchFields, SearchResults, events) { - "use strict"; +define(['focusManager', 'searchFields', 'searchResults', 'events'], function (focusManager, SearchFields, SearchResults, events) { + 'use strict'; return function (view, params) { function onSearch(e, value) { @@ -7,21 +7,21 @@ define(["focusManager", "searchFields", "searchResults", "events"], function (fo } var self = this; - view.addEventListener("viewshow", function () { + view.addEventListener('viewshow', function () { if (!self.searchFields) { self.searchFields = new SearchFields({ - element: view.querySelector(".searchFields") + element: view.querySelector('.searchFields') }); self.searchResults = new SearchResults({ - element: view.querySelector(".searchResults"), + element: view.querySelector('.searchResults'), serverId: params.serverId || ApiClient.serverId(), parentId: params.parentId, collectionType: params.collectionType }); - events.on(self.searchFields, "search", onSearch); + events.on(self.searchFields, 'search', onSearch); } }); - view.addEventListener("viewdestroy", function () { + view.addEventListener('viewdestroy', function () { if (self.searchFields) { self.searchFields.destroy(); self.searchFields = null; diff --git a/src/controllers/serveractivity.js b/src/controllers/serveractivity.js deleted file mode 100644 index fb3b8112dc..0000000000 --- a/src/controllers/serveractivity.js +++ /dev/null @@ -1,31 +0,0 @@ -define(["components/activitylog", "globalize"], function (ActivityLog, globalize) { - "use strict"; - - return function (view, params) { - var activityLog; - - if (params.useractivity !== "false") { - view.querySelector(".activityItems").setAttribute("data-useractivity", "true"); - view.querySelector(".sectionTitle").innerHTML = globalize.translate("HeaderActivity"); - } else { - view.querySelector(".activityItems").setAttribute("data-useractivity", "false"); - view.querySelector(".sectionTitle").innerHTML = globalize.translate("Alerts"); - } - - view.addEventListener("viewshow", function () { - if (!activityLog) { - activityLog = new ActivityLog({ - serverId: ApiClient.serverId(), - element: view.querySelector(".activityItems") - }); - } - }); - view.addEventListener("viewdestroy", function () { - if (activityLog) { - activityLog.destroy(); - } - - activityLog = null; - }); - }; -}); diff --git a/src/controllers/shows/episodes.js b/src/controllers/shows/episodes.js index 9c61ac80d8..eeede20661 100644 --- a/src/controllers/shows/episodes.js +++ b/src/controllers/shows/episodes.js @@ -1,5 +1,5 @@ -define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardBuilder", "emby-itemscontainer"], function (loading, events, libraryBrowser, imageLoader, listView, cardBuilder) { - "use strict"; +define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (loading, events, libraryBrowser, imageLoader, listView, cardBuilder, userSettings, globalize) { + 'use strict'; return function (view, params, tabContent) { function getPageData(context) { @@ -9,19 +9,23 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB if (!pageData) { pageData = data[key] = { query: { - SortBy: "SeriesSortName,SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Episode", + SortBy: 'SeriesSortName,SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Episode', Recursive: true, - Fields: "PrimaryImageAspectRatio,MediaSourceCount,UserData", + Fields: 'PrimaryImageAspectRatio,MediaSourceCount,UserData', IsMissing: false, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Thumb", - StartIndex: 0, - Limit: pageSize + EnableImageTypes: 'Primary,Backdrop,Thumb', + StartIndex: 0 }, - view: libraryBrowser.getSavedView(key) || "Poster" + view: libraryBrowser.getSavedView(key) || 'Poster' }; + + if (userSettings.libraryPageSize() > 0) { + pageData.query['Limit'] = userSettings.libraryPageSize(); + } + pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); } @@ -35,7 +39,7 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB function getSavedQueryKey(context) { if (!context.savedQueryKey) { - context.savedQueryKey = libraryBrowser.getSavedQueryKey("episodes"); + context.savedQueryKey = libraryBrowser.getSavedQueryKey('episodes'); } return context.savedQueryKey; @@ -43,17 +47,17 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB function onViewStyleChange() { var viewStyle = self.getCurrentViewStyle(); - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); - if ("List" == viewStyle) { - itemsContainer.classList.add("vertical-list"); - itemsContainer.classList.remove("vertical-wrap"); + if ('List' == viewStyle) { + itemsContainer.classList.add('vertical-list'); + itemsContainer.classList.remove('vertical-wrap'); } else { - itemsContainer.classList.remove("vertical-list"); - itemsContainer.classList.add("vertical-wrap"); + itemsContainer.classList.remove('vertical-list'); + itemsContainer.classList.add('vertical-wrap'); } - itemsContainer.innerHTML = ""; + itemsContainer.innerHTML = ''; } function reloadItems(page) { @@ -66,7 +70,9 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB return; } - query.StartIndex += query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } reloadItems(tabContent); } @@ -75,7 +81,9 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB return; } - query.StartIndex -= query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } reloadItems(tabContent); } @@ -92,17 +100,17 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB filterButton: false }); var viewStyle = self.getCurrentViewStyle(); - var itemsContainer = tabContent.querySelector(".itemsContainer"); - if (viewStyle == "List") { + var itemsContainer = tabContent.querySelector('.itemsContainer'); + if (viewStyle == 'List') { html = listView.getListViewHtml({ items: result.Items, sortBy: query.SortBy, showParentTitle: true }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', showTitle: true, showParentTitle: true, scalable: true, @@ -111,7 +119,7 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB } else { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', showTitle: true, showParentTitle: true, overlayText: false, @@ -124,19 +132,19 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB var length; var elems; - elems = tabContent.querySelectorAll(".paging"); + elems = tabContent.querySelectorAll('.paging'); for (i = 0, length = elems.length; i < length; i++) { elems[i].innerHTML = pagingHtml; } - elems = tabContent.querySelectorAll(".btnNextPage"); + elems = tabContent.querySelectorAll('.btnNextPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onNextPageClick); + elems[i].addEventListener('click', onNextPageClick); } - elems = tabContent.querySelectorAll(".btnPreviousPage"); + elems = tabContent.querySelectorAll('.btnPreviousPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onPreviousPageClick); + elems[i].addEventListener('click', onPreviousPageClick); } itemsContainer.innerHTML = html; @@ -145,25 +153,24 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB loading.hide(); isLoading = false; - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); }); } var self = this; - var pageSize = 100; var data = {}; var isLoading = false; self.showFilterMenu = function () { - require(["components/filterdialog/filterdialog"], function (filterDialogFactory) { + require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { var filterDialog = new filterDialogFactory({ query: getQuery(tabContent), - mode: "episodes", + mode: 'episodes', serverId: ApiClient.serverId() }); - events.on(filterDialog, "filterchange", function () { + events.on(filterDialog, 'filterchange', function () { reloadItems(tabContent); }); filterDialog.show(); @@ -175,35 +182,35 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB }; function initPage(tabContent) { - tabContent.querySelector(".btnFilter").addEventListener("click", function () { + tabContent.querySelector('.btnFilter').addEventListener('click', function () { self.showFilterMenu(); }); - tabContent.querySelector(".btnSort").addEventListener("click", function (e) { + tabContent.querySelector('.btnSort').addEventListener('click', function (e) { libraryBrowser.showSortMenu({ items: [{ - name: Globalize.translate("OptionNameSort"), - id: "SeriesSortName,SortName" + name: globalize.translate('OptionNameSort'), + id: 'SeriesSortName,SortName' }, { - name: Globalize.translate("OptionTvdbRating"), - id: "CommunityRating,SeriesSortName,SortName" + name: globalize.translate('OptionTvdbRating'), + id: 'CommunityRating,SeriesSortName,SortName' }, { - name: Globalize.translate("OptionDateAdded"), - id: "DateCreated,SeriesSortName,SortName" + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SeriesSortName,SortName' }, { - name: Globalize.translate("OptionPremiereDate"), - id: "PremiereDate,SeriesSortName,SortName" + name: globalize.translate('OptionPremiereDate'), + id: 'PremiereDate,SeriesSortName,SortName' }, { - name: Globalize.translate("OptionDatePlayed"), - id: "DatePlayed,SeriesSortName,SortName" + name: globalize.translate('OptionDatePlayed'), + id: 'DatePlayed,SeriesSortName,SortName' }, { - name: Globalize.translate("OptionParentalRating"), - id: "OfficialRating,SeriesSortName,SortName" + name: globalize.translate('OptionParentalRating'), + id: 'OfficialRating,SeriesSortName,SortName' }, { - name: Globalize.translate("OptionPlayCount"), - id: "PlayCount,SeriesSortName,SortName" + name: globalize.translate('OptionPlayCount'), + id: 'PlayCount,SeriesSortName,SortName' }, { - name: Globalize.translate("OptionRuntime"), - id: "Runtime,SeriesSortName,SortName" + name: globalize.translate('OptionRuntime'), + id: 'Runtime,SeriesSortName,SortName' }], callback: function () { reloadItems(tabContent); @@ -212,11 +219,11 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB button: e.target }); }); - var btnSelectView = tabContent.querySelector(".btnSelectView"); - btnSelectView.addEventListener("click", function (e) { - libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), "List,Poster,PosterCard".split(",")); + var btnSelectView = tabContent.querySelector('.btnSelectView'); + btnSelectView.addEventListener('click', function (e) { + libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'List,Poster,PosterCard'.split(',')); }); - btnSelectView.addEventListener("layoutchange", function (e) { + btnSelectView.addEventListener('layoutchange', function (e) { var viewStyle = e.detail.viewStyle; getPageData(tabContent).view = viewStyle; libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle); diff --git a/src/controllers/shows/tvgenres.js b/src/controllers/shows/tvgenres.js index 9c37e04e7c..de38763e99 100644 --- a/src/controllers/shows/tvgenres.js +++ b/src/controllers/shows/tvgenres.js @@ -1,5 +1,5 @@ -define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader", "apphost", "globalize", "appRouter", "dom", "emby-button"], function (layoutManager, loading, libraryBrowser, cardBuilder, lazyLoader, appHost, globalize, appRouter, dom) { - "use strict"; +define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader', 'apphost', 'globalize', 'appRouter', 'dom', 'emby-button'], function (layoutManager, loading, libraryBrowser, cardBuilder, lazyLoader, appHost, globalize, appRouter, dom) { + 'use strict'; return function (view, params, tabContent) { function getPageData() { @@ -9,13 +9,13 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader if (!pageData) { pageData = data[key] = { query: { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Series", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Series', Recursive: true, EnableTotalRecordCount: false }, - view: "Poster" + view: 'Poster' }; pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); @@ -29,7 +29,7 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader } function getSavedQueryKey() { - return libraryBrowser.getSavedQueryKey("seriesgenres"); + return libraryBrowser.getSavedQueryKey('seriesgenres'); } function getPromise() { @@ -43,29 +43,30 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader } function getThumbShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop"; + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function getPortraitShape() { - return enableScrollX() ? "overflowPortrait" : "portrait"; + return enableScrollX() ? 'overflowPortrait' : 'portrait'; } - function fillItemsContainer(elem) { - var id = elem.getAttribute("data-id"); + function fillItemsContainer(entry) { + var elem = entry.target; + var id = elem.getAttribute('data-id'); var viewStyle = self.getCurrentViewStyle(); - var limit = "Thumb" == viewStyle || "ThumbCard" == viewStyle ? 5 : 9; + var limit = 'Thumb' == viewStyle || 'ThumbCard' == viewStyle ? 5 : 9; if (enableScrollX()) { limit = 10; } - var enableImageTypes = "Thumb" == viewStyle || "ThumbCard" == viewStyle ? "Primary,Backdrop,Thumb" : "Primary"; + var enableImageTypes = 'Thumb' == viewStyle || 'ThumbCard' == viewStyle ? 'Primary,Backdrop,Thumb' : 'Primary'; var query = { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Series", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Series', Recursive: true, - Fields: "PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', ImageTypeLimit: 1, EnableImageTypes: enableImageTypes, Limit: limit, @@ -74,9 +75,9 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader ParentId: params.topParentId }; ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) { - var supportsImageAnalysis = appHost.supports("imageanalysis"); + var supportsImageAnalysis = appHost.supports('imageanalysis'); - if (viewStyle == "Thumb") { + if (viewStyle == 'Thumb') { cardBuilder.buildCards(result.Items, { itemsContainer: elem, shape: getThumbShape(), @@ -87,7 +88,7 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader overlayMoreButton: true, allowBottomPadding: false }); - } else if (viewStyle == "ThumbCard") { + } else if (viewStyle == 'ThumbCard') { cardBuilder.buildCards(result.Items, { itemsContainer: elem, shape: getThumbShape(), @@ -98,7 +99,7 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader cardLayout: true, showYear: true }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { cardBuilder.buildCards(result.Items, { itemsContainer: elem, shape: getPortraitShape(), @@ -108,17 +109,20 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader cardLayout: true, showYear: true }); - } else if (viewStyle == "Poster") { + } else if (viewStyle == 'Poster') { cardBuilder.buildCards(result.Items, { itemsContainer: elem, shape: getPortraitShape(), scalable: true, + showTitle: true, + centerText: true, + showYear: true, overlayMoreButton: true, allowBottomPadding: false }); } if (result.Items.length >= query.Limit) { - tabContent.querySelector(".btnMoreFromGenre" + id + " i").classList.remove("hide"); + tabContent.querySelector('.btnMoreFromGenre' + id + ' .material-icons').classList.remove('hide'); } }); } @@ -126,8 +130,8 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader function reloadItems(context, promise) { var query = getQuery(); promise.then(function (result) { - var elem = context.querySelector("#items"); - var html = ""; + var elem = context.querySelector('#items'); + var html = ''; var items = result.Items; for (var i = 0, length = items.length; i < length; i++) { @@ -135,26 +139,26 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader html += '
'; html += '"; + html += ''; + html += ''; + html += ''; + html += '
'; if (enableScrollX()) { - var scrollXClass = "scrollX hiddenScrollX"; + var scrollXClass = 'scrollX hiddenScrollX'; if (layoutManager.tv) { - scrollXClass += "smoothScrollX padded-top-focusscale padded-bottom-focusscale"; + scrollXClass += 'smoothScrollX padded-top-focusscale padded-bottom-focusscale'; } html += '
'; } else { html += '
'; } - html += "
"; - html += "
"; + html += ''; + html += ''; } elem.innerHTML = html; @@ -173,16 +177,16 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader var data = {}; self.getViewStyles = function () { - return "Poster,PosterCard,Thumb,ThumbCard".split(","); + return 'Poster,PosterCard,Thumb,ThumbCard'.split(','); }; self.getCurrentViewStyle = function () { - return getPageData(tabContent).view; + return getPageData().view; }; self.setCurrentViewStyle = function (viewStyle) { - getPageData(tabContent).view = viewStyle; - libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle); + getPageData().view = viewStyle; + libraryBrowser.saveViewSetting(getSavedQueryKey(), viewStyle); fullyReload(); }; diff --git a/src/controllers/shows/tvlatest.js b/src/controllers/shows/tvlatest.js index 5862fce45d..08e420a595 100644 --- a/src/controllers/shows/tvlatest.js +++ b/src/controllers/shows/tvlatest.js @@ -1,28 +1,28 @@ -define(["loading", "components/groupedcards", "cardBuilder", "apphost", "imageLoader"], function (loading, groupedcards, cardBuilder, appHost, imageLoader) { - "use strict"; +define(['loading', 'components/groupedcards', 'cardBuilder', 'apphost', 'imageLoader'], function (loading, groupedcards, cardBuilder, appHost, imageLoader) { + 'use strict'; function getLatestPromise(context, params) { loading.show(); var userId = ApiClient.getCurrentUserId(); var parentId = params.topParentId; var options = { - IncludeItemTypes: "Episode", + IncludeItemTypes: 'Episode', Limit: 30, - Fields: "PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo', ParentId: parentId, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Thumb" + EnableImageTypes: 'Primary,Backdrop,Thumb' }; - return ApiClient.getJSON(ApiClient.getUrl("Users/" + userId + "/Items/Latest", options)); + return ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)); } function loadLatest(context, params, promise) { promise.then(function (items) { - var html = ""; - appHost.supports("imageanalysis"); + var html = ''; + appHost.supports('imageanalysis'); html += cardBuilder.getCardsHtml({ items: items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, showTitle: true, showSeriesYear: true, @@ -36,12 +36,12 @@ define(["loading", "components/groupedcards", "cardBuilder", "apphost", "imageLo overlayPlayButton: true, lines: 2 }); - var elem = context.querySelector("#latestEpisodes"); + var elem = context.querySelector('#latestEpisodes'); elem.innerHTML = html; imageLoader.lazyChildren(elem); loading.hide(); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(context); }); }); @@ -59,6 +59,6 @@ define(["loading", "components/groupedcards", "cardBuilder", "apphost", "imageLo loadLatest(tabContent, params, latestPromise); }; - tabContent.querySelector("#latestEpisodes").addEventListener("click", groupedcards.onItemsContainerClick); + tabContent.querySelector('#latestEpisodes').addEventListener('click', groupedcards.onItemsContainerClick); }; }); diff --git a/src/controllers/shows/tvrecommended.js b/src/controllers/shows/tvrecommended.js index d1adb04342..8087a03096 100644 --- a/src/controllers/shows/tvrecommended.js +++ b/src/controllers/shows/tvrecommended.js @@ -1,39 +1,39 @@ -define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "dom", "userSettings", "cardBuilder", "playbackManager", "mainTabsManager", "scrollStyles", "emby-itemscontainer", "emby-button"], function (events, inputManager, libraryMenu, layoutManager, loading, dom, userSettings, cardBuilder, playbackManager, mainTabsManager) { - "use strict"; +define(['events', 'inputManager', 'libraryMenu', 'layoutManager', 'loading', 'dom', 'userSettings', 'cardBuilder', 'playbackManager', 'mainTabsManager', 'globalize', 'scrollStyles', 'emby-itemscontainer', 'emby-button'], function (events, inputManager, libraryMenu, layoutManager, loading, dom, userSettings, cardBuilder, playbackManager, mainTabsManager, globalize) { + 'use strict'; function getTabs() { return [{ - name: Globalize.translate("TabShows") + name: globalize.translate('TabShows') }, { - name: Globalize.translate("TabSuggestions") + name: globalize.translate('TabSuggestions') }, { - name: Globalize.translate("TabLatest") + name: globalize.translate('TabLatest') }, { - name: Globalize.translate("TabUpcoming") + name: globalize.translate('TabUpcoming') }, { - name: Globalize.translate("TabGenres") + name: globalize.translate('TabGenres') }, { - name: Globalize.translate("TabNetworks") + name: globalize.translate('TabNetworks') }, { - name: Globalize.translate("TabEpisodes") + name: globalize.translate('TabEpisodes') }, { - name: Globalize.translate("ButtonSearch"), - cssClass: "searchTabButton" + name: globalize.translate('ButtonSearch'), + cssClass: 'searchTabButton' }]; } function getDefaultTabIndex(folderId) { - switch (userSettings.get("landing-" + folderId)) { - case "suggestions": + switch (userSettings.get('landing-' + folderId)) { + case 'suggestions': return 1; - case "latest": + case 'latest': return 2; - case "favorites": + case 'favorites': return 1; - case "genres": + case 'genres': return 4; default: @@ -43,19 +43,19 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do function setScrollClasses(elem, scrollX) { if (scrollX) { - elem.classList.add("hiddenScrollX"); + elem.classList.add('hiddenScrollX'); if (layoutManager.tv) { - elem.classList.add("smoothScrollX"); + elem.classList.add('smoothScrollX'); } - elem.classList.add("scrollX"); - elem.classList.remove("vertical-wrap"); + elem.classList.add('scrollX'); + elem.classList.remove('vertical-wrap'); } else { - elem.classList.remove("hiddenScrollX"); - elem.classList.remove("smoothScrollX"); - elem.classList.remove("scrollX"); - elem.classList.add("vertical-wrap"); + elem.classList.remove('hiddenScrollX'); + elem.classList.remove('smoothScrollX'); + elem.classList.remove('scrollX'); + elem.classList.add('vertical-wrap'); } } @@ -69,25 +69,25 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do function loadNextUp() { var query = { Limit: 24, - Fields: "PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo', UserId: ApiClient.getCurrentUserId(), ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Thumb", + EnableImageTypes: 'Primary,Backdrop,Thumb', EnableTotalRecordCount: false }; query.ParentId = libraryMenu.getTopParentId(); ApiClient.getNextUpEpisodes(query).then(function (result) { if (result.Items.length) { - view.querySelector(".noNextUpItems").classList.add("hide"); + view.querySelector('.noNextUpItems').classList.add('hide'); } else { - view.querySelector(".noNextUpItems").classList.remove("hide"); + view.querySelector('.noNextUpItems').classList.remove('hide'); } - var container = view.querySelector("#nextUpItems"); + var container = view.querySelector('#nextUpItems'); cardBuilder.buildCards(result.Items, { itemsContainer: container, preferThumb: true, - shape: "backdrop", + shape: 'backdrop', scalable: true, showTitle: true, showParentTitle: true, @@ -98,7 +98,7 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do }); loading.hide(); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(view); }); }); @@ -109,7 +109,7 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do } function getThumbShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop"; + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function loadResume() { @@ -117,28 +117,28 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do var screenWidth = dom.getWindowSize().innerWidth; var limit = screenWidth >= 1600 ? 5 : 6; var options = { - SortBy: "DatePlayed", - SortOrder: "Descending", - IncludeItemTypes: "Episode", - Filters: "IsResumable", + SortBy: 'DatePlayed', + SortOrder: 'Descending', + IncludeItemTypes: 'Episode', + Filters: 'IsResumable', Limit: limit, Recursive: true, - Fields: "PrimaryImageAspectRatio,SeriesInfo,UserData,BasicSyncInfo", - ExcludeLocationTypes: "Virtual", + Fields: 'PrimaryImageAspectRatio,SeriesInfo,UserData,BasicSyncInfo', + ExcludeLocationTypes: 'Virtual', ParentId: parentId, ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Thumb", + EnableImageTypes: 'Primary,Backdrop,Thumb', EnableTotalRecordCount: false }; ApiClient.getItems(ApiClient.getCurrentUserId(), options).then(function (result) { if (result.Items.length) { - view.querySelector("#resumableSection").classList.remove("hide"); + view.querySelector('#resumableSection').classList.remove('hide'); } else { - view.querySelector("#resumableSection").classList.add("hide"); + view.querySelector('#resumableSection').classList.add('hide'); } var allowBottomPadding = !enableScrollX(); - var container = view.querySelector("#resumableItems"); + var container = view.querySelector('#resumableItems'); cardBuilder.buildCards(result.Items, { itemsContainer: container, preferThumb: true, @@ -165,7 +165,7 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do } function getTabContainers() { - return view.querySelectorAll(".pageTabContent"); + return view.querySelectorAll('.pageTabContent'); } function initTabs() { @@ -177,34 +177,34 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do switch (index) { case 0: - depends.push("controllers/shows/tvshows"); + depends.push('controllers/shows/tvshows'); break; case 1: break; case 2: - depends.push("controllers/shows/tvlatest"); + depends.push('controllers/shows/tvlatest'); break; case 3: - depends.push("controllers/shows/tvupcoming"); + depends.push('controllers/shows/tvupcoming'); break; case 4: - depends.push("controllers/shows/tvgenres"); + depends.push('controllers/shows/tvgenres'); break; case 5: - depends.push("controllers/shows/tvstudios"); + depends.push('controllers/shows/tvstudios'); break; case 6: - depends.push("controllers/shows/episodes"); + depends.push('controllers/shows/episodes'); break; case 7: - depends.push("scripts/searchtab"); + depends.push('scripts/searchtab'); } require(depends, function (controllerFactory) { @@ -224,7 +224,7 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do controller = self; } else if (index === 7) { controller = new controllerFactory(view, tabContent, { - collectionType: "tvshows", + collectionType: 'tvshows', parentId: params.topParentId }); } else { @@ -263,7 +263,7 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do } function onPlaybackStop(e, state) { - if (state.NowPlayingItem && state.NowPlayingItem.MediaType == "Video") { + if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { renderedTabs = []; mainTabsManager.getTabsElement().triggerTabChange(); } @@ -272,16 +272,16 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do function onWebSocketMessage(e, data) { var msg = data; - if (msg.MessageType === "UserDataChanged" && msg.Data.UserId == ApiClient.getCurrentUserId()) { + if (msg.MessageType === 'UserDataChanged' && msg.Data.UserId == ApiClient.getCurrentUserId()) { renderedTabs = []; } } function onInputCommand(e) { switch (e.detail.command) { - case "search": + case 'search': e.preventDefault(); - Dashboard.navigate("search.html?collectionType=tv&parentId=" + params.topParentId); + Dashboard.navigate('search.html?collectionType=tv&parentId=' + params.topParentId); } } @@ -292,7 +292,7 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do self.initTab = function () { var tabContent = self.tabContent; - setScrollClasses(tabContent.querySelector("#resumableItems"), enableScrollX()); + setScrollClasses(tabContent.querySelector('#resumableItems'), enableScrollX()); }; self.renderTab = function () { @@ -301,34 +301,34 @@ define(["events", "inputManager", "libraryMenu", "layoutManager", "loading", "do var tabControllers = []; var renderedTabs = []; - setScrollClasses(view.querySelector("#resumableItems"), enableScrollX()); - view.addEventListener("viewshow", function (e) { + setScrollClasses(view.querySelector('#resumableItems'), enableScrollX()); + view.addEventListener('viewshow', function (e) { isViewRestored = e.detail.isRestored; initTabs(); - if (!view.getAttribute("data-title")) { + if (!view.getAttribute('data-title')) { var parentId = params.topParentId; if (parentId) { ApiClient.getItem(ApiClient.getCurrentUserId(), parentId).then(function (item) { - view.setAttribute("data-title", item.Name); + view.setAttribute('data-title', item.Name); libraryMenu.setTitle(item.Name); }); } else { - view.setAttribute("data-title", Globalize.translate("TabShows")); - libraryMenu.setTitle(Globalize.translate("TabShows")); + view.setAttribute('data-title', globalize.translate('TabShows')); + libraryMenu.setTitle(globalize.translate('TabShows')); } } - events.on(playbackManager, "playbackstop", onPlaybackStop); - events.on(ApiClient, "message", onWebSocketMessage); + events.on(playbackManager, 'playbackstop', onPlaybackStop); + events.on(ApiClient, 'message', onWebSocketMessage); inputManager.on(window, onInputCommand); }); - view.addEventListener("viewbeforehide", function (e) { + view.addEventListener('viewbeforehide', function (e) { inputManager.off(window, onInputCommand); - events.off(playbackManager, "playbackstop", onPlaybackStop); - events.off(ApiClient, "message", onWebSocketMessage); + events.off(playbackManager, 'playbackstop', onPlaybackStop); + events.off(ApiClient, 'message', onWebSocketMessage); }); - view.addEventListener("viewdestroy", function (e) { + view.addEventListener('viewdestroy', function (e) { tabControllers.forEach(function (t) { if (t.destroy) { t.destroy(); diff --git a/src/controllers/shows/tvshows.js b/src/controllers/shows/tvshows.js index 24f6cb1a03..0bd5e4b52e 100644 --- a/src/controllers/shows/tvshows.js +++ b/src/controllers/shows/tvshows.js @@ -1,5 +1,5 @@ -define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "listView", "cardBuilder", "alphaPicker", "emby-itemscontainer"], function (layoutManager, loading, events, libraryBrowser, imageLoader, listView, cardBuilder, alphaPicker) { - "use strict"; +define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardBuilder', 'alphaPicker', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, listView, cardBuilder, alphaPicker, userSettings, globalize) { + 'use strict'; return function (view, params, tabContent) { function getPageData(context) { @@ -9,18 +9,22 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " if (!pageData) { pageData = data[key] = { query: { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Series", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Series', Recursive: true, - Fields: "PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'PrimaryImageAspectRatio,BasicSyncInfo', ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", - StartIndex: 0, - Limit: pageSize + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', + StartIndex: 0 }, - view: libraryBrowser.getSavedView(key) || "Poster" + view: libraryBrowser.getSavedView(key) || 'Poster' }; + + if (userSettings.libraryPageSize() > 0) { + pageData.query['Limit'] = userSettings.libraryPageSize(); + } + pageData.query.ParentId = params.topParentId; libraryBrowser.loadSavedQueryValues(key, pageData.query); } @@ -34,7 +38,7 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " function getSavedQueryKey(context) { if (!context.savedQueryKey) { - context.savedQueryKey = libraryBrowser.getSavedQueryKey("series"); + context.savedQueryKey = libraryBrowser.getSavedQueryKey('series'); } return context.savedQueryKey; @@ -42,17 +46,17 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " function onViewStyleChange() { var viewStyle = self.getCurrentViewStyle(); - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); - if ("List" == viewStyle) { - itemsContainer.classList.add("vertical-list"); - itemsContainer.classList.remove("vertical-wrap"); + if ('List' == viewStyle) { + itemsContainer.classList.add('vertical-list'); + itemsContainer.classList.remove('vertical-wrap'); } else { - itemsContainer.classList.remove("vertical-list"); - itemsContainer.classList.add("vertical-wrap"); + itemsContainer.classList.remove('vertical-list'); + itemsContainer.classList.add('vertical-wrap'); } - itemsContainer.innerHTML = ""; + itemsContainer.innerHTML = ''; } function reloadItems(page) { @@ -65,7 +69,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " return; } - query.StartIndex += query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } reloadItems(tabContent); } @@ -74,7 +80,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " return; } - query.StartIndex -= query.Limit; + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } reloadItems(tabContent); } @@ -92,45 +100,45 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " filterButton: false }); var viewStyle = self.getCurrentViewStyle(); - if (viewStyle == "Thumb") { + if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, - context: "tvshows", + context: 'tvshows', overlayMoreButton: true, showTitle: true, centerText: true }); - } else if (viewStyle == "ThumbCard") { + } else if (viewStyle == 'ThumbCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, - context: "tvshows", + context: 'tvshows', cardLayout: true, showTitle: true, showYear: true, centerText: true }); - } else if (viewStyle == "Banner") { + } else if (viewStyle == 'Banner') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "banner", + shape: 'banner', preferBanner: true, - context: "tvshows" + context: 'tvshows' }); - } else if (viewStyle == "List") { + } else if (viewStyle == 'List') { html = listView.getListViewHtml({ items: result.Items, - context: "tvshows", + context: 'tvshows', sortBy: query.SortBy }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "portrait", - context: "tvshows", + shape: 'portrait', + context: 'tvshows', showTitle: true, showYear: true, centerText: true, @@ -139,8 +147,8 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " } else { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "portrait", - context: "tvshows", + shape: 'portrait', + context: 'tvshows', centerText: true, lazy: true, overlayMoreButton: true, @@ -150,30 +158,30 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " } var i; var length; - var elems = tabContent.querySelectorAll(".paging"); + var elems = tabContent.querySelectorAll('.paging'); for (i = 0, length = elems.length; i < length; i++) { elems[i].innerHTML = pagingHtml; } - elems = tabContent.querySelectorAll(".btnNextPage"); + elems = tabContent.querySelectorAll('.btnNextPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onNextPageClick); + elems[i].addEventListener('click', onNextPageClick); } - elems = tabContent.querySelectorAll(".btnPreviousPage"); + elems = tabContent.querySelectorAll('.btnPreviousPage'); for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener("click", onPreviousPageClick); + elems[i].addEventListener('click', onPreviousPageClick); } - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var itemsContainer = tabContent.querySelector('.itemsContainer'); itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); libraryBrowser.saveQueryValues(getSavedQueryKey(page), query); loading.hide(); isLoading = false; - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(page); }); }); @@ -185,18 +193,17 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " } var self = this; - var pageSize = 100; var data = {}; var isLoading = false; self.showFilterMenu = function () { - require(["components/filterdialog/filterdialog"], function (filterDialogFactory) { + require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { var filterDialog = new filterDialogFactory({ query: getQuery(tabContent), - mode: "series", + mode: 'series', serverId: ApiClient.serverId() }); - events.on(filterDialog, "filterchange", function () { + events.on(filterDialog, 'filterchange', function () { getQuery(tabContent).StartIndex = 0; reloadItems(tabContent); }); @@ -209,10 +216,10 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " }; function initPage(tabContent) { - var alphaPickerElement = tabContent.querySelector(".alphaPicker"); - var itemsContainer = tabContent.querySelector(".itemsContainer"); + var alphaPickerElement = tabContent.querySelector('.alphaPicker'); + var itemsContainer = tabContent.querySelector('.itemsContainer'); - alphaPickerElement.addEventListener("alphavaluechanged", function (e) { + alphaPickerElement.addEventListener('alphavaluechanged', function (e) { var newValue = e.detail.value; var query = getQuery(tabContent); query.NameStartsWithOrGreater = newValue; @@ -221,36 +228,36 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " }); self.alphaPicker = new alphaPicker({ element: alphaPickerElement, - valueChangeEvent: "click" + valueChangeEvent: 'click' }); - tabContent.querySelector(".alphaPicker").classList.add("alphabetPicker-right"); - alphaPickerElement.classList.add("alphaPicker-fixed-right"); - itemsContainer.classList.add("padded-right-withalphapicker"); + tabContent.querySelector('.alphaPicker').classList.add('alphabetPicker-right'); + alphaPickerElement.classList.add('alphaPicker-fixed-right'); + itemsContainer.classList.add('padded-right-withalphapicker'); - tabContent.querySelector(".btnFilter").addEventListener("click", function () { + tabContent.querySelector('.btnFilter').addEventListener('click', function () { self.showFilterMenu(); }); - tabContent.querySelector(".btnSort").addEventListener("click", function (e) { + tabContent.querySelector('.btnSort').addEventListener('click', function (e) { libraryBrowser.showSortMenu({ items: [{ - name: Globalize.translate("OptionNameSort"), - id: "SortName" + name: globalize.translate('OptionNameSort'), + id: 'SortName' }, { - name: Globalize.translate("OptionImdbRating"), - id: "CommunityRating,SortName" + name: globalize.translate('OptionImdbRating'), + id: 'CommunityRating,SortName' }, { - name: Globalize.translate("OptionDateAdded"), - id: "DateCreated,SortName" + name: globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName' }, { - name: Globalize.translate("OptionDatePlayed"), - id: "DatePlayed,SortName" + name: globalize.translate('OptionDatePlayed'), + id: 'DatePlayed,SortName' }, { - name: Globalize.translate("OptionParentalRating"), - id: "OfficialRating,SortName" + name: globalize.translate('OptionParentalRating'), + id: 'OfficialRating,SortName' }, { - name: Globalize.translate("OptionReleaseDate"), - id: "PremiereDate,SortName" + name: globalize.translate('OptionReleaseDate'), + id: 'PremiereDate,SortName' }], callback: function () { getQuery(tabContent).StartIndex = 0; @@ -260,11 +267,11 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", " button: e.target }); }); - var btnSelectView = tabContent.querySelector(".btnSelectView"); - btnSelectView.addEventListener("click", function (e) { - libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), "Banner,List,Poster,PosterCard,Thumb,ThumbCard".split(",")); + var btnSelectView = tabContent.querySelector('.btnSelectView'); + btnSelectView.addEventListener('click', function (e) { + libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(',')); }); - btnSelectView.addEventListener("layoutchange", function (e) { + btnSelectView.addEventListener('layoutchange', function (e) { var viewStyle = e.detail.viewStyle; getPageData(tabContent).view = viewStyle; libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle); diff --git a/src/controllers/shows/tvstudios.js b/src/controllers/shows/tvstudios.js index 4e715e1fd7..1051bfa10b 100644 --- a/src/controllers/shows/tvstudios.js +++ b/src/controllers/shows/tvstudios.js @@ -1,5 +1,5 @@ -define(["loading", "libraryBrowser", "cardBuilder", "apphost"], function (loading, libraryBrowser, cardBuilder, appHost) { - "use strict"; +define(['loading', 'libraryBrowser', 'cardBuilder', 'apphost'], function (loading, libraryBrowser, cardBuilder, appHost) { + 'use strict'; function getQuery(params) { var key = getSavedQueryKey(); @@ -8,11 +8,11 @@ define(["loading", "libraryBrowser", "cardBuilder", "apphost"], function (loadin if (!pageData) { pageData = data[key] = { query: { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Series", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Series', Recursive: true, - Fields: "DateCreated,PrimaryImageAspectRatio", + Fields: 'DateCreated,PrimaryImageAspectRatio', StartIndex: 0 } }; @@ -23,7 +23,7 @@ define(["loading", "libraryBrowser", "cardBuilder", "apphost"], function (loadin } function getSavedQueryKey() { - return libraryBrowser.getSavedQueryKey("studios"); + return libraryBrowser.getSavedQueryKey('studios'); } function getPromise(context, params) { @@ -34,20 +34,20 @@ define(["loading", "libraryBrowser", "cardBuilder", "apphost"], function (loadin function reloadItems(context, params, promise) { promise.then(function (result) { - var elem = context.querySelector("#items"); + var elem = context.querySelector('#items'); cardBuilder.buildCards(result.Items, { itemsContainer: elem, - shape: "backdrop", + shape: 'backdrop', preferThumb: true, showTitle: true, scalable: true, centerText: true, overlayMoreButton: true, - context: "tvshows" + context: 'tvshows' }); loading.hide(); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(context); }); }); diff --git a/src/controllers/shows/tvupcoming.js b/src/controllers/shows/tvupcoming.js index 249d932d39..d57a5ae3cd 100644 --- a/src/controllers/shows/tvupcoming.js +++ b/src/controllers/shows/tvupcoming.js @@ -1,18 +1,18 @@ -define(["layoutManager", "loading", "datetime", "libraryBrowser", "cardBuilder", "apphost", "imageLoader", "scrollStyles", "emby-itemscontainer"], function (layoutManager, loading, datetime, libraryBrowser, cardBuilder, appHost, imageLoader) { - "use strict"; +define(['layoutManager', 'loading', 'datetime', 'libraryBrowser', 'cardBuilder', 'apphost', 'imageLoader', 'globalize', 'scrollStyles', 'emby-itemscontainer'], function (layoutManager, loading, datetime, libraryBrowser, cardBuilder, appHost, imageLoader, globalize) { + 'use strict'; function getUpcomingPromise(context, params) { loading.show(); var query = { Limit: 48, - Fields: "AirTime,UserData", + Fields: 'AirTime,UserData', UserId: ApiClient.getCurrentUserId(), ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', EnableTotalRecordCount: false }; query.ParentId = params.topParentId; - return ApiClient.getJSON(ApiClient.getUrl("Shows/Upcoming", query)); + return ApiClient.getJSON(ApiClient.getUrl('Shows/Upcoming', query)); } function loadUpcoming(context, params, promise) { @@ -20,12 +20,12 @@ define(["layoutManager", "loading", "datetime", "libraryBrowser", "cardBuilder", var items = result.Items; if (items.length) { - context.querySelector(".noItemsMessage").style.display = "none"; + context.querySelector('.noItemsMessage').style.display = 'none'; } else { - context.querySelector(".noItemsMessage").style.display = "block"; + context.querySelector('.noItemsMessage').style.display = 'block'; } - renderUpcoming(context.querySelector("#upcomingItems"), items); + renderUpcoming(context.querySelector('#upcomingItems'), items); loading.hide(); }); } @@ -35,27 +35,27 @@ define(["layoutManager", "loading", "datetime", "libraryBrowser", "cardBuilder", } function getThumbShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop"; + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function renderUpcoming(elem, items) { var i; var length; var groups = []; - var currentGroupName = ""; + var currentGroupName = ''; var currentGroup = []; for (i = 0, length = items.length; i < length; i++) { var item = items[i]; - var dateText = ""; + var dateText = ''; if (item.PremiereDate) { try { var premiereDate = datetime.parseISO8601Date(item.PremiereDate, true); - dateText = datetime.isRelativeDay(premiereDate, -1) ? Globalize.translate("Yesterday") : datetime.toLocaleDateString(premiereDate, { - weekday: "long", - month: "short", - day: "numeric" + dateText = datetime.isRelativeDay(premiereDate, -1) ? globalize.translate('Yesterday') : datetime.toLocaleDateString(premiereDate, { + weekday: 'long', + month: 'short', + day: 'numeric' }); } catch (err) { console.error('error parsing timestamp for upcoming tv shows'); @@ -77,20 +77,20 @@ define(["layoutManager", "loading", "datetime", "libraryBrowser", "cardBuilder", } } - var html = ""; + var html = ''; for (i = 0, length = groups.length; i < length; i++) { var group = groups[i]; html += '
'; - html += '

' + group.name + "

"; + html += '

' + group.name + '

'; var allowBottomPadding = true; if (enableScrollX()) { allowBottomPadding = false; - var scrollXClass = "scrollX hiddenScrollX"; + var scrollXClass = 'scrollX hiddenScrollX'; if (layoutManager.tv) { - scrollXClass += " smoothScrollX"; + scrollXClass += ' smoothScrollX'; } html += '
'; @@ -98,7 +98,7 @@ define(["layoutManager", "loading", "datetime", "libraryBrowser", "cardBuilder", html += '
'; } - var supportsImageAnalysis = appHost.supports("imageanalysis"); + var supportsImageAnalysis = appHost.supports('imageanalysis'); supportsImageAnalysis = false; html += cardBuilder.getCardsHtml({ items: group.items, @@ -116,8 +116,8 @@ define(["layoutManager", "loading", "datetime", "libraryBrowser", "cardBuilder", overlayMoreButton: true, missingIndicator: false }); - html += "
"; - html += "
"; + html += '
'; + html += ''; } elem.innerHTML = html; diff --git a/src/controllers/streamingsettings.js b/src/controllers/streamingsettings.js deleted file mode 100644 index 14e5e028a8..0000000000 --- a/src/controllers/streamingsettings.js +++ /dev/null @@ -1,43 +0,0 @@ -define(["jQuery", "libraryMenu", "loading"], function ($, libraryMenu, loading) { - "use strict"; - - function loadPage(page, config) { - $("#txtRemoteClientBitrateLimit", page).val(config.RemoteClientBitrateLimit / 1e6 || ""); - loading.hide(); - } - - function onSubmit() { - loading.show(); - var form = this; - ApiClient.getServerConfiguration().then(function (config) { - config.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($("#txtRemoteClientBitrateLimit", form).val() || "0")); - ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult); - }); - - return false; - } - - function getTabs() { - return [{ - href: "encodingsettings.html", - name: Globalize.translate("Transcoding") - }, { - href: "playbackconfiguration.html", - name: Globalize.translate("TabResumeSettings") - }, { - href: "streamingsettings.html", - name: Globalize.translate("TabStreaming") - }]; - } - - $(document).on("pageinit", "#streamingSettingsPage", function () { - $(".streamingSettingsForm").off("submit", onSubmit).on("submit", onSubmit); - }).on("pageshow", "#streamingSettingsPage", function () { - loading.show(); - libraryMenu.setTabs("playback", 2, getTabs); - var page = this; - ApiClient.getServerConfiguration().then(function (config) { - loadPage(page, config); - }); - }); -}); diff --git a/src/controllers/user/display.js b/src/controllers/user/display.js index 28d39b7fde..3aeb7db8ce 100644 --- a/src/controllers/user/display.js +++ b/src/controllers/user/display.js @@ -1,10 +1,10 @@ -define(["displaySettings", "userSettings", "autoFocuser"], function (DisplaySettings, userSettings, autoFocuser) { - "use strict"; +define(['displaySettings', 'userSettings', 'autoFocuser'], function (DisplaySettings, userSettings, autoFocuser) { + 'use strict'; return function (view, params) { function onBeforeUnload(e) { if (hasChanges) { - e.returnValue = "You currently have unsaved changes. Are you sure you wish to leave?"; + e.returnValue = 'You currently have unsaved changes. Are you sure you wish to leave?'; } } @@ -12,8 +12,8 @@ define(["displaySettings", "userSettings", "autoFocuser"], function (DisplaySett var hasChanges; var userId = params.userId || ApiClient.getCurrentUserId(); var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings(); - view.addEventListener("viewshow", function () { - window.addEventListener("beforeunload", onBeforeUnload); + view.addEventListener('viewshow', function () { + window.addEventListener('beforeunload', onBeforeUnload); if (settingsInstance) { settingsInstance.loadData(); @@ -21,7 +21,7 @@ define(["displaySettings", "userSettings", "autoFocuser"], function (DisplaySett settingsInstance = new DisplaySettings({ serverId: ApiClient.serverId(), userId: userId, - element: view.querySelector(".settingsContainer"), + element: view.querySelector('.settingsContainer'), userSettings: currentSettings, enableSaveButton: false, enableSaveConfirmation: false, @@ -29,18 +29,18 @@ define(["displaySettings", "userSettings", "autoFocuser"], function (DisplaySett }); } }); - view.addEventListener("change", function () { + view.addEventListener('change', function () { hasChanges = true; }); - view.addEventListener("viewbeforehide", function () { - window.removeEventListener("beforeunload", onBeforeUnload); + view.addEventListener('viewbeforehide', function () { + window.removeEventListener('beforeunload', onBeforeUnload); hasChanges = false; if (settingsInstance) { settingsInstance.submit(); } }); - view.addEventListener("viewdestroy", function () { + view.addEventListener('viewdestroy', function () { if (settingsInstance) { settingsInstance.destroy(); settingsInstance = null; diff --git a/src/controllers/user/home.js b/src/controllers/user/home.js index dccf6e5060..aa7d147c31 100644 --- a/src/controllers/user/home.js +++ b/src/controllers/user/home.js @@ -1,10 +1,10 @@ -define(["homescreenSettings", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (HomescreenSettings, dom, globalize, loading, userSettings, autoFocuser) { - "use strict"; +define(['homescreenSettings', 'dom', 'globalize', 'loading', 'userSettings', 'autoFocuser', 'listViewStyle'], function (HomescreenSettings, dom, globalize, loading, userSettings, autoFocuser) { + 'use strict'; return function (view, params) { function onBeforeUnload(e) { if (hasChanges) { - e.returnValue = "You currently have unsaved changes. Are you sure you wish to leave?"; + e.returnValue = 'You currently have unsaved changes. Are you sure you wish to leave?'; } } @@ -12,8 +12,8 @@ define(["homescreenSettings", "dom", "globalize", "loading", "userSettings", "au var hasChanges; var userId = params.userId || ApiClient.getCurrentUserId(); var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings(); - view.addEventListener("viewshow", function () { - window.addEventListener("beforeunload", onBeforeUnload); + view.addEventListener('viewshow', function () { + window.addEventListener('beforeunload', onBeforeUnload); if (homescreenSettingsInstance) { homescreenSettingsInstance.loadData(); @@ -21,7 +21,7 @@ define(["homescreenSettings", "dom", "globalize", "loading", "userSettings", "au homescreenSettingsInstance = new HomescreenSettings({ serverId: ApiClient.serverId(), userId: userId, - element: view.querySelector(".homeScreenSettingsContainer"), + element: view.querySelector('.homeScreenSettingsContainer'), userSettings: currentSettings, enableSaveButton: false, enableSaveConfirmation: false, @@ -29,17 +29,17 @@ define(["homescreenSettings", "dom", "globalize", "loading", "userSettings", "au }); } }); - view.addEventListener("change", function () { + view.addEventListener('change', function () { hasChanges = true; }); - view.addEventListener("viewbeforehide", function () { + view.addEventListener('viewbeforehide', function () { hasChanges = false; if (homescreenSettingsInstance) { homescreenSettingsInstance.submit(); } }); - view.addEventListener("viewdestroy", function () { + view.addEventListener('viewdestroy', function () { if (homescreenSettingsInstance) { homescreenSettingsInstance.destroy(); homescreenSettingsInstance = null; diff --git a/src/controllers/user/menu.js b/src/controllers/user/menu.js index e7d816fb6f..df5864dbdb 100644 --- a/src/controllers/user/menu.js +++ b/src/controllers/user/menu.js @@ -1,62 +1,62 @@ -define(["apphost", "connectionManager", "layoutManager", "listViewStyle", "emby-button"], function(appHost, connectionManager, layoutManager) { - "use strict"; +define(['apphost', 'connectionManager', 'layoutManager', 'listViewStyle', 'emby-button'], function(appHost, connectionManager, layoutManager) { + 'use strict'; return function(view, params) { - view.querySelector(".btnLogout").addEventListener("click", function() { + view.querySelector('.btnLogout').addEventListener('click', function() { Dashboard.logout(); }); - view.querySelector(".selectServer").addEventListener("click", function () { + view.querySelector('.selectServer').addEventListener('click', function () { Dashboard.selectServer(); }); - view.querySelector(".clientSettings").addEventListener("click", function () { + view.querySelector('.clientSettings').addEventListener('click', function () { window.NativeShell.openClientSettings(); }); - view.addEventListener("viewshow", function() { + view.addEventListener('viewshow', function() { // this page can also be used by admins to change user preferences from the user edit page var userId = params.userId || Dashboard.getCurrentUserId(); var page = this; - page.querySelector(".lnkMyProfile").setAttribute("href", "myprofile.html?userId=" + userId); - page.querySelector(".lnkDisplayPreferences").setAttribute("href", "mypreferencesdisplay.html?userId=" + userId); - page.querySelector(".lnkHomePreferences").setAttribute("href", "mypreferenceshome.html?userId=" + userId); - page.querySelector(".lnkPlaybackPreferences").setAttribute("href", "mypreferencesplayback.html?userId=" + userId); - page.querySelector(".lnkSubtitlePreferences").setAttribute("href", "mypreferencessubtitles.html?userId=" + userId); + page.querySelector('.lnkMyProfile').setAttribute('href', 'myprofile.html?userId=' + userId); + page.querySelector('.lnkDisplayPreferences').setAttribute('href', 'mypreferencesdisplay.html?userId=' + userId); + page.querySelector('.lnkHomePreferences').setAttribute('href', 'mypreferenceshome.html?userId=' + userId); + page.querySelector('.lnkPlaybackPreferences').setAttribute('href', 'mypreferencesplayback.html?userId=' + userId); + page.querySelector('.lnkSubtitlePreferences').setAttribute('href', 'mypreferencessubtitles.html?userId=' + userId); - if (window.NativeShell && window.NativeShell.AppHost.supports("clientsettings")) { - page.querySelector(".clientSettings").classList.remove("hide"); + if (window.NativeShell && window.NativeShell.AppHost.supports('clientsettings')) { + page.querySelector('.clientSettings').classList.remove('hide'); } else { - page.querySelector(".clientSettings").classList.add("hide"); + page.querySelector('.clientSettings').classList.add('hide'); } - if (appHost.supports("multiserver")) { - page.querySelector(".selectServer").classList.remove("hide"); + if (appHost.supports('multiserver')) { + page.querySelector('.selectServer').classList.remove('hide'); } else { - page.querySelector(".selectServer").classList.add("hide"); + page.querySelector('.selectServer').classList.add('hide'); } // hide the actions if user preferences are being edited for a different user if (params.userId && params.userId !== Dashboard.getCurrentUserId) { - page.querySelector(".userSection").classList.add("hide"); - page.querySelector(".adminSection").classList.add("hide"); + page.querySelector('.userSection').classList.add('hide'); + page.querySelector('.adminSection').classList.add('hide'); } if (layoutManager.mobile) { - page.querySelector(".headerUsername").classList.add("hide"); - page.querySelector(".adminSection").classList.add("hide"); - page.querySelector(".userSection").classList.add("hide"); + page.querySelector('.headerUsername').classList.add('hide'); + page.querySelector('.adminSection').classList.add('hide'); + page.querySelector('.userSection').classList.add('hide'); } ApiClient.getUser(userId).then(function(user) { - page.querySelector(".headerUsername").innerHTML = user.Name; + page.querySelector('.headerUsername').innerHTML = user.Name; if (!user.Policy.IsAdministrator) { - page.querySelector(".adminSection").classList.add("hide"); + page.querySelector('.adminSection').classList.add('hide'); } }); - require(["autoFocuser"], function (autoFocuser) { + require(['autoFocuser'], function (autoFocuser) { autoFocuser.autoFocus(view); }); }); diff --git a/src/controllers/user/playback.js b/src/controllers/user/playback.js index 8f48e0264b..e945a46aab 100644 --- a/src/controllers/user/playback.js +++ b/src/controllers/user/playback.js @@ -1,10 +1,10 @@ -define(["playbackSettings", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (PlaybackSettings, dom, globalize, loading, userSettings, autoFocuser) { - "use strict"; +define(['playbackSettings', 'dom', 'globalize', 'loading', 'userSettings', 'autoFocuser', 'listViewStyle'], function (PlaybackSettings, dom, globalize, loading, userSettings, autoFocuser) { + 'use strict'; return function (view, params) { function onBeforeUnload(e) { if (hasChanges) { - e.returnValue = "You currently have unsaved changes. Are you sure you wish to leave?"; + e.returnValue = 'You currently have unsaved changes. Are you sure you wish to leave?'; } } @@ -12,8 +12,8 @@ define(["playbackSettings", "dom", "globalize", "loading", "userSettings", "auto var hasChanges; var userId = params.userId || ApiClient.getCurrentUserId(); var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings(); - view.addEventListener("viewshow", function () { - window.addEventListener("beforeunload", onBeforeUnload); + view.addEventListener('viewshow', function () { + window.addEventListener('beforeunload', onBeforeUnload); if (settingsInstance) { settingsInstance.loadData(); @@ -21,7 +21,7 @@ define(["playbackSettings", "dom", "globalize", "loading", "userSettings", "auto settingsInstance = new PlaybackSettings({ serverId: ApiClient.serverId(), userId: userId, - element: view.querySelector(".settingsContainer"), + element: view.querySelector('.settingsContainer'), userSettings: currentSettings, enableSaveButton: false, enableSaveConfirmation: false, @@ -29,17 +29,17 @@ define(["playbackSettings", "dom", "globalize", "loading", "userSettings", "auto }); } }); - view.addEventListener("change", function () { + view.addEventListener('change', function () { hasChanges = true; }); - view.addEventListener("viewbeforehide", function () { + view.addEventListener('viewbeforehide', function () { hasChanges = false; if (settingsInstance) { settingsInstance.submit(); } }); - view.addEventListener("viewdestroy", function () { + view.addEventListener('viewdestroy', function () { if (settingsInstance) { settingsInstance.destroy(); settingsInstance = null; diff --git a/src/controllers/user/profile.js b/src/controllers/user/profile.js index 3b85cb1d8c..fd7d1e32cc 100644 --- a/src/controllers/user/profile.js +++ b/src/controllers/user/profile.js @@ -1,31 +1,31 @@ -define(["controllers/userpasswordpage", "loading", "libraryMenu", "apphost", "emby-button"], function (UserPasswordPage, loading, libraryMenu, appHost) { - "use strict"; +define(['controllers/dashboard/users/userpasswordpage', 'loading', 'libraryMenu', 'apphost', 'globalize', 'emby-button'], function (UserPasswordPage, loading, libraryMenu, appHost, globalize) { + 'use strict'; function reloadUser(page) { - var userId = getParameterByName("userId"); + var userId = getParameterByName('userId'); loading.show(); ApiClient.getUser(userId).then(function (user) { - page.querySelector(".username").innerHTML = user.Name; + page.querySelector('.username').innerHTML = user.Name; libraryMenu.setTitle(user.Name); - var imageUrl = "assets/img/avatar.png"; + var imageUrl = 'assets/img/avatar.png'; if (user.PrimaryImageTag) { imageUrl = ApiClient.getUserImageUrl(user.Id, { tag: user.PrimaryImageTag, - type: "Primary" + type: 'Primary' }); } - var userImage = page.querySelector("#image"); - userImage.style.backgroundImage = "url(" + imageUrl + ")"; + var userImage = page.querySelector('#image'); + userImage.style.backgroundImage = 'url(' + imageUrl + ')'; Dashboard.getCurrentUser().then(function (loggedInUser) { if (user.PrimaryImageTag) { - page.querySelector("#btnAddImage").classList.add("hide"); - page.querySelector("#btnDeleteImage").classList.remove("hide"); - } else if (appHost.supports("fileinput") && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) { - page.querySelector("#btnDeleteImage").classList.add("hide"); - page.querySelector("#btnAddImage").classList.remove("hide"); + page.querySelector('#btnAddImage').classList.add('hide'); + page.querySelector('#btnDeleteImage').classList.remove('hide'); + } else if (appHost.supports('fileinput') && (loggedInUser.Policy.IsAdministrator || user.Policy.EnableUserPreferenceAccess)) { + page.querySelector('#btnDeleteImage').classList.add('hide'); + page.querySelector('#btnAddImage').classList.remove('hide'); } }); loading.hide(); @@ -36,8 +36,8 @@ define(["controllers/userpasswordpage", "loading", "libraryMenu", "apphost", "em loading.hide(); switch (evt.target.error.code) { case evt.target.error.NOT_FOUND_ERR: - require(["toast"], function (toast) { - toast(Globalize.translate("FileNotFound")); + require(['toast'], function (toast) { + toast(globalize.translate('FileNotFound')); }); break; case evt.target.error.ABORT_ERR: @@ -45,24 +45,24 @@ define(["controllers/userpasswordpage", "loading", "libraryMenu", "apphost", "em break; case evt.target.error.NOT_READABLE_ERR: default: - require(["toast"], function (toast) { - toast(Globalize.translate("FileReadError")); + require(['toast'], function (toast) { + toast(globalize.translate('FileReadError')); }); } } function onFileReaderAbort(evt) { loading.hide(); - require(["toast"], function (toast) { - toast(Globalize.translate("FileReadCancelled")); + require(['toast'], function (toast) { + toast(globalize.translate('FileReadCancelled')); }); } function setFiles(page, files) { - var userImage = page.querySelector("#image"); + var userImage = page.querySelector('#image'); var file = files[0]; - if (!file || !file.type.match("image.*")) { + if (!file || !file.type.match('image.*')) { return false; } @@ -70,9 +70,9 @@ define(["controllers/userpasswordpage", "loading", "libraryMenu", "apphost", "em reader.onerror = onFileReaderError; reader.onabort = onFileReaderAbort; reader.onload = function (evt) { - userImage.style.backgroundImage = "url(" + evt.target.result + ")"; - var userId = getParameterByName("userId"); - ApiClient.uploadUserImage(userId, "Primary", file).then(function () { + userImage.style.backgroundImage = 'url(' + evt.target.result + ')'; + var userId = getParameterByName('userId'); + ApiClient.uploadUserImage(userId, 'Primary', file).then(function () { loading.hide(); reloadUser(page); }); @@ -84,22 +84,22 @@ define(["controllers/userpasswordpage", "loading", "libraryMenu", "apphost", "em return function (view, params) { reloadUser(view); new UserPasswordPage(view, params); - view.querySelector("#btnDeleteImage").addEventListener("click", function () { - require(["confirm"], function (confirm) { - confirm(Globalize.translate("DeleteImageConfirmation"), Globalize.translate("DeleteImage")).then(function () { + view.querySelector('#btnDeleteImage').addEventListener('click', function () { + require(['confirm'], function (confirm) { + confirm(globalize.translate('DeleteImageConfirmation'), globalize.translate('DeleteImage')).then(function () { loading.show(); - var userId = getParameterByName("userId"); - ApiClient.deleteUserImage(userId, "primary").then(function () { + var userId = getParameterByName('userId'); + ApiClient.deleteUserImage(userId, 'primary').then(function () { loading.hide(); reloadUser(view); }); }); }); }); - view.querySelector("#btnAddImage").addEventListener("click", function (evt) { - view.querySelector("#uploadImage").click(); + view.querySelector('#btnAddImage').addEventListener('click', function (evt) { + view.querySelector('#uploadImage').click(); }); - view.querySelector("#uploadImage").addEventListener("change", function (evt) { + view.querySelector('#uploadImage').addEventListener('change', function (evt) { setFiles(view, evt.target.files); }); }; diff --git a/src/controllers/user/subtitles.js b/src/controllers/user/subtitles.js index e2b98dc2dd..152301f31a 100644 --- a/src/controllers/user/subtitles.js +++ b/src/controllers/user/subtitles.js @@ -1,10 +1,10 @@ -define(["subtitleSettings", "userSettings", "autoFocuser"], function (SubtitleSettings, userSettings, autoFocuser) { - "use strict"; +define(['subtitleSettings', 'userSettings', 'autoFocuser'], function (SubtitleSettings, userSettings, autoFocuser) { + 'use strict'; return function (view, params) { function onBeforeUnload(e) { if (hasChanges) { - e.returnValue = "You currently have unsaved changes. Are you sure you wish to leave?"; + e.returnValue = 'You currently have unsaved changes. Are you sure you wish to leave?'; } } @@ -12,8 +12,8 @@ define(["subtitleSettings", "userSettings", "autoFocuser"], function (SubtitleSe var hasChanges; var userId = params.userId || ApiClient.getCurrentUserId(); var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings(); - view.addEventListener("viewshow", function () { - window.addEventListener("beforeunload", onBeforeUnload); + view.addEventListener('viewshow', function () { + window.addEventListener('beforeunload', onBeforeUnload); if (subtitleSettingsInstance) { subtitleSettingsInstance.loadData(); @@ -21,7 +21,7 @@ define(["subtitleSettings", "userSettings", "autoFocuser"], function (SubtitleSe subtitleSettingsInstance = new SubtitleSettings({ serverId: ApiClient.serverId(), userId: userId, - element: view.querySelector(".settingsContainer"), + element: view.querySelector('.settingsContainer'), userSettings: currentSettings, enableSaveButton: false, enableSaveConfirmation: false, @@ -29,17 +29,17 @@ define(["subtitleSettings", "userSettings", "autoFocuser"], function (SubtitleSe }); } }); - view.addEventListener("change", function () { + view.addEventListener('change', function () { hasChanges = true; }); - view.addEventListener("viewbeforehide", function () { + view.addEventListener('viewbeforehide', function () { hasChanges = false; if (subtitleSettingsInstance) { subtitleSettingsInstance.submit(); } }); - view.addEventListener("viewdestroy", function () { + view.addEventListener('viewdestroy', function () { if (subtitleSettingsInstance) { subtitleSettingsInstance.destroy(); subtitleSettingsInstance = null; diff --git a/src/controllers/useredit.js b/src/controllers/useredit.js deleted file mode 100644 index f6a5aaf000..0000000000 --- a/src/controllers/useredit.js +++ /dev/null @@ -1,198 +0,0 @@ -define(["jQuery", "loading", "libraryMenu", "fnchecked"], function ($, loading, libraryMenu) { - "use strict"; - - function loadDeleteFolders(page, user, mediaFolders) { - ApiClient.getJSON(ApiClient.getUrl("Channels", { - SupportsMediaDeletion: true - })).then(function (channelsResult) { - var i; - var length; - var folder; - var isChecked; - var checkedAttribute; - var html = ""; - - for (i = 0, length = mediaFolders.length; i < length; i++) { - folder = mediaFolders[i]; - isChecked = user.Policy.EnableContentDeletion || -1 != user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id); - checkedAttribute = isChecked ? ' checked="checked"' : ""; - html += '"; - } - - for (i = 0, length = channelsResult.Items.length; i < length; i++) { - folder = channelsResult.Items[i]; - isChecked = user.Policy.EnableContentDeletion || -1 != user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id); - checkedAttribute = isChecked ? ' checked="checked"' : ""; - html += '"; - } - - $(".deleteAccess", page).html(html).trigger("create"); - $("#chkEnableDeleteAllFolders", page).checked(user.Policy.EnableContentDeletion).trigger("change"); - }); - } - - function loadAuthProviders(page, user, providers) { - if (providers.length > 1) { - page.querySelector(".fldSelectLoginProvider").classList.remove("hide"); - } else { - page.querySelector(".fldSelectLoginProvider").classList.add("hide"); - } - - var currentProviderId = user.Policy.AuthenticationProviderId; - page.querySelector(".selectLoginProvider").innerHTML = providers.map(function (provider) { - var selected = provider.Id === currentProviderId || providers.length < 2 ? " selected" : ""; - return '"; - }); - } - - function loadPasswordResetProviders(page, user, providers) { - if (providers.length > 1) { - page.querySelector(".fldSelectPasswordResetProvider").classList.remove("hide"); - } else { - page.querySelector(".fldSelectPasswordResetProvider").classList.add("hide"); - } - - var currentProviderId = user.Policy.PasswordResetProviderId; - page.querySelector(".selectPasswordResetProvider").innerHTML = providers.map(function (provider) { - var selected = provider.Id === currentProviderId || providers.length < 2 ? " selected" : ""; - return '"; - }); - } - - function loadUser(page, user) { - currentUser = user; - ApiClient.getJSON(ApiClient.getUrl("Auth/Providers")).then(function (providers) { - loadAuthProviders(page, user, providers); - }); - ApiClient.getJSON(ApiClient.getUrl("Auth/PasswordResetProviders")).then(function (providers) { - loadPasswordResetProviders(page, user, providers); - }); - ApiClient.getJSON(ApiClient.getUrl("Library/MediaFolders", { - IsHidden: false - })).then(function (folders) { - loadDeleteFolders(page, user, folders.Items); - }); - - if (user.Policy.IsDisabled) { - $(".disabledUserBanner", page).show(); - } else { - $(".disabledUserBanner", page).hide(); - } - - $("#txtUserName", page).prop("disabled", "").removeAttr("disabled"); - $("#fldConnectInfo", page).show(); - $(".lnkEditUserPreferences", page).attr("href", "mypreferencesmenu.html?userId=" + user.Id); - libraryMenu.setTitle(user.Name); - page.querySelector(".username").innerHTML = user.Name; - $("#txtUserName", page).val(user.Name); - $("#chkIsAdmin", page).checked(user.Policy.IsAdministrator); - $("#chkDisabled", page).checked(user.Policy.IsDisabled); - $("#chkIsHidden", page).checked(user.Policy.IsHidden); - $("#chkRemoteControlSharedDevices", page).checked(user.Policy.EnableSharedDeviceControl); - $("#chkEnableRemoteControlOtherUsers", page).checked(user.Policy.EnableRemoteControlOfOtherUsers); - $("#chkEnableDownloading", page).checked(user.Policy.EnableContentDownloading); - $("#chkManageLiveTv", page).checked(user.Policy.EnableLiveTvManagement); - $("#chkEnableLiveTvAccess", page).checked(user.Policy.EnableLiveTvAccess); - $("#chkEnableMediaPlayback", page).checked(user.Policy.EnableMediaPlayback); - $("#chkEnableAudioPlaybackTranscoding", page).checked(user.Policy.EnableAudioPlaybackTranscoding); - $("#chkEnableVideoPlaybackTranscoding", page).checked(user.Policy.EnableVideoPlaybackTranscoding); - $("#chkEnableVideoPlaybackRemuxing", page).checked(user.Policy.EnablePlaybackRemuxing); - $("#chkForceRemoteSourceTranscoding", page).checked(user.Policy.ForceRemoteSourceTranscoding); - $("#chkRemoteAccess", page).checked(null == user.Policy.EnableRemoteAccess || user.Policy.EnableRemoteAccess); - $("#chkEnableSyncTranscoding", page).checked(user.Policy.EnableSyncTranscoding); - $("#chkEnableConversion", page).checked(user.Policy.EnableMediaConversion || false); - $("#chkEnableSharing", page).checked(user.Policy.EnablePublicSharing); - $("#txtRemoteClientBitrateLimit", page).val(user.Policy.RemoteClientBitrateLimit / 1e6 || ""); - $("#txtLoginAttemptsBeforeLockout", page).val(user.Policy.LoginAttemptsBeforeLockout || "0"); - loading.hide(); - } - - function onSaveComplete(page, user) { - Dashboard.navigate("userprofiles.html"); - loading.hide(); - - require(["toast"], function (toast) { - toast(Globalize.translate("SettingsSaved")); - }); - } - - function saveUser(user, page) { - user.Name = $("#txtUserName", page).val(); - user.Policy.IsAdministrator = $("#chkIsAdmin", page).checked(); - user.Policy.IsHidden = $("#chkIsHidden", page).checked(); - user.Policy.IsDisabled = $("#chkDisabled", page).checked(); - user.Policy.EnableRemoteControlOfOtherUsers = $("#chkEnableRemoteControlOtherUsers", page).checked(); - user.Policy.EnableLiveTvManagement = $("#chkManageLiveTv", page).checked(); - user.Policy.EnableLiveTvAccess = $("#chkEnableLiveTvAccess", page).checked(); - user.Policy.EnableSharedDeviceControl = $("#chkRemoteControlSharedDevices", page).checked(); - user.Policy.EnableMediaPlayback = $("#chkEnableMediaPlayback", page).checked(); - user.Policy.EnableAudioPlaybackTranscoding = $("#chkEnableAudioPlaybackTranscoding", page).checked(); - user.Policy.EnableVideoPlaybackTranscoding = $("#chkEnableVideoPlaybackTranscoding", page).checked(); - user.Policy.EnablePlaybackRemuxing = $("#chkEnableVideoPlaybackRemuxing", page).checked(); - user.Policy.ForceRemoteSourceTranscoding = $("#chkForceRemoteSourceTranscoding", page).checked(); - user.Policy.EnableContentDownloading = $("#chkEnableDownloading", page).checked(); - user.Policy.EnableSyncTranscoding = $("#chkEnableSyncTranscoding", page).checked(); - user.Policy.EnableMediaConversion = $("#chkEnableConversion", page).checked(); - user.Policy.EnablePublicSharing = $("#chkEnableSharing", page).checked(); - user.Policy.EnableRemoteAccess = $("#chkRemoteAccess", page).checked(); - user.Policy.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($("#txtRemoteClientBitrateLimit", page).val() || "0")); - user.Policy.LoginAttemptsBeforeLockout = parseInt($("#txtLoginAttemptsBeforeLockout", page).val() || "0"); - user.Policy.AuthenticationProviderId = page.querySelector(".selectLoginProvider").value; - user.Policy.PasswordResetProviderId = page.querySelector(".selectPasswordResetProvider").value; - user.Policy.EnableContentDeletion = $("#chkEnableDeleteAllFolders", page).checked(); - user.Policy.EnableContentDeletionFromFolders = user.Policy.EnableContentDeletion ? [] : $(".chkFolder", page).get().filter(function (c) { - return c.checked; - }).map(function (c) { - return c.getAttribute("data-id"); - }); - ApiClient.updateUser(user).then(function () { - ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () { - onSaveComplete(page, user); - }); - }); - } - - function onSubmit() { - var page = $(this).parents(".page")[0]; - loading.show(); - getUser().then(function (result) { - saveUser(result, page); - }); - return false; - } - - function getUser() { - var userId = getParameterByName("userId"); - return ApiClient.getUser(userId); - } - - function loadData(page) { - loading.show(); - getUser().then(function (user) { - loadUser(page, user); - }); - } - - var currentUser; - $(document).on("pageinit", "#editUserPage", function () { - $(".editUserProfileForm").off("submit", onSubmit).on("submit", onSubmit); - this.querySelector(".sharingHelp").innerHTML = Globalize.translate("OptionAllowLinkSharingHelp", 30); - var page = this; - $("#chkEnableDeleteAllFolders", this).on("change", function () { - if (this.checked) { - $(".deleteAccess", page).hide(); - } else { - $(".deleteAccess", page).show(); - } - }); - ApiClient.getServerConfiguration().then(function (config) { - if (config.EnableRemoteAccess) { - page.querySelector(".fldRemoteAccess").classList.remove("hide"); - } else { - page.querySelector(".fldRemoteAccess").classList.add("hide"); - } - }); - }).on("pagebeforeshow", "#editUserPage", function () { - loadData(this); - }); -}); diff --git a/src/controllers/usernew.js b/src/controllers/usernew.js deleted file mode 100644 index ec80679f8c..0000000000 --- a/src/controllers/usernew.js +++ /dev/null @@ -1,126 +0,0 @@ -define(["jQuery", "loading", "fnchecked", "emby-checkbox"], function ($, loading) { - "use strict"; - - function loadMediaFolders(page, mediaFolders) { - var html = ""; - html += '

' + Globalize.translate("HeaderLibraries") + "

"; - html += '
'; - - for (var i = 0; i < mediaFolders.length; i++) { - var folder = mediaFolders[i]; - html += '"; - } - - html += "
"; - $(".folderAccess", page).html(html).trigger("create"); - $("#chkEnableAllFolders", page).checked(true).trigger("change"); - } - - function loadChannels(page, channels) { - var html = ""; - html += '

' + Globalize.translate("HeaderChannels") + "

"; - html += '
'; - - for (var i = 0; i < channels.length; i++) { - var folder = channels[i]; - html += '"; - } - - html += "
"; - $(".channelAccess", page).show().html(html).trigger("create"); - - if (channels.length) { - $(".channelAccessContainer", page).show(); - } else { - $(".channelAccessContainer", page).hide(); - } - - $("#chkEnableAllChannels", page).checked(true).trigger("change"); - } - - function loadUser(page) { - $("#txtUsername", page).val(""); - $("#txtPassword", page).val(""); - loading.show(); - var promiseFolders = ApiClient.getJSON(ApiClient.getUrl("Library/MediaFolders", { - IsHidden: false - })); - var promiseChannels = ApiClient.getJSON(ApiClient.getUrl("Channels")); - Promise.all([promiseFolders, promiseChannels]).then(function (responses) { - loadMediaFolders(page, responses[0].Items); - loadChannels(page, responses[1].Items); - loading.hide(); - }); - } - - function saveUser(page) { - var user = {}; - user.Name = $("#txtUsername", page).val(); - user.Password = $("#txtPassword", page).val(); - ApiClient.createUser(user).then(function (user) { - user.Policy.EnableAllFolders = $("#chkEnableAllFolders", page).checked(); - user.Policy.EnabledFolders = []; - - if (!user.Policy.EnableAllFolders) { - user.Policy.EnabledFolders = $(".chkFolder", page).get().filter(function (i) { - return i.checked; - }).map(function (i) { - return i.getAttribute("data-id"); - }); - } - - user.Policy.EnableAllChannels = $("#chkEnableAllChannels", page).checked(); - user.Policy.EnabledChannels = []; - - if (!user.Policy.EnableAllChannels) { - user.Policy.EnabledChannels = $(".chkChannel", page).get().filter(function (i) { - return i.checked; - }).map(function (i) { - return i.getAttribute("data-id"); - }); - } - - ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () { - Dashboard.navigate("useredit.html?userId=" + user.Id); - }); - }, function (response) { - require(["toast"], function (toast) { - toast(Globalize.translate("DefaultErrorMessage")); - }); - - loading.hide(); - }); - } - - function onSubmit() { - var page = $(this).parents(".page")[0]; - loading.show(); - saveUser(page); - return false; - } - - function loadData(page) { - loadUser(page); - } - - $(document).on("pageinit", "#newUserPage", function () { - var page = this; - $("#chkEnableAllChannels", page).on("change", function () { - if (this.checked) { - $(".channelAccessListContainer", page).hide(); - } else { - $(".channelAccessListContainer", page).show(); - } - }); - $("#chkEnableAllFolders", page).on("change", function () { - if (this.checked) { - $(".folderAccessListContainer", page).hide(); - } else { - $(".folderAccessListContainer", page).show(); - } - }); - $(".newUserProfileForm").off("submit", onSubmit).on("submit", onSubmit); - }).on("pageshow", "#newUserPage", function () { - loadData(this); - }); -}); diff --git a/src/controllers/wizard/finish.js b/src/controllers/wizard/finish.js index 8242a16cb4..c27d9a5f4b 100644 --- a/src/controllers/wizard/finish.js +++ b/src/controllers/wizard/finish.js @@ -1,18 +1,18 @@ -define(["loading"], function (loading) { - "use strict"; +define(['loading'], function (loading) { + 'use strict'; function onFinish() { loading.show(); ApiClient.ajax({ - url: ApiClient.getUrl("Startup/Complete"), - type: "POST" + url: ApiClient.getUrl('Startup/Complete'), + type: 'POST' }).then(function () { loading.hide(); - window.location.href = "index.html"; + window.location.href = 'index.html'; }); } return function (view, params) { - view.querySelector(".btnWizardNext").addEventListener("click", onFinish); + view.querySelector('.btnWizardNext').addEventListener('click', onFinish); }; }); diff --git a/src/controllers/wizard/remoteaccess.js b/src/controllers/wizard/remoteaccess.js index 554a417e57..400cd357f4 100644 --- a/src/controllers/wizard/remoteaccess.js +++ b/src/controllers/wizard/remoteaccess.js @@ -1,16 +1,16 @@ -define(["loading", "emby-checkbox", "emby-button", "emby-select"], function (loading) { - "use strict"; +define(['loading', 'emby-checkbox', 'emby-button', 'emby-select'], function (loading) { + 'use strict'; function save(page) { loading.show(); var apiClient = ApiClient; var config = {}; - config.EnableRemoteAccess = page.querySelector("#chkRemoteAccess").checked; - config.EnableAutomaticPortMapping = page.querySelector("#chkEnableUpnp").checked; + config.EnableRemoteAccess = page.querySelector('#chkRemoteAccess').checked; + config.EnableAutomaticPortMapping = page.querySelector('#chkEnableUpnp').checked; apiClient.ajax({ - type: "POST", + type: 'POST', data: config, - url: apiClient.getUrl("Startup/RemoteAccess") + url: apiClient.getUrl('Startup/RemoteAccess') }).then(function () { loading.hide(); navigateToNextPage(); @@ -18,7 +18,7 @@ define(["loading", "emby-checkbox", "emby-button", "emby-select"], function (loa } function navigateToNextPage() { - Dashboard.navigate("wizardfinish.html"); + Dashboard.navigate('wizardfinish.html'); } function onSubmit(e) { @@ -28,12 +28,12 @@ define(["loading", "emby-checkbox", "emby-button", "emby-select"], function (loa } return function (view, params) { - view.querySelector(".wizardSettingsForm").addEventListener("submit", onSubmit); - view.addEventListener("viewshow", function () { - document.querySelector(".skinHeader").classList.add("noHomeButtonHeader"); + view.querySelector('.wizardSettingsForm').addEventListener('submit', onSubmit); + view.addEventListener('viewshow', function () { + document.querySelector('.skinHeader').classList.add('noHomeButtonHeader'); }); - view.addEventListener("viewhide", function () { - document.querySelector(".skinHeader").classList.remove("noHomeButtonHeader"); + view.addEventListener('viewhide', function () { + document.querySelector('.skinHeader').classList.remove('noHomeButtonHeader'); }); }; }); diff --git a/src/controllers/wizard/settings.js b/src/controllers/wizard/settings.js index 487f068a40..2062e795a3 100644 --- a/src/controllers/wizard/settings.js +++ b/src/controllers/wizard/settings.js @@ -1,16 +1,16 @@ -define(["loading", "emby-checkbox", "emby-button", "emby-select"], function (loading) { - "use strict"; +define(['loading', 'emby-checkbox', 'emby-button', 'emby-select'], function (loading) { + 'use strict'; function save(page) { loading.show(); var apiClient = ApiClient; - apiClient.getJSON(apiClient.getUrl("Startup/Configuration")).then(function (config) { - config.PreferredMetadataLanguage = page.querySelector("#selectLanguage").value; - config.MetadataCountryCode = page.querySelector("#selectCountry").value; + apiClient.getJSON(apiClient.getUrl('Startup/Configuration')).then(function (config) { + config.PreferredMetadataLanguage = page.querySelector('#selectLanguage').value; + config.MetadataCountryCode = page.querySelector('#selectCountry').value; apiClient.ajax({ - type: "POST", + type: 'POST', data: config, - url: apiClient.getUrl("Startup/Configuration") + url: apiClient.getUrl('Startup/Configuration') }).then(function () { loading.hide(); navigateToNextPage(); @@ -19,41 +19,41 @@ define(["loading", "emby-checkbox", "emby-button", "emby-select"], function (loa } function populateLanguages(select, languages) { - var html = ""; + var html = ''; html += ""; for (var i = 0, length = languages.length; i < length; i++) { var culture = languages[i]; - html += ""; + html += "'; } select.innerHTML = html; } function populateCountries(select, allCountries) { - var html = ""; + var html = ''; html += ""; for (var i = 0, length = allCountries.length; i < length; i++) { var culture = allCountries[i]; - html += ""; + html += "'; } select.innerHTML = html; } function reloadData(page, config, cultures, countries) { - populateLanguages(page.querySelector("#selectLanguage"), cultures); - populateCountries(page.querySelector("#selectCountry"), countries); - page.querySelector("#selectLanguage").value = config.PreferredMetadataLanguage; - page.querySelector("#selectCountry").value = config.MetadataCountryCode; + populateLanguages(page.querySelector('#selectLanguage'), cultures); + populateCountries(page.querySelector('#selectCountry'), countries); + page.querySelector('#selectLanguage').value = config.PreferredMetadataLanguage; + page.querySelector('#selectCountry').value = config.MetadataCountryCode; loading.hide(); } function reload(page) { loading.show(); var apiClient = ApiClient; - var promise1 = apiClient.getJSON(apiClient.getUrl("Startup/Configuration")); + var promise1 = apiClient.getJSON(apiClient.getUrl('Startup/Configuration')); var promise2 = apiClient.getCultures(); var promise3 = apiClient.getCountries(); Promise.all([promise1, promise2, promise3]).then(function (responses) { @@ -62,7 +62,7 @@ define(["loading", "emby-checkbox", "emby-button", "emby-select"], function (loa } function navigateToNextPage() { - Dashboard.navigate("wizardremoteaccess.html"); + Dashboard.navigate('wizardremoteaccess.html'); } function onSubmit(e) { @@ -72,13 +72,13 @@ define(["loading", "emby-checkbox", "emby-button", "emby-select"], function (loa } return function (view, params) { - view.querySelector(".wizardSettingsForm").addEventListener("submit", onSubmit); - view.addEventListener("viewshow", function () { - document.querySelector(".skinHeader").classList.add("noHomeButtonHeader"); + view.querySelector('.wizardSettingsForm').addEventListener('submit', onSubmit); + view.addEventListener('viewshow', function () { + document.querySelector('.skinHeader').classList.add('noHomeButtonHeader'); reload(this); }); - view.addEventListener("viewhide", function () { - document.querySelector(".skinHeader").classList.remove("noHomeButtonHeader"); + view.addEventListener('viewhide', function () { + document.querySelector('.skinHeader').classList.remove('noHomeButtonHeader'); }); }; }); diff --git a/src/controllers/wizard/start.js b/src/controllers/wizard/start.js index 1c2917b9ec..b7fb920d45 100644 --- a/src/controllers/wizard/start.js +++ b/src/controllers/wizard/start.js @@ -1,9 +1,9 @@ -define(["jQuery", "loading", "emby-button", "emby-select"], function ($, loading) { - "use strict"; +define(['jQuery', 'loading', 'emby-button', 'emby-select'], function ($, loading) { + 'use strict'; function loadPage(page, config, languageOptions) { - $("#selectLocalizationLanguage", page).html(languageOptions.map(function (l) { - return '"; + $('#selectLocalizationLanguage', page).html(languageOptions.map(function (l) { + return ''; })).val(config.UICulture); loading.hide(); } @@ -11,38 +11,38 @@ define(["jQuery", "loading", "emby-button", "emby-select"], function ($, loading function save(page) { loading.show(); var apiClient = ApiClient; - apiClient.getJSON(apiClient.getUrl("Startup/Configuration")).then(function (config) { - config.UICulture = $("#selectLocalizationLanguage", page).val(); + apiClient.getJSON(apiClient.getUrl('Startup/Configuration')).then(function (config) { + config.UICulture = $('#selectLocalizationLanguage', page).val(); apiClient.ajax({ - type: "POST", + type: 'POST', data: config, - url: apiClient.getUrl("Startup/Configuration") + url: apiClient.getUrl('Startup/Configuration') }).then(function () { - Dashboard.navigate("wizarduser.html"); + Dashboard.navigate('wizarduser.html'); }); }); } function onSubmit() { - save($(this).parents(".page")); + save($(this).parents('.page')); return false; } return function (view, params) { - $(".wizardStartForm", view).on("submit", onSubmit); - view.addEventListener("viewshow", function () { - document.querySelector(".skinHeader").classList.add("noHomeButtonHeader"); + $('.wizardStartForm', view).on('submit', onSubmit); + view.addEventListener('viewshow', function () { + document.querySelector('.skinHeader').classList.add('noHomeButtonHeader'); loading.show(); var page = this; var apiClient = ApiClient; - var promise1 = apiClient.getJSON(apiClient.getUrl("Startup/Configuration")); - var promise2 = apiClient.getJSON(apiClient.getUrl("Localization/Options")); + var promise1 = apiClient.getJSON(apiClient.getUrl('Startup/Configuration')); + var promise2 = apiClient.getJSON(apiClient.getUrl('Localization/Options')); Promise.all([promise1, promise2]).then(function (responses) { loadPage(page, responses[0], responses[1]); }); }); - view.addEventListener("viewhide", function () { - document.querySelector(".skinHeader").classList.remove("noHomeButtonHeader"); + view.addEventListener('viewhide', function () { + document.querySelector('.skinHeader').classList.remove('noHomeButtonHeader'); }); }; }); diff --git a/src/controllers/wizard/user.js b/src/controllers/wizard/user.js index 270953b24e..e62edef9fe 100644 --- a/src/controllers/wizard/user.js +++ b/src/controllers/wizard/user.js @@ -1,16 +1,16 @@ -define(["loading", "globalize", "dashboardcss", "emby-input", "emby-button", "emby-button"], function (loading, globalize) { - "use strict"; +define(['loading', 'globalize', 'dashboardcss', 'emby-input', 'emby-button', 'emby-button'], function (loading, globalize) { + 'use strict'; function getApiClient() { return ApiClient; } function nextWizardPage() { - Dashboard.navigate("wizardlibrary.html"); + Dashboard.navigate('wizardlibrary.html'); } function onUpdateUserComplete(result) { - console.debug("user update complete: " + result); + console.debug('user update complete: ' + result); loading.hide(); nextWizardPage(); } @@ -19,21 +19,21 @@ define(["loading", "globalize", "dashboardcss", "emby-input", "emby-button", "em loading.show(); var apiClient = getApiClient(); apiClient.ajax({ - type: "POST", + type: 'POST', data: { - Name: form.querySelector("#txtUsername").value, - Password: form.querySelector("#txtManualPassword").value + Name: form.querySelector('#txtUsername').value, + Password: form.querySelector('#txtManualPassword').value }, - url: apiClient.getUrl("Startup/User") + url: apiClient.getUrl('Startup/User') }).then(onUpdateUserComplete); } function onSubmit(e) { var form = this; - if (form.querySelector("#txtManualPassword").value != form.querySelector("#txtPasswordConfirm").value) { - require(["toast"], function (toast) { - toast(Globalize.translate("PasswordMatchError")); + if (form.querySelector('#txtManualPassword').value != form.querySelector('#txtPasswordConfirm').value) { + require(['toast'], function (toast) { + toast(globalize.translate('PasswordMatchError')); }); } else { submit(form); @@ -47,21 +47,21 @@ define(["loading", "globalize", "dashboardcss", "emby-input", "emby-button", "em loading.show(); var page = this; var apiClient = getApiClient(); - apiClient.getJSON(apiClient.getUrl("Startup/User")).then(function (user) { - page.querySelector("#txtUsername").value = user.Name || ""; - page.querySelector("#txtManualPassword").value = user.Password || ""; + apiClient.getJSON(apiClient.getUrl('Startup/User')).then(function (user) { + page.querySelector('#txtUsername').value = user.Name || ''; + page.querySelector('#txtManualPassword').value = user.Password || ''; loading.hide(); }); } return function (view, params) { - view.querySelector(".wizardUserForm").addEventListener("submit", onSubmit); - view.addEventListener("viewshow", function () { - document.querySelector(".skinHeader").classList.add("noHomeButtonHeader"); + view.querySelector('.wizardUserForm').addEventListener('submit', onSubmit); + view.addEventListener('viewshow', function () { + document.querySelector('.skinHeader').classList.add('noHomeButtonHeader'); }); - view.addEventListener("viewhide", function () { - document.querySelector(".skinHeader").classList.remove("noHomeButtonHeader"); + view.addEventListener('viewhide', function () { + document.querySelector('.skinHeader').classList.remove('noHomeButtonHeader'); }); - view.addEventListener("viewshow", onViewShow); + view.addEventListener('viewshow', onViewShow); }; }); diff --git a/src/dashboard.html b/src/dashboard.html index adb38a9ad3..b7af3c8c03 100644 --- a/src/dashboard.html +++ b/src/dashboard.html @@ -5,7 +5,7 @@

${TabServer}

- +
@@ -16,7 +16,7 @@
- +
${LabelCachePathHelp}
@@ -49,7 +42,7 @@
- +
${LabelMetadataPathHelp}
@@ -62,7 +55,7 @@
${LabelLoginDisclaimerHelp}
-
+
${LabelCustomCssHelp}
diff --git a/src/device.html b/src/device.html index 11fc9671b1..093b311208 100644 --- a/src/device.html +++ b/src/device.html @@ -5,7 +5,7 @@
diff --git a/src/devices.html b/src/devices.html index 4e6552f05e..55f51d7e23 100644 --- a/src/devices.html +++ b/src/devices.html @@ -4,7 +4,7 @@

${TabDevices}

- ${Help} + ${Help}
diff --git a/src/dlnaprofile.html b/src/dlnaprofile.html index 2420ab4c35..e960d97a0f 100644 --- a/src/dlnaprofile.html +++ b/src/dlnaprofile.html @@ -5,7 +5,7 @@

${HeaderProfileInformation}

- ${Help} + ${Help}
@@ -98,7 +98,7 @@

${HeaderHttpHeaders}

@@ -221,7 +221,7 @@

${HeaderXmlDocumentAttributes}

@@ -272,7 +272,7 @@
-
+

${HeaderDirectPlayProfile}

@@ -312,7 +312,7 @@
-
+

${HeaderTranscodingProfile}

@@ -394,7 +394,7 @@
-
+

${HeaderContainerProfile}

@@ -426,7 +426,7 @@
-
+

${HeaderCodecProfile}

@@ -455,7 +455,7 @@
-
+

${HeaderResponseProfile}

@@ -495,7 +495,7 @@
-
+

${HeaderIdentificationHeader}

@@ -525,7 +525,7 @@
-
+

${HeaderXmlDocumentAttribute}

@@ -548,7 +548,7 @@
-
+

${HeaderSubtitleProfile}

diff --git a/src/dlnaprofiles.html b/src/dlnaprofiles.html index c1ae38609c..b47b2fd6bf 100644 --- a/src/dlnaprofiles.html +++ b/src/dlnaprofiles.html @@ -9,9 +9,9 @@

${HeaderCustomDlnaProfiles}

- add + - ${Help} + ${Help}

${CustomDlnaProfilesHelp}

diff --git a/src/dlnasettings.html b/src/dlnasettings.html index 8068acf5ab..703dd66a9f 100644 --- a/src/dlnasettings.html +++ b/src/dlnasettings.html @@ -8,7 +8,7 @@

${TabSettings}

- ${Help} + ${Help}
diff --git a/src/elements/emby-button/emby-button.css b/src/elements/emby-button/emby-button.css index a19ce571e2..0963125652 100644 --- a/src/elements/emby-button/emby-button.css +++ b/src/elements/emby-button/emby-button.css @@ -49,6 +49,7 @@ .button-link { background: transparent; + cursor: pointer; margin: 0; padding: 0; vertical-align: initial; @@ -58,12 +59,12 @@ text-decoration: underline; } -.emby-button > i { +.emby-button > .material-icons { /* For non-fab buttons that have icons */ font-size: 1.36em; } -.button-link > i { +.button-link > .material-icons { font-size: 1em; } @@ -136,7 +137,7 @@ cursor: default; } -.paper-icon-button-light > i { +.paper-icon-button-light > .material-icons { font-size: 1.66956521739130434em; /* Make sure its on top of the ripple */ diff --git a/src/elements/emby-checkbox/emby-checkbox.js b/src/elements/emby-checkbox/emby-checkbox.js index b5e587d5a6..4d02d56163 100644 --- a/src/elements/emby-checkbox/emby-checkbox.js +++ b/src/elements/emby-checkbox/emby-checkbox.js @@ -57,8 +57,8 @@ define(['browser', 'dom', 'css!./emby-checkbox', 'registerElement'], function (b var checkedIcon = this.getAttribute('data-checkedicon') || 'check'; var uncheckedIcon = this.getAttribute('data-uncheckedicon') || ''; - var checkHtml = '' + checkedIcon + ''; - var uncheckedHtml = '' + uncheckedIcon + ''; + var checkHtml = ''; + var uncheckedHtml = ''; labelElement.insertAdjacentHTML('beforeend', '' + checkHtml + uncheckedHtml + ''); labelTextElement.classList.add('checkboxLabel'); diff --git a/src/elements/emby-collapse/emby-collapse.js b/src/elements/emby-collapse/emby-collapse.js index fdd77adf0b..707e81a786 100644 --- a/src/elements/emby-collapse/emby-collapse.js +++ b/src/elements/emby-collapse/emby-collapse.js @@ -24,7 +24,7 @@ define(['browser', 'css!./emby-collapse', 'registerElement', 'emby-button'], fun elem.style.height = 'auto'; }, 300); - var icon = button.querySelector('i'); + var icon = button.querySelector('.material-icons'); //icon.innerHTML = 'expand_less'; icon.classList.add('emby-collapse-expandIconExpanded'); } @@ -46,7 +46,7 @@ define(['browser', 'css!./emby-collapse', 'registerElement', 'emby-button'], fun } }, 300); - var icon = button.querySelector('i'); + var icon = button.querySelector('.material-icons'); //icon.innerHTML = 'expand_more'; icon.classList.remove('emby-collapse-expandIconExpanded'); } @@ -80,7 +80,7 @@ define(['browser', 'css!./emby-collapse', 'registerElement', 'emby-button'], fun var title = this.getAttribute('title'); - var html = ''; + var html = ''; this.insertAdjacentHTML('afterbegin', html); diff --git a/src/elements/emby-input/emby-input.css b/src/elements/emby-input/emby-input.css index 18ad37a87a..195e680279 100644 --- a/src/elements/emby-input/emby-input.css +++ b/src/elements/emby-input/emby-input.css @@ -29,6 +29,27 @@ margin-bottom: 1.8em; } +.inlineForm { + display: flex; +} + +.inlineForm .inputContainer, +.inlineForm .selectContainer { + flex-basis: 0; + flex-grow: 1; + margin: 0 0.5em 1.8em; +} + +.inlineForm .inputContainer:first-child, +.inlineForm .selectContainer:first-child { + margin-left: 0; +} + +.inlineForm .inputContainer:last-child, +.inlineForm .selectContainer:last-child { + margin-right: 0; +} + .inputLabel { display: inline-block; margin-bottom: 0.25em; diff --git a/src/components/emby-itemrefreshindicator/emby-itemrefreshindicator.js b/src/elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js similarity index 100% rename from src/components/emby-itemrefreshindicator/emby-itemrefreshindicator.js rename to src/elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js diff --git a/src/components/emby-itemscontainer/emby-itemscontainer.js b/src/elements/emby-itemscontainer/emby-itemscontainer.js similarity index 99% rename from src/components/emby-itemscontainer/emby-itemscontainer.js rename to src/elements/emby-itemscontainer/emby-itemscontainer.js index c1aba9b6a0..5d3772ca93 100644 --- a/src/components/emby-itemscontainer/emby-itemscontainer.js +++ b/src/elements/emby-itemscontainer/emby-itemscontainer.js @@ -124,7 +124,7 @@ define(['itemShortcuts', 'inputManager', 'connectionManager', 'playbackManager', var self = this; require(['sortable'], function (Sortable) { self.sortable = new Sortable(self, { - draggable: ".listItem", + draggable: '.listItem', handle: '.listViewDragHandle', // dragging ended diff --git a/src/components/userdatabuttons/emby-playstatebutton.js b/src/elements/emby-playstatebutton/emby-playstatebutton.js similarity index 98% rename from src/components/userdatabuttons/emby-playstatebutton.js rename to src/elements/emby-playstatebutton/emby-playstatebutton.js index 4c3e5b6b62..57f7eb76eb 100644 --- a/src/components/userdatabuttons/emby-playstatebutton.js +++ b/src/elements/emby-playstatebutton/emby-playstatebutton.js @@ -41,7 +41,7 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby function setState(button, played, updateAttribute) { var icon = button.iconElement; if (!icon) { - button.iconElement = button.querySelector('i'); + button.iconElement = button.querySelector('.material-icons'); icon = button.iconElement; } diff --git a/src/elements/emby-programcell/emby-programcell.js b/src/elements/emby-programcell/emby-programcell.js new file mode 100644 index 0000000000..a959033186 --- /dev/null +++ b/src/elements/emby-programcell/emby-programcell.js @@ -0,0 +1,16 @@ +define([], function() { + 'use strict'; + + var ProgramCellPrototype = Object.create(HTMLButtonElement.prototype); + + ProgramCellPrototype.detachedCallback = function () { + this.posLeft = null; + this.posWidth = null; + this.guideProgramName = null; + }; + + document.registerElement('emby-programcell', { + prototype: ProgramCellPrototype, + extends: 'button' + }); +}); diff --git a/src/elements/emby-progressbar/emby-progressbar.js b/src/elements/emby-progressbar/emby-progressbar.js new file mode 100644 index 0000000000..a799f82bdd --- /dev/null +++ b/src/elements/emby-progressbar/emby-progressbar.js @@ -0,0 +1,42 @@ +define([], function() { + 'use strict'; + + var ProgressBarPrototype = Object.create(HTMLDivElement.prototype); + + function onAutoTimeProgress() { + var start = parseInt(this.getAttribute('data-starttime')); + var end = parseInt(this.getAttribute('data-endtime')); + + var now = new Date().getTime(); + var total = end - start; + var pct = 100 * ((now - start) / total); + + pct = Math.min(100, pct); + pct = Math.max(0, pct); + + var itemProgressBarForeground = this.querySelector('.itemProgressBarForeground'); + itemProgressBarForeground.style.width = pct + '%'; + } + + ProgressBarPrototype.attachedCallback = function () { + if (this.timeInterval) { + clearInterval(this.timeInterval); + } + + if (this.getAttribute('data-automode') === 'time') { + this.timeInterval = setInterval(onAutoTimeProgress.bind(this), 60000); + } + }; + + ProgressBarPrototype.detachedCallback = function () { + if (this.timeInterval) { + clearInterval(this.timeInterval); + this.timeInterval = null; + } + }; + + document.registerElement('emby-progressbar', { + prototype: ProgressBarPrototype, + extends: 'div' + }); +}); diff --git a/src/components/userdatabuttons/emby-ratingbutton.js b/src/elements/emby-ratingbutton/emby-ratingbutton.js similarity index 95% rename from src/components/userdatabuttons/emby-ratingbutton.js rename to src/elements/emby-ratingbutton/emby-ratingbutton.js index 84ae780de5..142920ca15 100644 --- a/src/components/userdatabuttons/emby-ratingbutton.js +++ b/src/elements/emby-ratingbutton/emby-ratingbutton.js @@ -57,12 +57,12 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby function setState(button, likes, isFavorite, updateAttribute) { - var icon = button.querySelector('i'); + var icon = button.querySelector('.material-icons'); if (isFavorite) { if (icon) { - icon.innerHTML = 'favorite'; + icon.classList.add('favorite'); icon.classList.add('ratingbutton-icon-withrating'); } @@ -71,7 +71,7 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby } else if (likes) { if (icon) { - icon.innerHTML = 'favorite'; + icon.classList.add('favorite'); icon.classList.remove('ratingbutton-icon-withrating'); //icon.innerHTML = 'thumb_up'; } @@ -80,7 +80,7 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby } else if (likes === false) { if (icon) { - icon.innerHTML = 'favorite'; + icon.classList.add('favorite'); icon.classList.remove('ratingbutton-icon-withrating'); //icon.innerHTML = 'thumb_down'; } @@ -89,7 +89,7 @@ define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby } else { if (icon) { - icon.innerHTML = 'favorite'; + icon.classList.add('favorite'); icon.classList.remove('ratingbutton-icon-withrating'); //icon.innerHTML = 'thumbs_up_down'; } diff --git a/src/components/emby-scrollbuttons/emby-scrollbuttons.css b/src/elements/emby-scrollbuttons/emby-scrollbuttons.css similarity index 87% rename from src/components/emby-scrollbuttons/emby-scrollbuttons.css rename to src/elements/emby-scrollbuttons/emby-scrollbuttons.css index 32e60292ad..b2e0d3bc23 100644 --- a/src/components/emby-scrollbuttons/emby-scrollbuttons.css +++ b/src/elements/emby-scrollbuttons/emby-scrollbuttons.css @@ -12,7 +12,7 @@ display: flex; } -.emby-scrollbuttons-button > i { +.emby-scrollbuttons-button > .material-icons { min-width: 24px; min-height: 24px; display: block; diff --git a/src/components/emby-scrollbuttons/emby-scrollbuttons.js b/src/elements/emby-scrollbuttons/emby-scrollbuttons.js similarity index 97% rename from src/components/emby-scrollbuttons/emby-scrollbuttons.js rename to src/elements/emby-scrollbuttons/emby-scrollbuttons.js index af8261a632..a4c37384c8 100644 --- a/src/components/emby-scrollbuttons/emby-scrollbuttons.js +++ b/src/elements/emby-scrollbuttons/emby-scrollbuttons.js @@ -7,10 +7,10 @@ define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', ' function getScrollButtonHtml(direction) { var html = ''; - var icon = direction === 'left' ? '' : ''; + var icon = direction === 'left' ? 'chevron_left' : 'chevron_right'; html += ''; return html; diff --git a/src/components/emby-scroller/emby-scroller.css b/src/elements/emby-scroller/emby-scroller.css similarity index 100% rename from src/components/emby-scroller/emby-scroller.css rename to src/elements/emby-scroller/emby-scroller.css diff --git a/src/components/emby-scroller/emby-scroller.js b/src/elements/emby-scroller/emby-scroller.js similarity index 91% rename from src/components/emby-scroller/emby-scroller.js rename to src/elements/emby-scroller/emby-scroller.js index cb5bae818f..3df40fa6c2 100644 --- a/src/components/emby-scroller/emby-scroller.js +++ b/src/elements/emby-scroller/emby-scroller.js @@ -96,17 +96,6 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro } } - function initHeadroom(elem) { - require(['headroom'], function (Headroom) { - var headroom = new Headroom([], { - scroller: elem - }); - - headroom.add(document.querySelector('.skinHeader')); - elem.headroom = headroom; - }); - } - ScrollerPrototype.attachedCallback = function () { if (this.getAttribute('data-navcommands')) { inputManager.on(this, onInputCommand); @@ -120,8 +109,6 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro slider.style['white-space'] = 'nowrap'; } - var bindHeader = this.getAttribute('data-bindheader') === 'true'; - var scrollFrame = this; var enableScrollButtons = layoutManager.desktop && horizontal && this.getAttribute('data-scrollbuttons') !== 'false'; @@ -137,7 +124,7 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro dragHandle: 1, autoImmediate: true, skipSlideToWhenVisible: this.getAttribute('data-skipfocuswhenvisible') === 'true', - dispatchScrollEvent: enableScrollButtons || bindHeader || this.getAttribute('data-scrollevent') === 'true', + dispatchScrollEvent: enableScrollButtons || this.getAttribute('data-scrollevent') === 'true', hideScrollbar: enableScrollButtons || this.getAttribute('data-hidescrollbar') === 'true', allowNativeSmoothScroll: this.getAttribute('data-allownativesmoothscroll') === 'true' && !enableScrollButtons, allowNativeScroll: !enableScrollButtons, @@ -155,10 +142,6 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro initCenterFocus(this, this.scroller); } - if (bindHeader && layoutManager.mobile) { - initHeadroom(this); - } - if (enableScrollButtons) { loadScrollButtons(this); } diff --git a/src/elements/emby-select/emby-select.js b/src/elements/emby-select/emby-select.js index c772c0f960..2716967560 100644 --- a/src/elements/emby-select/emby-select.js +++ b/src/elements/emby-select/emby-select.js @@ -27,8 +27,8 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe } function triggerChange(select) { - var evt = document.createEvent("HTMLEvents"); - evt.initEvent("change", false, true); + var evt = document.createEvent('HTMLEvents'); + evt.initEvent('change', false, true); select.dispatchEvent(evt); } @@ -144,7 +144,7 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe this.parentNode.insertBefore(label, this); if (this.classList.contains('emby-select-withcolor')) { - this.parentNode.insertAdjacentHTML('beforeend', '
0
'); + this.parentNode.insertAdjacentHTML('beforeend', '
0
'); } }; diff --git a/src/elements/emby-slider/emby-slider.css b/src/elements/emby-slider/emby-slider.css index f59c2a3cd0..7661895f15 100644 --- a/src/elements/emby-slider/emby-slider.css +++ b/src/elements/emby-slider/emby-slider.css @@ -87,10 +87,6 @@ transform: scale(1.3); } -.slider-no-webkit-thumb::-webkit-slider-thumb { - opacity: 0 !important; -} - .mdl-slider::-moz-range-thumb { -moz-appearance: none; width: 1.08em; diff --git a/src/elements/emby-slider/emby-slider.js b/src/elements/emby-slider/emby-slider.js index 03d64719e2..1b78fca0ae 100644 --- a/src/elements/emby-slider/emby-slider.js +++ b/src/elements/emby-slider/emby-slider.js @@ -148,10 +148,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli this.classList.add('mdl-slider'); this.classList.add('mdl-js-slider'); - if (browser.noFlex) { - this.classList.add('slider-no-webkit-thumb'); - } - if (browser.edge || browser.msie) { + if (browser.edge) { this.classList.add('slider-browser-edge'); } if (!layoutManager.mobile) { diff --git a/src/components/emby-tabs/emby-tabs.css b/src/elements/emby-tabs/emby-tabs.css similarity index 100% rename from src/components/emby-tabs/emby-tabs.css rename to src/elements/emby-tabs/emby-tabs.css diff --git a/src/components/emby-tabs/emby-tabs.js b/src/elements/emby-tabs/emby-tabs.js similarity index 96% rename from src/components/emby-tabs/emby-tabs.js rename to src/elements/emby-tabs/emby-tabs.js index fa5979fa4c..5e03c3f096 100644 --- a/src/components/emby-tabs/emby-tabs.js +++ b/src/elements/emby-tabs/emby-tabs.js @@ -46,7 +46,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register function triggerBeforeTabChange(tabs, index, previousIndex) { - tabs.dispatchEvent(new CustomEvent("beforetabchange", { + tabs.dispatchEvent(new CustomEvent('beforetabchange', { detail: { selectedTabIndex: index, previousIndex: previousIndex @@ -94,7 +94,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register tabs.selectedTabIndex = index; - tabs.dispatchEvent(new CustomEvent("tabchange", { + tabs.dispatchEvent(new CustomEvent('tabchange', { detail: { selectedTabIndex: index, previousIndex: previousIndex @@ -198,7 +198,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register if (!this.readyFired) { this.readyFired = true; - this.dispatchEvent(new CustomEvent("ready", {})); + this.dispatchEvent(new CustomEvent('ready', {})); } }; @@ -238,7 +238,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register triggerBeforeTabChange(tabs, selected, current); - tabs.dispatchEvent(new CustomEvent("tabchange", { + tabs.dispatchEvent(new CustomEvent('tabchange', { detail: { selectedTabIndex: selected } @@ -315,7 +315,7 @@ define(['dom', 'scroller', 'browser', 'layoutManager', 'focusManager', 'register var tabs = this; - tabs.dispatchEvent(new CustomEvent("tabchange", { + tabs.dispatchEvent(new CustomEvent('tabchange', { detail: { selectedTabIndex: tabs.selectedIndex() } diff --git a/src/elements/emby-textarea/emby-textarea.js b/src/elements/emby-textarea/emby-textarea.js index e0ce77aa51..87a3d7fcee 100644 --- a/src/elements/emby-textarea/emby-textarea.js +++ b/src/elements/emby-textarea/emby-textarea.js @@ -55,6 +55,7 @@ define(['layoutManager', 'browser', 'css!./emby-textarea', 'registerElement', 'e newHeight = textarea.scrollHeight/* - offset*/; hasGrown = true; } + $('.customCssContainer').css('height', newHeight + 'px'); textarea.style.height = newHeight + 'px'; } diff --git a/src/encodingsettings.html b/src/encodingsettings.html index 8e8c8fc5a2..6b21df4036 100644 --- a/src/encodingsettings.html +++ b/src/encodingsettings.html @@ -5,7 +5,7 @@

${TabTranscoding}

- ${Help} + ${Help}
@@ -21,7 +21,7 @@
@@ -104,7 +104,7 @@
- +
${LabelffmpegPathHelp}
@@ -115,7 +115,7 @@
- +
${LabelTranscodingTempPathHelp}
diff --git a/src/index.html b/src/index.html index 624be19360..ed63ca1a98 100644 --- a/src/index.html +++ b/src/index.html @@ -92,7 +92,7 @@ left: 0; bottom: 0; z-index: 1; - width: 10px; + width: 0.8em; } diff --git a/src/itemdetails.html b/src/itemdetails.html index 605ff926e3..18de25845c 100644 --- a/src/itemdetails.html +++ b/src/itemdetails.html @@ -1,7 +1,7 @@
@@ -17,91 +17,91 @@
@@ -136,7 +136,7 @@
-
+
@@ -148,6 +148,9 @@

+

diff --git a/src/legacy/dashboard.js b/src/legacy/dashboard.js index 69c47d3b1f..08c5e8330b 100644 --- a/src/legacy/dashboard.js +++ b/src/legacy/dashboard.js @@ -1,20 +1,24 @@ Dashboard.confirm = function(message, title, callback) { - "use strict"; - require(["confirm"], function(confirm) { + 'use strict'; + require(['confirm'], function(confirm) { confirm(message, title).then(function() { callback(!0); - }, function() { + }).catch(function() { callback(!1); }); }); -}, Dashboard.showLoadingMsg = function() { - "use strict"; - require(["loading"], function(loading) { +}; + +Dashboard.showLoadingMsg = function() { + 'use strict'; + require(['loading'], function(loading) { loading.show(); }); -}, Dashboard.hideLoadingMsg = function() { - "use strict"; - require(["loading"], function(loading) { +}; + +Dashboard.hideLoadingMsg = function() { + 'use strict'; + require(['loading'], function(loading) { loading.hide(); }); }; diff --git a/src/legacy/fnchecked.js b/src/legacy/fnchecked.js index 1f5a5f6014..c3eff813e0 100644 --- a/src/legacy/fnchecked.js +++ b/src/legacy/fnchecked.js @@ -1,10 +1,12 @@ -define(["jQuery"], function($) { - "use strict"; +define(['jQuery'], function($) { + 'use strict'; $.fn.checked = function(value) { return !0 === value || !1 === value ? $(this).each(function() { this.checked = value; }) : this.length && this[0].checked; - }, $.fn.checkboxradio = function() { + }; + + $.fn.checkboxradio = function() { return this; }; }); diff --git a/src/components/polyfills/focusPreventScroll.js b/src/legacy/focusPreventScroll.js similarity index 81% rename from src/components/polyfills/focusPreventScroll.js rename to src/legacy/focusPreventScroll.js index 6df9e9928c..93f53dca29 100644 --- a/src/components/polyfills/focusPreventScroll.js +++ b/src/legacy/focusPreventScroll.js @@ -4,14 +4,14 @@ if (HTMLElement.prototype.nativeFocus === undefined) { (function () { var supportsPreventScrollOption = false; try { - var focusElem = document.createElement("div"); + var focusElem = document.createElement('div'); - focusElem.addEventListener("focus", function(event) { + focusElem.addEventListener('focus', function(event) { event.preventDefault(); event.stopPropagation(); }, true); - var opts = Object.defineProperty({}, "preventScroll", { + var opts = Object.defineProperty({}, 'preventScroll', { // eslint-disable-next-line getter-return get: function () { supportsPreventScrollOption = true; @@ -20,7 +20,7 @@ if (HTMLElement.prototype.nativeFocus === undefined) { focusElem.focus(opts); } catch (e) { - console.error("error checking preventScroll support"); + console.error('error checking preventScroll support'); } if (!supportsPreventScrollOption) { diff --git a/src/legacy/selectmenu.js b/src/legacy/selectmenu.js index 95f435b587..f4cc5466b1 100644 --- a/src/legacy/selectmenu.js +++ b/src/legacy/selectmenu.js @@ -1,5 +1,5 @@ -define(["jQuery"], function($) { - "use strict"; +define(['jQuery'], function($) { + 'use strict'; $.fn.selectmenu = function() { return this; }; diff --git a/src/libraries/apiclient/apiclient.js b/src/libraries/apiclient/apiclient.js deleted file mode 100644 index 58cfa300b9..0000000000 --- a/src/libraries/apiclient/apiclient.js +++ /dev/null @@ -1,236 +0,0 @@ -//TODO: (vitorsemeano) modify this lines for webpack -define(["libraries/apiclient/apiclientcore", "localassetmanager"], function(ApiClient, localassetmanager) { - "use strict"; - - if ("cordova" !== window.appMode && "android" !== window.appMode) { - return ApiClient; - } - - function isLocalId(str) { - return startsWith(str, localPrefix) - } - - function isLocalViewId(str) { - return startsWith(str, localViewPrefix) - } - - function isTopLevelLocalViewId(str) { - return "localview" === str - } - - function stripLocalPrefix(str) { - var res = stripStart(str, localPrefix); - return res = stripStart(res, localViewPrefix) - } - - function startsWith(str, find) { - return !!(str && find && str.length > find.length && 0 === str.indexOf(find)) - } - - function stripStart(str, find) { - return startsWith(str, find) ? str.substr(find.length) : str - } - - function createEmptyList() { - return { - Items: [], - TotalRecordCount: 0 - } - } - - function convertGuidToLocal(guid) { - return guid ? isLocalId(guid) ? guid : "local:" + guid : null - } - - function adjustGuidProperties(downloadedItem) { - downloadedItem.Id = convertGuidToLocal(downloadedItem.Id), downloadedItem.SeriesId = convertGuidToLocal(downloadedItem.SeriesId), downloadedItem.SeasonId = convertGuidToLocal(downloadedItem.SeasonId), downloadedItem.AlbumId = convertGuidToLocal(downloadedItem.AlbumId), downloadedItem.ParentId = convertGuidToLocal(downloadedItem.ParentId), downloadedItem.ParentThumbItemId = convertGuidToLocal(downloadedItem.ParentThumbItemId), downloadedItem.ParentPrimaryImageItemId = convertGuidToLocal(downloadedItem.ParentPrimaryImageItemId), downloadedItem.PrimaryImageItemId = convertGuidToLocal(downloadedItem.PrimaryImageItemId), downloadedItem.ParentLogoItemId = convertGuidToLocal(downloadedItem.ParentLogoItemId), downloadedItem.ParentBackdropItemId = convertGuidToLocal(downloadedItem.ParentBackdropItemId), downloadedItem.ParentBackdropImageTags = null - } - - function getLocalView(instance, serverId, userId) { - return instance.getLocalFolders(serverId, userId).then(function(views) { - var localView = null; - return views.length > 0 && (localView = { - Name: instance.downloadsTitleText || "Downloads", - ServerId: serverId, - Id: "localview", - Type: "localview", - IsFolder: !0 - }), Promise.resolve(localView) - }) - } - - function ApiClientEx(serverAddress, clientName, applicationVersion, deviceName, deviceId) { - ApiClient.call(this, serverAddress, clientName, applicationVersion, deviceName, deviceId) - } - var localPrefix = "local:", - localViewPrefix = "localview:"; - return Object.assign(ApiClientEx.prototype, ApiClient.prototype), ApiClientEx.prototype.getPlaybackInfo = function(itemId, options, deviceProfile) { - var onFailure = function() { - return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile) - }; - if (isLocalId(itemId)) return localassetmanager.getLocalItem(this.serverId(), stripLocalPrefix(itemId)).then(function(item) { - return { - MediaSources: item.Item.MediaSources.map(function(m) { - return m.SupportsDirectPlay = !0, m.SupportsDirectStream = !1, m.SupportsTranscoding = !1, m.IsLocal = !0, m - }) - } - }, onFailure); - var instance = this; - return localassetmanager.getLocalItem(this.serverId(), itemId).then(function(item) { - if (item) { - var mediaSources = item.Item.MediaSources.map(function(m) { - return m.SupportsDirectPlay = !0, m.SupportsDirectStream = !1, m.SupportsTranscoding = !1, m.IsLocal = !0, m - }); - return localassetmanager.fileExists(item.LocalPath).then(function(exists) { - if (exists) { - var res = { - MediaSources: mediaSources - }; - return Promise.resolve(res) - } - return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile) - }, onFailure) - } - return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile) - }, onFailure) - }, ApiClientEx.prototype.getItems = function(userId, options) { - var i, serverInfo = this.serverInfo(); - if (serverInfo && "localview" === options.ParentId) return this.getLocalFolders(serverInfo.Id, userId).then(function(items) { - var result = { - Items: items, - TotalRecordCount: items.length - }; - return Promise.resolve(result) - }); - if (serverInfo && options && (isLocalId(options.ParentId) || isLocalId(options.SeriesId) || isLocalId(options.SeasonId) || isLocalViewId(options.ParentId) || isLocalId(options.AlbumIds))) return localassetmanager.getViewItems(serverInfo.Id, userId, options).then(function(items) { - items.forEach(function(item) { - adjustGuidProperties(item) - }); - var result = { - Items: items, - TotalRecordCount: items.length - }; - return Promise.resolve(result) - }); - if (options && options.ExcludeItemIds && options.ExcludeItemIds.length) { - var exItems = options.ExcludeItemIds.split(","); - for (i = 0; i < exItems.length; i++) - if (isLocalId(exItems[i])) return Promise.resolve(createEmptyList()) - } else if (options && options.Ids && options.Ids.length) { - var ids = options.Ids.split(","), - hasLocal = !1; - for (i = 0; i < ids.length; i++) isLocalId(ids[i]) && (hasLocal = !0); - if (hasLocal) return localassetmanager.getItemsFromIds(serverInfo.Id, ids).then(function(items) { - items.forEach(function(item) { - adjustGuidProperties(item) - }); - var result = { - Items: items, - TotalRecordCount: items.length - }; - return Promise.resolve(result) - }) - } - return ApiClient.prototype.getItems.call(this, userId, options) - }, ApiClientEx.prototype.getUserViews = function(options, userId) { - var instance = this; - options = options || {}; - var basePromise = ApiClient.prototype.getUserViews.call(instance, options, userId); - return options.enableLocalView ? basePromise.then(function(result) { - var serverInfo = instance.serverInfo(); - return serverInfo ? getLocalView(instance, serverInfo.Id, userId).then(function(localView) { - return localView && (result.Items.push(localView), result.TotalRecordCount++), Promise.resolve(result) - }) : Promise.resolve(result) - }) : basePromise - }, ApiClientEx.prototype.getItem = function(userId, itemId) { - if (!itemId) throw new Error("null itemId"); - itemId && (itemId = itemId.toString()); - var serverInfo; - return isTopLevelLocalViewId(itemId) && (serverInfo = this.serverInfo()) ? getLocalView(this, serverInfo.Id, userId) : isLocalViewId(itemId) && (serverInfo = this.serverInfo()) ? this.getLocalFolders(serverInfo.Id, userId).then(function(items) { - var views = items.filter(function(item) { - return item.Id === itemId - }); - return views.length > 0 ? Promise.resolve(views[0]) : Promise.reject() - }) : isLocalId(itemId) && (serverInfo = this.serverInfo()) ? localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(itemId)).then(function(item) { - return adjustGuidProperties(item.Item), Promise.resolve(item.Item) - }) : ApiClient.prototype.getItem.call(this, userId, itemId) - }, ApiClientEx.prototype.getLocalFolders = function(userId) { - var serverInfo = this.serverInfo(); - return userId = userId || serverInfo.UserId, localassetmanager.getViews(serverInfo.Id, userId) - }, ApiClientEx.prototype.getNextUpEpisodes = function(options) { - return options.SeriesId && isLocalId(options.SeriesId) ? Promise.resolve(createEmptyList()) : ApiClient.prototype.getNextUpEpisodes.call(this, options) - }, ApiClientEx.prototype.getSeasons = function(itemId, options) { - return isLocalId(itemId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Season", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : ApiClient.prototype.getSeasons.call(this, itemId, options) - }, ApiClientEx.prototype.getEpisodes = function(itemId, options) { - return isLocalId(options.SeasonId) || isLocalId(options.seasonId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Episode", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : isLocalId(itemId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Episode", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : ApiClient.prototype.getEpisodes.call(this, itemId, options) - }, ApiClientEx.prototype.getLatestOfflineItems = function(options) { - options.SortBy = "DateCreated", options.SortOrder = "Descending"; - var serverInfo = this.serverInfo(); - return serverInfo ? localassetmanager.getViewItems(serverInfo.Id, null, options).then(function(items) { - return items.forEach(function(item) { - adjustGuidProperties(item) - }), Promise.resolve(items) - }) : Promise.resolve([]) - }, ApiClientEx.prototype.getThemeMedia = function(userId, itemId, inherit) { - return isLocalViewId(itemId) || isLocalId(itemId) || isTopLevelLocalViewId(itemId) ? Promise.reject() : ApiClient.prototype.getThemeMedia.call(this, userId, itemId, inherit) - }, ApiClientEx.prototype.getSpecialFeatures = function(userId, itemId) { - return isLocalId(itemId) ? Promise.resolve([]) : ApiClient.prototype.getSpecialFeatures.call(this, userId, itemId) - }, ApiClientEx.prototype.getSimilarItems = function(itemId, options) { - return isLocalId(itemId) ? Promise.resolve(createEmptyList()) : ApiClient.prototype.getSimilarItems.call(this, itemId, options) - }, ApiClientEx.prototype.updateFavoriteStatus = function(userId, itemId, isFavorite) { - return isLocalId(itemId) ? Promise.resolve() : ApiClient.prototype.updateFavoriteStatus.call(this, userId, itemId, isFavorite) - }, ApiClientEx.prototype.getScaledImageUrl = function(itemId, options) { - if (isLocalId(itemId) || options && options.itemid && isLocalId(options.itemid)) { - var serverInfo = this.serverInfo(), - id = stripLocalPrefix(itemId); - return localassetmanager.getImageUrl(serverInfo.Id, id, options) - } - return ApiClient.prototype.getScaledImageUrl.call(this, itemId, options) - }, ApiClientEx.prototype.reportPlaybackStart = function(options) { - if (!options) throw new Error("null options"); - return isLocalId(options.ItemId) ? Promise.resolve() : ApiClient.prototype.reportPlaybackStart.call(this, options) - }, ApiClientEx.prototype.reportPlaybackProgress = function(options) { - if (!options) throw new Error("null options"); - if (isLocalId(options.ItemId)) { - var serverInfo = this.serverInfo(); - return serverInfo ? localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(options.ItemId)).then(function(item) { - var libraryItem = item.Item; - return "Video" === libraryItem.MediaType || "AudioBook" === libraryItem.Type ? (libraryItem.UserData = libraryItem.UserData || {}, libraryItem.UserData.PlaybackPositionTicks = options.PositionTicks, libraryItem.UserData.PlayedPercentage = Math.min(libraryItem.RunTimeTicks ? (options.PositionTicks || 0) / libraryItem.RunTimeTicks * 100 : 0, 100), localassetmanager.addOrUpdateLocalItem(item)) : Promise.resolve() - }) : Promise.resolve() - } - return ApiClient.prototype.reportPlaybackProgress.call(this, options) - }, ApiClientEx.prototype.reportPlaybackStopped = function(options) { - if (!options) throw new Error("null options"); - if (isLocalId(options.ItemId)) { - var serverInfo = this.serverInfo(), - action = { - Date: (new Date).getTime(), - ItemId: stripLocalPrefix(options.ItemId), - PositionTicks: options.PositionTicks, - ServerId: serverInfo.Id, - Type: 0, - UserId: this.getCurrentUserId() - }; - return localassetmanager.recordUserAction(action) - } - return ApiClient.prototype.reportPlaybackStopped.call(this, options) - }, ApiClientEx.prototype.getIntros = function(itemId) { - return isLocalId(itemId) ? Promise.resolve({ - Items: [], - TotalRecordCount: 0 - }) : ApiClient.prototype.getIntros.call(this, itemId) - }, ApiClientEx.prototype.getInstantMixFromItem = function(itemId, options) { - return isLocalId(itemId) ? Promise.resolve({ - Items: [], - TotalRecordCount: 0 - }) : ApiClient.prototype.getInstantMixFromItem.call(this, itemId, options) - }, ApiClientEx.prototype.getItemDownloadUrl = function(itemId) { - if (isLocalId(itemId)) { - var serverInfo = this.serverInfo(); - if (serverInfo) return localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(itemId)).then(function(item) { - return Promise.resolve(item.LocalPath) - }) - } - return ApiClient.prototype.getItemDownloadUrl.call(this, itemId) - }, ApiClientEx -}); diff --git a/src/libraries/apiclient/apiclientcore.js b/src/libraries/apiclient/apiclientcore.js deleted file mode 100644 index 557a4e1033..0000000000 --- a/src/libraries/apiclient/apiclientcore.js +++ /dev/null @@ -1,1568 +0,0 @@ -define(["events", "appStorage"], function(events, appStorage) { - "use strict"; - - function redetectBitrate(instance) { - stopBitrateDetection(instance), instance.accessToken() && !1 !== instance.enableAutomaticBitrateDetection && setTimeout(redetectBitrateInternal.bind(instance), 6e3) - } - - function redetectBitrateInternal() { - this.accessToken() && this.detectBitrate() - } - - function stopBitrateDetection(instance) { - instance.detectTimeout && clearTimeout(instance.detectTimeout) - } - - function replaceAll(originalString, strReplace, strWith) { - var reg = new RegExp(strReplace, "ig"); - return originalString.replace(reg, strWith) - } - - function onFetchFail(instance, url, response) { - events.trigger(instance, "requestfail", [{ - url: url, - status: response.status, - errorCode: response.headers ? response.headers.get("X-Application-Error-Code") : null - }]) - } - - function paramsToString(params) { - var values = []; - for (var key in params) { - var value = params[key]; - null !== value && void 0 !== value && "" !== value && values.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)) - } - return values.join("&") - } - - function fetchWithTimeout(url, options, timeoutMs) { - return new Promise(function(resolve, reject) { - var timeout = setTimeout(reject, timeoutMs); - options = options || {}, options.credentials = "same-origin", fetch(url, options).then(function(response) { - clearTimeout(timeout), resolve(response) - }, function(error) { - clearTimeout(timeout), reject(error) - }) - }) - } - - function getFetchPromise(request) { - var headers = request.headers || {}; - "json" === request.dataType && (headers.accept = "application/json"); - var fetchRequest = { - headers: headers, - method: request.type, - credentials: "same-origin" - }, - contentType = request.contentType; - return request.data && ("string" == typeof request.data ? fetchRequest.body = request.data : (fetchRequest.body = paramsToString(request.data), contentType = contentType || "application/x-www-form-urlencoded; charset=UTF-8")), contentType && (headers["Content-Type"] = contentType), request.timeout ? fetchWithTimeout(request.url, fetchRequest, request.timeout) : fetch(request.url, fetchRequest) - } - - function ApiClient(serverAddress, appName, appVersion, deviceName, deviceId) { - if (!serverAddress) { - throw new Error("Must supply a serverAddress"); - } - - console.debug("ApiClient serverAddress: " + serverAddress); - console.debug("ApiClient appName: " + appName); - console.debug("ApiClient appVersion: " + appVersion); - console.debug("ApiClient deviceName: " + deviceName); - console.debug("ApiClient deviceId: " + deviceId); - - this._serverInfo = {}; - this._serverAddress = serverAddress; - this._deviceId = deviceId; - this._deviceName = deviceName; - this._appName = appName; - this._appVersion = appVersion; - } - - function setSavedEndpointInfo(instance, info) { - instance._endPointInfo = info - } - - function getTryConnectPromise(instance, url, state, resolve, reject) { - console.debug("getTryConnectPromise " + url); - fetchWithTimeout(instance.getUrl("system/info/public", null, url), { - method: "GET", - accept: "application/json" - }, 15e3).then(function() { - state.resolved || (state.resolved = !0, console.debug("Reconnect succeeded to " + url), instance.serverAddress(url), resolve()) - }, function() { - state.resolved || (console.error("Reconnect failed to " + url), ++state.rejects >= state.numAddresses && reject()) - }) - } - - function tryReconnectInternal(instance) { - var addresses = [], - addressesStrings = [], - serverInfo = instance.serverInfo(); - return serverInfo.LocalAddress && -1 === addressesStrings.indexOf(serverInfo.LocalAddress) && (addresses.push({ - url: serverInfo.LocalAddress, - timeout: 0 - }), addressesStrings.push(addresses[addresses.length - 1].url)), serverInfo.ManualAddress && -1 === addressesStrings.indexOf(serverInfo.ManualAddress) && (addresses.push({ - url: serverInfo.ManualAddress, - timeout: 100 - }), addressesStrings.push(addresses[addresses.length - 1].url)), serverInfo.RemoteAddress && -1 === addressesStrings.indexOf(serverInfo.RemoteAddress) && (addresses.push({ - url: serverInfo.RemoteAddress, - timeout: 200 - }), addressesStrings.push(addresses[addresses.length - 1].url)), console.debug("tryReconnect: " + addressesStrings.join("|")), new Promise(function(resolve, reject) { - var state = {}; - state.numAddresses = addresses.length, state.rejects = 0, addresses.map(function(url) { - setTimeout(function() { - state.resolved || getTryConnectPromise(instance, url.url, state, resolve, reject) - }, url.timeout) - }) - }) - } - - function tryReconnect(instance, retryCount) { - return retryCount = retryCount || 0, retryCount >= 20 ? Promise.reject() : tryReconnectInternal(instance).catch(function(err) { - return console.error("error in tryReconnectInternal: " + (err || "")), new Promise(function(resolve, reject) { - setTimeout(function() { - tryReconnect(instance, retryCount + 1).then(resolve, reject) - }, 500) - }) - }) - } - - function getCachedUser(instance, userId) { - var serverId = instance.serverId(); - if (!serverId) return null; - var json = appStorage.getItem("user-" + userId + "-" + serverId); - return json ? JSON.parse(json) : null - } - - function onWebSocketMessage(msg) { - var instance = this; - msg = JSON.parse(msg.data), onMessageReceivedInternal(instance, msg) - } - - function onMessageReceivedInternal(instance, msg) { - var messageId = msg.MessageId; - if (messageId) { - if (messageIdsReceived[messageId]) return; - messageIdsReceived[messageId] = !0 - } - if ("UserDeleted" === msg.MessageType) instance._currentUser = null; - else if ("UserUpdated" === msg.MessageType || "UserConfigurationUpdated" === msg.MessageType) { - var user = msg.Data; - user.Id === instance.getCurrentUserId() && (instance._currentUser = null) - } - events.trigger(instance, "message", [msg]) - } - - function onWebSocketOpen() { - var instance = this; - console.debug("web socket connection opened"), events.trigger(instance, "websocketopen") - } - - function onWebSocketError() { - var instance = this; - events.trigger(instance, "websocketerror") - } - - function setSocketOnClose(apiClient, socket) { - socket.onclose = function() { - console.debug("web socket closed"); - if (apiClient._webSocket === socket) { - console.debug("nulling out web socket"); - apiClient._webSocket = null; - } - setTimeout(function() { - events.trigger(apiClient, "websocketclose") - }, 0) - } - } - - function normalizeReturnBitrate(instance, bitrate) { - if (!bitrate) return instance.lastDetectedBitrate ? instance.lastDetectedBitrate : Promise.reject(); - var result = Math.round(.7 * bitrate); - if (instance.getMaxBandwidth) { - var maxRate = instance.getMaxBandwidth(); - maxRate && (result = Math.min(result, maxRate)) - } - return instance.lastDetectedBitrate = result, instance.lastDetectedBitrateTime = (new Date).getTime(), result - } - - function detectBitrateInternal(instance, tests, index, currentBitrate) { - if (index >= tests.length) return normalizeReturnBitrate(instance, currentBitrate); - var test = tests[index]; - return instance.getDownloadSpeed(test.bytes).then(function(bitrate) { - return bitrate < test.threshold ? normalizeReturnBitrate(instance, bitrate) : detectBitrateInternal(instance, tests, index + 1, bitrate) - }, function() { - return normalizeReturnBitrate(instance, currentBitrate) - }) - } - - function detectBitrateWithEndpointInfo(instance, endpointInfo) { - if (endpointInfo.IsInNetwork) { - return instance.lastDetectedBitrate = 14e7, instance.lastDetectedBitrateTime = (new Date).getTime(), 14e7 - } - return detectBitrateInternal(instance, [{ - bytes: 5e5, - threshold: 5e5 - }, { - bytes: 1e6, - threshold: 2e7 - }, { - bytes: 3e6, - threshold: 5e7 - }], 0) - } - - function getRemoteImagePrefix(instance, options) { - var urlPrefix; - return options.artist ? (urlPrefix = "Artists/" + instance.encodeName(options.artist), delete options.artist) : options.person ? (urlPrefix = "Persons/" + instance.encodeName(options.person), delete options.person) : options.genre ? (urlPrefix = "Genres/" + instance.encodeName(options.genre), delete options.genre) : options.musicGenre ? (urlPrefix = "MusicGenres/" + instance.encodeName(options.musicGenre), delete options.musicGenre) : options.studio ? (urlPrefix = "Studios/" + instance.encodeName(options.studio), delete options.studio) : (urlPrefix = "Items/" + options.itemId, delete options.itemId), urlPrefix - } - - function normalizeImageOptions(instance, options) { - var ratio = window.devicePixelRatio; - ratio && (options.minScale && (ratio = Math.max(options.minScale, ratio)), options.width && (options.width = Math.round(options.width * ratio)), options.height && (options.height = Math.round(options.height * ratio)), options.maxWidth && (options.maxWidth = Math.round(options.maxWidth * ratio)), options.maxHeight && (options.maxHeight = Math.round(options.maxHeight * ratio))), options.quality = options.quality || instance.getDefaultImageQuality(options.type), instance.normalizeImageOptions && instance.normalizeImageOptions(options) - } - - function compareVersions(a, b) { - a = a.split("."), b = b.split("."); - for (var i = 0, length = Math.max(a.length, b.length); i < length; i++) { - var aVal = parseInt(a[i] || "0"), - bVal = parseInt(b[i] || "0"); - if (aVal < bVal) return -1; - if (aVal > bVal) return 1 - } - return 0 - } - ApiClient.prototype.appName = function() { - return this._appName - }, ApiClient.prototype.setRequestHeaders = function(headers) { - var currentServerInfo = this.serverInfo(), - appName = this._appName, - accessToken = currentServerInfo.AccessToken, - values = []; - if (appName && values.push('Client="' + appName + '"'), this._deviceName && values.push('Device="' + this._deviceName + '"'), this._deviceId && values.push('DeviceId="' + this._deviceId + '"'), this._appVersion && values.push('Version="' + this._appVersion + '"'), accessToken && values.push('Token="' + accessToken + '"'), values.length) { - var auth = "MediaBrowser " + values.join(", "); - headers["X-Emby-Authorization"] = auth - } - }, ApiClient.prototype.appVersion = function() { - return this._appVersion - }, ApiClient.prototype.deviceName = function() { - return this._deviceName - }, ApiClient.prototype.deviceId = function() { - return this._deviceId - }, ApiClient.prototype.serverAddress = function(val) { - if (null != val) { - if (0 !== val.toLowerCase().indexOf("http")) throw new Error("Invalid url: " + val); - var changed = val !== this._serverAddress; - this._serverAddress = val, this.onNetworkChange(), changed && events.trigger(this, "serveraddresschanged") - } - return this._serverAddress - }, ApiClient.prototype.onNetworkChange = function() { - this.lastDetectedBitrate = 0, this.lastDetectedBitrateTime = 0, setSavedEndpointInfo(this, null), redetectBitrate(this) - }, ApiClient.prototype.getUrl = function(name, params, serverAddress) { - if (!name) throw new Error("Url name cannot be empty"); - var url = serverAddress || this._serverAddress; - if (!url) throw new Error("serverAddress is yet not set"); - var lowered = url.toLowerCase(); - return "/" !== name.charAt(0) && (url += "/"), url += name, params && (params = paramsToString(params)) && (url += "?" + params), url - }, ApiClient.prototype.fetchWithFailover = function(request, enableReconnection) { - console.debug("Requesting " + request.url), request.timeout = 3e4; - var instance = this; - return getFetchPromise(request).then(function(response) { - return instance.lastFetch = (new Date).getTime(), response.status < 400 ? "json" === request.dataType || "application/json" === request.headers.accept ? response.json() : "text" === request.dataType || 0 === (response.headers.get("Content-Type") || "").toLowerCase().indexOf("text/") ? response.text() : response : (onFetchFail(instance, request.url, response), Promise.reject(response)) - }, function(error) { - if (error ? console.error("Request failed to " + request.url + " " + (error.status || "") + " " + error.toString()) : console.error("Request timed out to " + request.url), error && error.status || !enableReconnection) throw console.error("Reporting request failure"), onFetchFail(instance, request.url, {}), error; - console.debug("Attempting reconnection"); - var previousServerAddress = instance.serverAddress(); - return tryReconnect(instance).then(function() { - return console.debug("Reconnect succeesed"), request.url = request.url.replace(previousServerAddress, instance.serverAddress()), instance.fetchWithFailover(request, !1) - }, function(innerError) { - throw console.error("Reconnect failed"), onFetchFail(instance, request.url, {}), innerError - }) - }) - }, ApiClient.prototype.fetch = function(request, includeAuthorization) { - if (!request) throw new Error("Request cannot be null"); - if (request.headers = request.headers || {}, !1 !== includeAuthorization && this.setRequestHeaders(request.headers), !1 === this.enableAutomaticNetworking || "GET" !== request.type) { - console.debug("Requesting url without automatic networking: " + request.url); - var instance = this; - return getFetchPromise(request).then(function(response) { - return instance.lastFetch = (new Date).getTime(), response.status < 400 ? "json" === request.dataType || "application/json" === request.headers.accept ? response.json() : "text" === request.dataType || 0 === (response.headers.get("Content-Type") || "").toLowerCase().indexOf("text/") ? response.text() : response : (onFetchFail(instance, request.url, response), Promise.reject(response)) - }, function(error) { - throw onFetchFail(instance, request.url, {}), error - }) - } - return this.fetchWithFailover(request, !0) - }, ApiClient.prototype.setAuthenticationInfo = function(accessKey, userId) { - this._currentUser = null, this._serverInfo.AccessToken = accessKey, this._serverInfo.UserId = userId, redetectBitrate(this) - }, ApiClient.prototype.serverInfo = function(info) { - return info && (this._serverInfo = info), this._serverInfo - }, ApiClient.prototype.getCurrentUserId = function() { - return this._serverInfo.UserId - }, ApiClient.prototype.accessToken = function() { - return this._serverInfo.AccessToken - }, ApiClient.prototype.serverId = function() { - return this.serverInfo().Id - }, ApiClient.prototype.serverName = function() { - return this.serverInfo().Name - }, ApiClient.prototype.ajax = function(request, includeAuthorization) { - if (!request) throw new Error("Request cannot be null"); - return this.fetch(request, includeAuthorization) - }, ApiClient.prototype.getCurrentUser = function(enableCache) { - if (this._currentUser) return Promise.resolve(this._currentUser); - var userId = this.getCurrentUserId(); - if (!userId) return Promise.reject(); - var user, instance = this, - serverPromise = this.getUser(userId).then(function(user) { - return appStorage.setItem("user-" + user.Id + "-" + user.ServerId, JSON.stringify(user)), instance._currentUser = user, user - }, function(response) { - if (!response.status && userId && instance.accessToken() && (user = getCachedUser(instance, userId))) return Promise.resolve(user); - throw response - }); - return !this.lastFetch && !1 !== enableCache && (user = getCachedUser(instance, userId)) ? Promise.resolve(user) : serverPromise - }, ApiClient.prototype.isLoggedIn = function() { - var info = this.serverInfo(); - return !!(info && info.UserId && info.AccessToken) - }, ApiClient.prototype.logout = function() { - stopBitrateDetection(this), this.closeWebSocket(); - var done = function() { - appStorage.removeItem("user-" + this._currentUser.Id + "-" + this._currentUser.ServerId) - this.setAuthenticationInfo(null, null) - }.bind(this); - if (this.accessToken()) { - var url = this.getUrl("Sessions/Logout"); - return this.ajax({ - type: "POST", - url: url - }).then(done, done) - } - return done(), Promise.resolve() - }, ApiClient.prototype.authenticateUserByName = function(name, password) { - if (!name) return Promise.reject(); - var url = this.getUrl("Users/authenticatebyname"), - instance = this; - return new Promise(function(resolve, reject) { - var postData = { - Username: name, - Pw: password || "" - }; - instance.ajax({ - type: "POST", - url: url, - data: JSON.stringify(postData), - dataType: "json", - contentType: "application/json" - }).then(function(result) { - var afterOnAuthenticated = function() { - redetectBitrate(instance), resolve(result) - }; - instance.onAuthenticated ? instance.onAuthenticated(instance, result).then(afterOnAuthenticated) : afterOnAuthenticated() - }, reject) - }) - }, ApiClient.prototype.ensureWebSocket = function() { - if (!this.isWebSocketOpenOrConnecting() && this.isWebSocketSupported()) try { - this.openWebSocket() - } catch (err) { - console.error("error opening web socket: " + err) - } - }; - var messageIdsReceived = {}; - return ApiClient.prototype.openWebSocket = function() { - var accessToken = this.accessToken(); - if (!accessToken) throw new Error("Cannot open web socket without access token."); - var url = this.getUrl("socket"); - url = replaceAll(url, "emby/socket", "embywebsocket"), url = replaceAll(url, "https:", "wss:"), url = replaceAll(url, "http:", "ws:"), url += "?api_key=" + accessToken, url += "&deviceId=" + this.deviceId(), console.debug("opening web socket with url: " + url); - var webSocket = new WebSocket(url); - webSocket.onmessage = onWebSocketMessage.bind(this), webSocket.onopen = onWebSocketOpen.bind(this), webSocket.onerror = onWebSocketError.bind(this), setSocketOnClose(this, webSocket), this._webSocket = webSocket - }, ApiClient.prototype.closeWebSocket = function() { - var socket = this._webSocket; - socket && socket.readyState === WebSocket.OPEN && socket.close() - }, ApiClient.prototype.sendWebSocketMessage = function(name, data) { - console.debug("Sending web socket message: " + name); - var msg = { - MessageType: name - }; - data && (msg.Data = data), msg = JSON.stringify(msg), this._webSocket.send(msg) - }, ApiClient.prototype.sendMessage = function(name, data) { - this.isWebSocketOpen() && this.sendWebSocketMessage(name, data) - }, ApiClient.prototype.isMessageChannelOpen = function() { - return this.isWebSocketOpen() - }, ApiClient.prototype.isWebSocketOpen = function() { - var socket = this._webSocket; - return !!socket && socket.readyState === WebSocket.OPEN - }, ApiClient.prototype.isWebSocketOpenOrConnecting = function() { - var socket = this._webSocket; - return !!socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) - }, ApiClient.prototype.get = function(url) { - return this.ajax({ - type: "GET", - url: url - }) - }, ApiClient.prototype.getJSON = function(url, includeAuthorization) { - return this.fetch({ - url: url, - type: "GET", - dataType: "json", - headers: { - accept: "application/json" - } - }, includeAuthorization) - }, ApiClient.prototype.updateServerInfo = function(server, serverUrl) { - if (null == server) throw new Error("server cannot be null"); - if (this.serverInfo(server), !serverUrl) throw new Error("serverUrl cannot be null. serverInfo: " + JSON.stringify(server)); - console.debug("Setting server address to " + serverUrl), this.serverAddress(serverUrl) - }, ApiClient.prototype.isWebSocketSupported = function() { - try { - return null != WebSocket - } catch (err) { - return !1 - } - }, ApiClient.prototype.clearAuthenticationInfo = function() { - this.setAuthenticationInfo(null, null) - }, ApiClient.prototype.encodeName = function(name) { - name = name.split("/").join("-"), name = name.split("&").join("-"), name = name.split("?").join("-"); - var val = paramsToString({ - name: name - }); - return val.substring(val.indexOf("=") + 1).replace("'", "%27") - }, ApiClient.prototype.getDownloadSpeed = function(byteSize) { - var url = this.getUrl("Playback/BitrateTest", { - Size: byteSize - }), - now = (new Date).getTime(); - return this.ajax({ - type: "GET", - url: url, - timeout: 5e3 - }).then(function() { - var responseTimeSeconds = ((new Date).getTime() - now) / 1e3, - bytesPerSecond = byteSize / responseTimeSeconds; - return Math.round(8 * bytesPerSecond) - }) - }, ApiClient.prototype.detectBitrate = function(force) { - if (!force && this.lastDetectedBitrate && (new Date).getTime() - (this.lastDetectedBitrateTime || 0) <= 36e5) return Promise.resolve(this.lastDetectedBitrate); - var instance = this; - return this.getEndpointInfo().then(function(info) { - return detectBitrateWithEndpointInfo(instance, info) - }, function(info) { - return detectBitrateWithEndpointInfo(instance, {}) - }) - }, ApiClient.prototype.getItem = function(userId, itemId) { - if (!itemId) throw new Error("null itemId"); - var url = userId ? this.getUrl("Users/" + userId + "/Items/" + itemId) : this.getUrl("Items/" + itemId); - return this.getJSON(url) - }, ApiClient.prototype.getRootFolder = function(userId) { - if (!userId) throw new Error("null userId"); - var url = this.getUrl("Users/" + userId + "/Items/Root"); - return this.getJSON(url) - }, ApiClient.prototype.getNotificationSummary = function(userId) { - if (!userId) throw new Error("null userId"); - var url = this.getUrl("Notifications/" + userId + "/Summary"); - return this.getJSON(url) - }, ApiClient.prototype.getNotifications = function(userId, options) { - if (!userId) throw new Error("null userId"); - var url = this.getUrl("Notifications/" + userId, options || {}); - return this.getJSON(url) - }, ApiClient.prototype.markNotificationsRead = function(userId, idList, isRead) { - if (!userId) throw new Error("null userId"); - if (!idList) throw new Error("null idList"); - var suffix = isRead ? "Read" : "Unread", - params = { - UserId: userId, - Ids: idList.join(",") - }, - url = this.getUrl("Notifications/" + userId + "/" + suffix, params); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.getRemoteImageProviders = function(options) { - if (!options) throw new Error("null options"); - var urlPrefix = getRemoteImagePrefix(this, options), - url = this.getUrl(urlPrefix + "/RemoteImages/Providers", options); - return this.getJSON(url) - }, ApiClient.prototype.getAvailableRemoteImages = function(options) { - if (!options) throw new Error("null options"); - var urlPrefix = getRemoteImagePrefix(this, options), - url = this.getUrl(urlPrefix + "/RemoteImages", options); - return this.getJSON(url) - }, ApiClient.prototype.downloadRemoteImage = function(options) { - if (!options) throw new Error("null options"); - var urlPrefix = getRemoteImagePrefix(this, options), - url = this.getUrl(urlPrefix + "/RemoteImages/Download", options); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.getRecordingFolders = function(userId) { - var url = this.getUrl("LiveTv/Recordings/Folders", { - userId: userId - }); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvInfo = function(options) { - var url = this.getUrl("LiveTv/Info", options || {}); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvGuideInfo = function(options) { - var url = this.getUrl("LiveTv/GuideInfo", options || {}); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvChannel = function(id, userId) { - if (!id) throw new Error("null id"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("LiveTv/Channels/" + id, options); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvChannels = function(options) { - var url = this.getUrl("LiveTv/Channels", options || {}); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvPrograms = function(options) { - return options = options || {}, options.channelIds && options.channelIds.length > 1800 ? this.ajax({ - type: "POST", - url: this.getUrl("LiveTv/Programs"), - data: JSON.stringify(options), - contentType: "application/json", - dataType: "json" - }) : this.ajax({ - type: "GET", - url: this.getUrl("LiveTv/Programs", options), - dataType: "json" - }) - }, ApiClient.prototype.getLiveTvRecommendedPrograms = function(options) { - return options = options || {}, this.ajax({ - type: "GET", - url: this.getUrl("LiveTv/Programs/Recommended", options), - dataType: "json" - }) - }, ApiClient.prototype.getLiveTvRecordings = function(options) { - var url = this.getUrl("LiveTv/Recordings", options || {}); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvRecordingSeries = function(options) { - var url = this.getUrl("LiveTv/Recordings/Series", options || {}); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvRecordingGroups = function(options) { - var url = this.getUrl("LiveTv/Recordings/Groups", options || {}); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvRecordingGroup = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("LiveTv/Recordings/Groups/" + id); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvRecording = function(id, userId) { - if (!id) throw new Error("null id"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("LiveTv/Recordings/" + id, options); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvProgram = function(id, userId) { - if (!id) throw new Error("null id"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("LiveTv/Programs/" + id, options); - return this.getJSON(url) - }, ApiClient.prototype.deleteLiveTvRecording = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("LiveTv/Recordings/" + id); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.cancelLiveTvTimer = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("LiveTv/Timers/" + id); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.getLiveTvTimers = function(options) { - var url = this.getUrl("LiveTv/Timers", options || {}); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvTimer = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("LiveTv/Timers/" + id); - return this.getJSON(url) - }, ApiClient.prototype.getNewLiveTvTimerDefaults = function(options) { - options = options || {}; - var url = this.getUrl("LiveTv/Timers/Defaults", options); - return this.getJSON(url) - }, ApiClient.prototype.createLiveTvTimer = function(item) { - if (!item) throw new Error("null item"); - var url = this.getUrl("LiveTv/Timers"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(item), - contentType: "application/json" - }) - }, ApiClient.prototype.updateLiveTvTimer = function(item) { - if (!item) throw new Error("null item"); - var url = this.getUrl("LiveTv/Timers/" + item.Id); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(item), - contentType: "application/json" - }) - }, ApiClient.prototype.resetLiveTvTuner = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("LiveTv/Tuners/" + id + "/Reset"); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.getLiveTvSeriesTimers = function(options) { - var url = this.getUrl("LiveTv/SeriesTimers", options || {}); - return this.getJSON(url) - }, ApiClient.prototype.getLiveTvSeriesTimer = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("LiveTv/SeriesTimers/" + id); - return this.getJSON(url) - }, ApiClient.prototype.cancelLiveTvSeriesTimer = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("LiveTv/SeriesTimers/" + id); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.createLiveTvSeriesTimer = function(item) { - if (!item) throw new Error("null item"); - var url = this.getUrl("LiveTv/SeriesTimers"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(item), - contentType: "application/json" - }) - }, ApiClient.prototype.updateLiveTvSeriesTimer = function(item) { - if (!item) throw new Error("null item"); - var url = this.getUrl("LiveTv/SeriesTimers/" + item.Id); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(item), - contentType: "application/json" - }) - }, ApiClient.prototype.getRegistrationInfo = function(feature) { - var url = this.getUrl("Registrations/" + feature); - return this.getJSON(url) - }, ApiClient.prototype.getSystemInfo = function() { - var url = this.getUrl("System/Info"), - instance = this; - return this.getJSON(url).then(function(info) { - return instance.setSystemInfo(info), Promise.resolve(info) - }) - }, ApiClient.prototype.getSyncStatus = function(itemId) { - var url = this.getUrl("Sync/" + itemId + "/Status"); - return this.ajax({ - url: url, - type: "POST", - dataType: "json", - contentType: "application/json", - data: JSON.stringify({ - TargetId: this.deviceId() - }) - }) - }, ApiClient.prototype.getPublicSystemInfo = function() { - var url = this.getUrl("System/Info/Public"), - instance = this; - return this.getJSON(url).then(function(info) { - return instance.setSystemInfo(info), Promise.resolve(info) - }) - }, ApiClient.prototype.getInstantMixFromItem = function(itemId, options) { - var url = this.getUrl("Items/" + itemId + "/InstantMix", options); - return this.getJSON(url) - }, ApiClient.prototype.getEpisodes = function(itemId, options) { - var url = this.getUrl("Shows/" + itemId + "/Episodes", options); - return this.getJSON(url) - }, ApiClient.prototype.getDisplayPreferences = function(id, userId, app) { - var url = this.getUrl("DisplayPreferences/" + id, { - userId: userId, - client: app - }); - return this.getJSON(url) - }, ApiClient.prototype.updateDisplayPreferences = function(id, obj, userId, app) { - var url = this.getUrl("DisplayPreferences/" + id, { - userId: userId, - client: app - }); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(obj), - contentType: "application/json" - }) - }, ApiClient.prototype.getSeasons = function(itemId, options) { - var url = this.getUrl("Shows/" + itemId + "/Seasons", options); - return this.getJSON(url) - }, ApiClient.prototype.getSimilarItems = function(itemId, options) { - var url = this.getUrl("Items/" + itemId + "/Similar", options); - return this.getJSON(url) - }, ApiClient.prototype.getCultures = function() { - var url = this.getUrl("Localization/cultures"); - return this.getJSON(url) - }, ApiClient.prototype.getCountries = function() { - var url = this.getUrl("Localization/countries"); - return this.getJSON(url) - }, ApiClient.prototype.getPlaybackInfo = function(itemId, options, deviceProfile) { - var postData = { - DeviceProfile: deviceProfile - }; - return this.ajax({ - url: this.getUrl("Items/" + itemId + "/PlaybackInfo", options), - type: "POST", - data: JSON.stringify(postData), - contentType: "application/json", - dataType: "json" - }) - }, ApiClient.prototype.getLiveStreamMediaInfo = function(liveStreamId) { - var postData = { - LiveStreamId: liveStreamId - }; - return this.ajax({ - url: this.getUrl("LiveStreams/MediaInfo"), - type: "POST", - data: JSON.stringify(postData), - contentType: "application/json", - dataType: "json" - }) - }, ApiClient.prototype.getIntros = function(itemId) { - return this.getJSON(this.getUrl("Users/" + this.getCurrentUserId() + "/Items/" + itemId + "/Intros")) - }, ApiClient.prototype.getDirectoryContents = function(path, options) { - if (!path) throw new Error("null path"); - if ("string" != typeof path) throw new Error("invalid path"); - options = options || {}, options.path = path; - var url = this.getUrl("Environment/DirectoryContents", options); - return this.getJSON(url) - }, ApiClient.prototype.getNetworkShares = function(path) { - if (!path) throw new Error("null path"); - var options = {}; - options.path = path; - var url = this.getUrl("Environment/NetworkShares", options); - return this.getJSON(url) - }, ApiClient.prototype.getParentPath = function(path) { - if (!path) throw new Error("null path"); - var options = {}; - options.path = path; - var url = this.getUrl("Environment/ParentPath", options); - return this.ajax({ - type: "GET", - url: url, - dataType: "text" - }) - }, ApiClient.prototype.getDrives = function() { - var url = this.getUrl("Environment/Drives"); - return this.getJSON(url) - }, ApiClient.prototype.getNetworkDevices = function() { - var url = this.getUrl("Environment/NetworkDevices"); - return this.getJSON(url) - }, ApiClient.prototype.cancelPackageInstallation = function(installationId) { - if (!installationId) throw new Error("null installationId"); - var url = this.getUrl("Packages/Installing/" + installationId); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.refreshItem = function(itemId, options) { - if (!itemId) throw new Error("null itemId"); - var url = this.getUrl("Items/" + itemId + "/Refresh", options || {}); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.installPlugin = function(name, guid, updateClass, version) { - if (!name) throw new Error("null name"); - if (!updateClass) throw new Error("null updateClass"); - var options = { - updateClass: updateClass, - AssemblyGuid: guid - }; - version && (options.version = version); - var url = this.getUrl("Packages/Installed/" + name, options); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.restartServer = function() { - var url = this.getUrl("System/Restart"); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.shutdownServer = function() { - var url = this.getUrl("System/Shutdown"); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.getPackageInfo = function(name, guid) { - if (!name) throw new Error("null name"); - var options = { - AssemblyGuid: guid - }, - url = this.getUrl("Packages/" + name, options); - return this.getJSON(url) - }, ApiClient.prototype.getVirtualFolders = function() { - var url = "Library/VirtualFolders"; - return url = this.getUrl(url), this.getJSON(url) - }, ApiClient.prototype.getPhysicalPaths = function() { - var url = this.getUrl("Library/PhysicalPaths"); - return this.getJSON(url) - }, ApiClient.prototype.getServerConfiguration = function() { - var url = this.getUrl("System/Configuration"); - return this.getJSON(url) - }, ApiClient.prototype.getDevicesOptions = function() { - var url = this.getUrl("System/Configuration/devices"); - return this.getJSON(url) - }, ApiClient.prototype.getContentUploadHistory = function() { - var url = this.getUrl("Devices/CameraUploads", { - DeviceId: this.deviceId() - }); - return this.getJSON(url) - }, ApiClient.prototype.getNamedConfiguration = function(name) { - var url = this.getUrl("System/Configuration/" + name); - return this.getJSON(url) - }, ApiClient.prototype.getScheduledTasks = function(options) { - options = options || {}; - var url = this.getUrl("ScheduledTasks", options); - return this.getJSON(url) - }, ApiClient.prototype.startScheduledTask = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("ScheduledTasks/Running/" + id); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.getScheduledTask = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("ScheduledTasks/" + id); - return this.getJSON(url) - }, ApiClient.prototype.getNextUpEpisodes = function(options) { - var url = this.getUrl("Shows/NextUp", options); - return this.getJSON(url) - }, ApiClient.prototype.stopScheduledTask = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("ScheduledTasks/Running/" + id); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.getPluginConfiguration = function(id) { - if (!id) throw new Error("null Id"); - var url = this.getUrl("Plugins/" + id + "/Configuration"); - return this.getJSON(url) - }, ApiClient.prototype.getAvailablePlugins = function(options) { - options = options || {}, options.PackageType = "UserInstalled"; - var url = this.getUrl("Packages", options); - return this.getJSON(url) - }, ApiClient.prototype.uninstallPlugin = function(id) { - if (!id) throw new Error("null Id"); - var url = this.getUrl("Plugins/" + id); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.removeVirtualFolder = function(name, refreshLibrary) { - if (!name) throw new Error("null name"); - var url = "Library/VirtualFolders"; - return url = this.getUrl(url, { - refreshLibrary: !!refreshLibrary, - name: name - }), this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.addVirtualFolder = function(name, type, refreshLibrary, libraryOptions) { - if (!name) throw new Error("null name"); - var options = {}; - type && (options.collectionType = type), options.refreshLibrary = !!refreshLibrary, options.name = name; - var url = "Library/VirtualFolders"; - return url = this.getUrl(url, options), this.ajax({ - type: "POST", - url: url, - data: JSON.stringify({ - LibraryOptions: libraryOptions - }), - contentType: "application/json" - }) - }, ApiClient.prototype.updateVirtualFolderOptions = function(id, libraryOptions) { - if (!id) throw new Error("null name"); - var url = "Library/VirtualFolders/LibraryOptions"; - return url = this.getUrl(url), this.ajax({ - type: "POST", - url: url, - data: JSON.stringify({ - Id: id, - LibraryOptions: libraryOptions - }), - contentType: "application/json" - }) - }, ApiClient.prototype.renameVirtualFolder = function(name, newName, refreshLibrary) { - if (!name) throw new Error("null name"); - var url = "Library/VirtualFolders/Name"; - return url = this.getUrl(url, { - refreshLibrary: !!refreshLibrary, - newName: newName, - name: name - }), this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.addMediaPath = function(virtualFolderName, mediaPath, networkSharePath, refreshLibrary) { - if (!virtualFolderName) throw new Error("null virtualFolderName"); - if (!mediaPath) throw new Error("null mediaPath"); - var url = "Library/VirtualFolders/Paths", - pathInfo = { - Path: mediaPath - }; - return networkSharePath && (pathInfo.NetworkPath = networkSharePath), url = this.getUrl(url, { - refreshLibrary: !!refreshLibrary - }), this.ajax({ - type: "POST", - url: url, - data: JSON.stringify({ - Name: virtualFolderName, - PathInfo: pathInfo - }), - contentType: "application/json" - }) - }, ApiClient.prototype.updateMediaPath = function(virtualFolderName, pathInfo) { - if (!virtualFolderName) throw new Error("null virtualFolderName"); - if (!pathInfo) throw new Error("null pathInfo"); - var url = "Library/VirtualFolders/Paths/Update"; - return url = this.getUrl(url), this.ajax({ - type: "POST", - url: url, - data: JSON.stringify({ - Name: virtualFolderName, - PathInfo: pathInfo - }), - contentType: "application/json" - }) - }, ApiClient.prototype.removeMediaPath = function(virtualFolderName, mediaPath, refreshLibrary) { - if (!virtualFolderName) throw new Error("null virtualFolderName"); - if (!mediaPath) throw new Error("null mediaPath"); - var url = "Library/VirtualFolders/Paths"; - return url = this.getUrl(url, { - refreshLibrary: !!refreshLibrary, - path: mediaPath, - name: virtualFolderName - }), this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.deleteUser = function(id) { - if (!id) throw new Error("null id"); - var url = this.getUrl("Users/" + id); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.deleteUserImage = function(userId, imageType, imageIndex) { - if (!userId) throw new Error("null userId"); - if (!imageType) throw new Error("null imageType"); - var url = this.getUrl("Users/" + userId + "/Images/" + imageType); - return null != imageIndex && (url += "/" + imageIndex), this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.deleteItemImage = function(itemId, imageType, imageIndex) { - if (!imageType) throw new Error("null imageType"); - var url = this.getUrl("Items/" + itemId + "/Images"); - return url += "/" + imageType, null != imageIndex && (url += "/" + imageIndex), this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.deleteItem = function(itemId) { - if (!itemId) throw new Error("null itemId"); - var url = this.getUrl("Items/" + itemId); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.stopActiveEncodings = function(playSessionId) { - var options = { - deviceId: this.deviceId() - }; - playSessionId && (options.PlaySessionId = playSessionId); - var url = this.getUrl("Videos/ActiveEncodings", options); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.reportCapabilities = function(options) { - var url = this.getUrl("Sessions/Capabilities/Full"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(options), - contentType: "application/json" - }) - }, ApiClient.prototype.updateItemImageIndex = function(itemId, imageType, imageIndex, newIndex) { - if (!imageType) throw new Error("null imageType"); - var options = { - newIndex: newIndex - }, - url = this.getUrl("Items/" + itemId + "/Images/" + imageType + "/" + imageIndex + "/Index", options); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.getItemImageInfos = function(itemId) { - var url = this.getUrl("Items/" + itemId + "/Images"); - return this.getJSON(url) - }, ApiClient.prototype.getCriticReviews = function(itemId, options) { - if (!itemId) throw new Error("null itemId"); - var url = this.getUrl("Items/" + itemId + "/CriticReviews", options); - return this.getJSON(url) - }, ApiClient.prototype.getItemDownloadUrl = function(itemId) { - if (!itemId) throw new Error("itemId cannot be empty"); - var url = "Items/" + itemId + "/Download"; - return this.getUrl(url, { - api_key: this.accessToken() - }) - }, ApiClient.prototype.getSessions = function(options) { - var url = this.getUrl("Sessions", options); - return this.getJSON(url) - }, ApiClient.prototype.uploadUserImage = function(userId, imageType, file) { - if (!userId) throw new Error("null userId"); - if (!imageType) throw new Error("null imageType"); - if (!file) throw new Error("File must be an image."); - if ("image/png" !== file.type && "image/jpeg" !== file.type && "image/jpeg" !== file.type) throw new Error("File must be an image."); - var instance = this; - return new Promise(function(resolve, reject) { - var reader = new FileReader; - reader.onerror = function() { - reject() - }, reader.onabort = function() { - reject() - }, reader.onload = function(e) { - var data = e.target.result.split(",")[1], - url = instance.getUrl("Users/" + userId + "/Images/" + imageType); - instance.ajax({ - type: "POST", - url: url, - data: data, - contentType: "image/" + file.name.substring(file.name.lastIndexOf(".") + 1) - }).then(resolve, reject) - }, reader.readAsDataURL(file) - }) - }, ApiClient.prototype.uploadItemImage = function(itemId, imageType, file) { - if (!itemId) throw new Error("null itemId"); - if (!imageType) throw new Error("null imageType"); - if (!file) throw new Error("File must be an image."); - if ("image/png" !== file.type && "image/jpeg" !== file.type && "image/jpeg" !== file.type) throw new Error("File must be an image."); - var url = this.getUrl("Items/" + itemId + "/Images"); - url += "/" + imageType; - var instance = this; - return new Promise(function(resolve, reject) { - var reader = new FileReader; - reader.onerror = function() { - reject() - }, reader.onabort = function() { - reject() - }, reader.onload = function(e) { - var data = e.target.result.split(",")[1]; - instance.ajax({ - type: "POST", - url: url, - data: data, - contentType: "image/" + file.name.substring(file.name.lastIndexOf(".") + 1) - }).then(resolve, reject) - }, reader.readAsDataURL(file) - }) - }, ApiClient.prototype.getInstalledPlugins = function() { - var options = {}, - url = this.getUrl("Plugins", options); - return this.getJSON(url) - }, ApiClient.prototype.getUser = function(id) { - if (!id) throw new Error("Must supply a userId"); - var url = this.getUrl("Users/" + id); - return this.getJSON(url) - }, ApiClient.prototype.getStudio = function(name, userId) { - if (!name) throw new Error("null name"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("Studios/" + this.encodeName(name), options); - return this.getJSON(url) - }, ApiClient.prototype.getGenre = function(name, userId) { - if (!name) throw new Error("null name"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("Genres/" + this.encodeName(name), options); - return this.getJSON(url) - }, ApiClient.prototype.getMusicGenre = function(name, userId) { - if (!name) throw new Error("null name"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("MusicGenres/" + this.encodeName(name), options); - return this.getJSON(url) - }, ApiClient.prototype.getArtist = function(name, userId) { - if (!name) throw new Error("null name"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("Artists/" + this.encodeName(name), options); - return this.getJSON(url) - }, ApiClient.prototype.getPerson = function(name, userId) { - if (!name) throw new Error("null name"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("Persons/" + this.encodeName(name), options); - return this.getJSON(url) - }, ApiClient.prototype.getPublicUsers = function() { - var url = this.getUrl("users/public"); - return this.ajax({ - type: "GET", - url: url, - dataType: "json" - }, !1) - }, ApiClient.prototype.getUsers = function(options) { - var url = this.getUrl("users", options || {}); - return this.getJSON(url) - }, ApiClient.prototype.getParentalRatings = function() { - var url = this.getUrl("Localization/ParentalRatings"); - return this.getJSON(url) - }, ApiClient.prototype.getDefaultImageQuality = function(imageType) { - return "backdrop" === imageType.toLowerCase() ? 80 : 90 - }, ApiClient.prototype.getUserImageUrl = function(userId, options) { - if (!userId) throw new Error("null userId"); - options = options || {}; - var url = "Users/" + userId + "/Images/" + options.type; - return null != options.index && (url += "/" + options.index), normalizeImageOptions(this, options), delete options.type, delete options.index, this.getUrl(url, options) - }, ApiClient.prototype.getImageUrl = function(itemId, options) { - if (!itemId) throw new Error("itemId cannot be empty"); - options = options || {}; - var url = "Items/" + itemId + "/Images/" + options.type; - return null != options.index && (url += "/" + options.index), options.quality = options.quality || this.getDefaultImageQuality(options.type), this.normalizeImageOptions && this.normalizeImageOptions(options), delete options.type, delete options.index, this.getUrl(url, options) - }, ApiClient.prototype.getScaledImageUrl = function(itemId, options) { - if (!itemId) throw new Error("itemId cannot be empty"); - options = options || {}; - var url = "Items/" + itemId + "/Images/" + options.type; - return null != options.index && (url += "/" + options.index), normalizeImageOptions(this, options), delete options.type, delete options.index, delete options.minScale, this.getUrl(url, options) - }, ApiClient.prototype.getThumbImageUrl = function(item, options) { - if (!item) throw new Error("null item"); - return options = options || {}, options.imageType = "thumb", item.ImageTags && item.ImageTags.Thumb ? (options.tag = item.ImageTags.Thumb, this.getImageUrl(item.Id, options)) : item.ParentThumbItemId ? (options.tag = item.ImageTags.ParentThumbImageTag, this.getImageUrl(item.ParentThumbItemId, options)) : null - }, ApiClient.prototype.updateUserPassword = function(userId, currentPassword, newPassword) { - if (!userId) return Promise.reject(); - var url = this.getUrl("Users/" + userId + "/Password"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify({ - CurrentPw: currentPassword || "", - NewPw: newPassword - }), - contentType: "application/json" - }) - }, ApiClient.prototype.updateEasyPassword = function(userId, newPassword) { - if (!userId) return void Promise.reject(); - var url = this.getUrl("Users/" + userId + "/EasyPassword"); - return this.ajax({ - type: "POST", - url: url, - data: { - NewPw: newPassword - } - }) - }, ApiClient.prototype.resetUserPassword = function(userId) { - if (!userId) throw new Error("null userId"); - var url = this.getUrl("Users/" + userId + "/Password"), - postData = {}; - return postData.resetPassword = !0, this.ajax({ - type: "POST", - url: url, - data: postData - }) - }, ApiClient.prototype.resetEasyPassword = function(userId) { - if (!userId) throw new Error("null userId"); - var url = this.getUrl("Users/" + userId + "/EasyPassword"), - postData = {}; - return postData.resetPassword = !0, this.ajax({ - type: "POST", - url: url, - data: postData - }) - }, ApiClient.prototype.updateServerConfiguration = function(configuration) { - if (!configuration) throw new Error("null configuration"); - var url = this.getUrl("System/Configuration"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(configuration), - contentType: "application/json" - }) - }, ApiClient.prototype.updateNamedConfiguration = function(name, configuration) { - if (!configuration) throw new Error("null configuration"); - var url = this.getUrl("System/Configuration/" + name); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(configuration), - contentType: "application/json" - }) - }, ApiClient.prototype.updateItem = function(item) { - if (!item) throw new Error("null item"); - var url = this.getUrl("Items/" + item.Id); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(item), - contentType: "application/json" - }) - }, ApiClient.prototype.updatePluginSecurityInfo = function(info) { - var url = this.getUrl("Plugins/SecurityInfo"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(info), - contentType: "application/json" - }) - }, ApiClient.prototype.createUser = function(user) { - var url = this.getUrl("Users/New"); - return this.ajax({ - type: "POST", - url: url, - data: user, - dataType: "json" - }) - }, ApiClient.prototype.updateUser = function(user) { - if (!user) throw new Error("null user"); - var url = this.getUrl("Users/" + user.Id); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(user), - contentType: "application/json" - }) - }, ApiClient.prototype.updateUserPolicy = function(userId, policy) { - if (!userId) throw new Error("null userId"); - if (!policy) throw new Error("null policy"); - var url = this.getUrl("Users/" + userId + "/Policy"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(policy), - contentType: "application/json" - }) - }, ApiClient.prototype.updateUserConfiguration = function(userId, configuration) { - if (!userId) throw new Error("null userId"); - if (!configuration) throw new Error("null configuration"); - var url = this.getUrl("Users/" + userId + "/Configuration"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(configuration), - contentType: "application/json" - }) - }, ApiClient.prototype.updateScheduledTaskTriggers = function(id, triggers) { - if (!id) throw new Error("null id"); - if (!triggers) throw new Error("null triggers"); - var url = this.getUrl("ScheduledTasks/" + id + "/Triggers"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(triggers), - contentType: "application/json" - }) - }, ApiClient.prototype.updatePluginConfiguration = function(id, configuration) { - if (!id) throw new Error("null Id"); - if (!configuration) throw new Error("null configuration"); - var url = this.getUrl("Plugins/" + id + "/Configuration"); - return this.ajax({ - type: "POST", - url: url, - data: JSON.stringify(configuration), - contentType: "application/json" - }) - }, ApiClient.prototype.getAncestorItems = function(itemId, userId) { - if (!itemId) throw new Error("null itemId"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("Items/" + itemId + "/Ancestors", options); - return this.getJSON(url) - }, ApiClient.prototype.getItems = function(userId, options) { - var url; - return url = "string" === (typeof userId).toString().toLowerCase() ? this.getUrl("Users/" + userId + "/Items", options) : this.getUrl("Items", options), this.getJSON(url) - }, ApiClient.prototype.getResumableItems = function(userId, options) { - return this.isMinServerVersion("3.2.33") ? this.getJSON(this.getUrl("Users/" + userId + "/Items/Resume", options)) : this.getItems(userId, Object.assign({ - SortBy: "DatePlayed", - SortOrder: "Descending", - Filters: "IsResumable", - Recursive: !0, - CollapseBoxSetItems: !1, - ExcludeLocationTypes: "Virtual" - }, options)) - }, ApiClient.prototype.getMovieRecommendations = function(options) { - return this.getJSON(this.getUrl("Movies/Recommendations", options)) - }, ApiClient.prototype.getUpcomingEpisodes = function(options) { - return this.getJSON(this.getUrl("Shows/Upcoming", options)) - }, ApiClient.prototype.getUserViews = function(options, userId) { - options = options || {}; - var url = this.getUrl("Users/" + (userId || this.getCurrentUserId()) + "/Views", options); - return this.getJSON(url) - }, ApiClient.prototype.getArtists = function(userId, options) { - if (!userId) throw new Error("null userId"); - options = options || {}, options.userId = userId; - var url = this.getUrl("Artists", options); - return this.getJSON(url) - }, ApiClient.prototype.getAlbumArtists = function(userId, options) { - if (!userId) throw new Error("null userId"); - options = options || {}, options.userId = userId; - var url = this.getUrl("Artists/AlbumArtists", options); - return this.getJSON(url) - }, ApiClient.prototype.getGenres = function(userId, options) { - if (!userId) throw new Error("null userId"); - options = options || {}, options.userId = userId; - var url = this.getUrl("Genres", options); - return this.getJSON(url) - }, ApiClient.prototype.getMusicGenres = function(userId, options) { - if (!userId) throw new Error("null userId"); - options = options || {}, options.userId = userId; - var url = this.getUrl("MusicGenres", options); - return this.getJSON(url) - }, ApiClient.prototype.getPeople = function(userId, options) { - if (!userId) throw new Error("null userId"); - options = options || {}, options.userId = userId; - var url = this.getUrl("Persons", options); - return this.getJSON(url) - }, ApiClient.prototype.getStudios = function(userId, options) { - if (!userId) throw new Error("null userId"); - options = options || {}, options.userId = userId; - var url = this.getUrl("Studios", options); - return this.getJSON(url) - }, ApiClient.prototype.getLocalTrailers = function(userId, itemId) { - if (!userId) throw new Error("null userId"); - if (!itemId) throw new Error("null itemId"); - var url = this.getUrl("Users/" + userId + "/Items/" + itemId + "/LocalTrailers"); - return this.getJSON(url) - }, ApiClient.prototype.getAdditionalVideoParts = function(userId, itemId) { - if (!itemId) throw new Error("null itemId"); - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("Videos/" + itemId + "/AdditionalParts", options); - return this.getJSON(url) - }, ApiClient.prototype.getThemeMedia = function(userId, itemId, inherit) { - if (!itemId) throw new Error("null itemId"); - var options = {}; - userId && (options.userId = userId), options.InheritFromParent = inherit || !1; - var url = this.getUrl("Items/" + itemId + "/ThemeMedia", options); - return this.getJSON(url) - }, ApiClient.prototype.getSearchHints = function(options) { - var url = this.getUrl("Search/Hints", options), - serverId = this.serverId(); - return this.getJSON(url).then(function(result) { - return result.SearchHints.forEach(function(i) { - i.ServerId = serverId - }), result - }) - }, ApiClient.prototype.getSpecialFeatures = function(userId, itemId) { - if (!userId) throw new Error("null userId"); - if (!itemId) throw new Error("null itemId"); - var url = this.getUrl("Users/" + userId + "/Items/" + itemId + "/SpecialFeatures"); - return this.getJSON(url) - }, ApiClient.prototype.getDateParamValue = function(date) { - function formatDigit(i) { - return i < 10 ? "0" + i : i - } - var d = date; - return "" + d.getFullYear() + formatDigit(d.getMonth() + 1) + formatDigit(d.getDate()) + formatDigit(d.getHours()) + formatDigit(d.getMinutes()) + formatDigit(d.getSeconds()) - }, ApiClient.prototype.markPlayed = function(userId, itemId, date) { - if (!userId) throw new Error("null userId"); - if (!itemId) throw new Error("null itemId"); - var options = {}; - date && (options.DatePlayed = this.getDateParamValue(date)); - var url = this.getUrl("Users/" + userId + "/PlayedItems/" + itemId, options); - return this.ajax({ - type: "POST", - url: url, - dataType: "json" - }) - }, ApiClient.prototype.markUnplayed = function(userId, itemId) { - if (!userId) throw new Error("null userId"); - if (!itemId) throw new Error("null itemId"); - var url = this.getUrl("Users/" + userId + "/PlayedItems/" + itemId); - return this.ajax({ - type: "DELETE", - url: url, - dataType: "json" - }) - }, ApiClient.prototype.updateFavoriteStatus = function(userId, itemId, isFavorite) { - if (!userId) throw new Error("null userId"); - if (!itemId) throw new Error("null itemId"); - var url = this.getUrl("Users/" + userId + "/FavoriteItems/" + itemId), - method = isFavorite ? "POST" : "DELETE"; - return this.ajax({ - type: method, - url: url, - dataType: "json" - }) - }, ApiClient.prototype.updateUserItemRating = function(userId, itemId, likes) { - if (!userId) throw new Error("null userId"); - if (!itemId) throw new Error("null itemId"); - var url = this.getUrl("Users/" + userId + "/Items/" + itemId + "/Rating", { - likes: likes - }); - return this.ajax({ - type: "POST", - url: url, - dataType: "json" - }) - }, ApiClient.prototype.getItemCounts = function(userId) { - var options = {}; - userId && (options.userId = userId); - var url = this.getUrl("Items/Counts", options); - return this.getJSON(url) - }, ApiClient.prototype.clearUserItemRating = function(userId, itemId) { - if (!userId) throw new Error("null userId"); - if (!itemId) throw new Error("null itemId"); - var url = this.getUrl("Users/" + userId + "/Items/" + itemId + "/Rating"); - return this.ajax({ - type: "DELETE", - url: url, - dataType: "json" - }) - }, ApiClient.prototype.reportPlaybackStart = function(options) { - if (!options) throw new Error("null options"); - this.lastPlaybackProgressReport = 0, this.lastPlaybackProgressReportTicks = null, stopBitrateDetection(this); - var url = this.getUrl("Sessions/Playing"); - return this.ajax({ - type: "POST", - data: JSON.stringify(options), - contentType: "application/json", - url: url - }) - }, ApiClient.prototype.reportPlaybackProgress = function(options) { - if (!options) throw new Error("null options"); - var newPositionTicks = options.PositionTicks; - if ("timeupdate" === (options.EventName || "timeupdate")) { - var now = (new Date).getTime(), - msSinceLastReport = now - (this.lastPlaybackProgressReport || 0); - if (msSinceLastReport <= 1e4) { - if (!newPositionTicks) return Promise.resolve(); - var expectedReportTicks = 1e4 * msSinceLastReport + (this.lastPlaybackProgressReportTicks || 0); - if (Math.abs((newPositionTicks || 0) - expectedReportTicks) < 5e7) return Promise.resolve() - } - this.lastPlaybackProgressReport = now - } else this.lastPlaybackProgressReport = 0; - this.lastPlaybackProgressReportTicks = newPositionTicks; - var url = this.getUrl("Sessions/Playing/Progress"); - return this.ajax({ - type: "POST", - data: JSON.stringify(options), - contentType: "application/json", - url: url - }) - }, ApiClient.prototype.reportOfflineActions = function(actions) { - if (!actions) throw new Error("null actions"); - var url = this.getUrl("Sync/OfflineActions"); - return this.ajax({ - type: "POST", - data: JSON.stringify(actions), - contentType: "application/json", - url: url - }) - }, ApiClient.prototype.syncData = function(data) { - if (!data) throw new Error("null data"); - var url = this.getUrl("Sync/Data"); - return this.ajax({ - type: "POST", - data: JSON.stringify(data), - contentType: "application/json", - url: url, - dataType: "json" - }) - }, ApiClient.prototype.getReadySyncItems = function(deviceId) { - if (!deviceId) throw new Error("null deviceId"); - var url = this.getUrl("Sync/Items/Ready", { - TargetId: deviceId - }); - return this.getJSON(url) - }, ApiClient.prototype.reportSyncJobItemTransferred = function(syncJobItemId) { - if (!syncJobItemId) throw new Error("null syncJobItemId"); - var url = this.getUrl("Sync/JobItems/" + syncJobItemId + "/Transferred"); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.cancelSyncItems = function(itemIds, targetId) { - if (!itemIds) throw new Error("null itemIds"); - var url = this.getUrl("Sync/" + (targetId || this.deviceId()) + "/Items", { - ItemIds: itemIds.join(",") - }); - return this.ajax({ - type: "DELETE", - url: url - }) - }, ApiClient.prototype.reportPlaybackStopped = function(options) { - if (!options) throw new Error("null options"); - this.lastPlaybackProgressReport = 0, this.lastPlaybackProgressReportTicks = null, redetectBitrate(this); - var url = this.getUrl("Sessions/Playing/Stopped"); - return this.ajax({ - type: "POST", - data: JSON.stringify(options), - contentType: "application/json", - url: url - }) - }, ApiClient.prototype.sendPlayCommand = function(sessionId, options) { - if (!sessionId) throw new Error("null sessionId"); - if (!options) throw new Error("null options"); - var url = this.getUrl("Sessions/" + sessionId + "/Playing", options); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.sendCommand = function(sessionId, command) { - if (!sessionId) throw new Error("null sessionId"); - if (!command) throw new Error("null command"); - var url = this.getUrl("Sessions/" + sessionId + "/Command"), - ajaxOptions = { - type: "POST", - url: url - }; - return ajaxOptions.data = JSON.stringify(command), ajaxOptions.contentType = "application/json", this.ajax(ajaxOptions) - }, ApiClient.prototype.sendMessageCommand = function(sessionId, options) { - if (!sessionId) throw new Error("null sessionId"); - if (!options) throw new Error("null options"); - var url = this.getUrl("Sessions/" + sessionId + "/Message"), - ajaxOptions = { - type: "POST", - url: url - }; - return ajaxOptions.data = JSON.stringify(options), ajaxOptions.contentType = "application/json", this.ajax(ajaxOptions) - }, ApiClient.prototype.sendPlayStateCommand = function(sessionId, command, options) { - if (!sessionId) throw new Error("null sessionId"); - if (!command) throw new Error("null command"); - var url = this.getUrl("Sessions/" + sessionId + "/Playing/" + command, options || {}); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.createPackageReview = function(review) { - var url = this.getUrl("Packages/Reviews/" + review.id, review); - return this.ajax({ - type: "POST", - url: url - }) - }, ApiClient.prototype.getPackageReviews = function(packageId, minRating, maxRating, limit) { - if (!packageId) throw new Error("null packageId"); - var options = {}; - minRating && (options.MinRating = minRating), maxRating && (options.MaxRating = maxRating), limit && (options.Limit = limit); - var url = this.getUrl("Packages/" + packageId + "/Reviews", options); - return this.getJSON(url) - }, ApiClient.prototype.getSavedEndpointInfo = function() { - return this._endPointInfo - }, ApiClient.prototype.getEndpointInfo = function() { - var savedValue = this._endPointInfo; - if (savedValue) return Promise.resolve(savedValue); - var instance = this; - return this.getJSON(this.getUrl("System/Endpoint")).then(function(endPointInfo) { - return setSavedEndpointInfo(instance, endPointInfo), endPointInfo - }) - }, ApiClient.prototype.getLatestItems = function(options) { - return options = options || {}, this.getJSON(this.getUrl("Users/" + this.getCurrentUserId() + "/Items/Latest", options)) - }, ApiClient.prototype.getFilters = function(options) { - return this.getJSON(this.getUrl("Items/Filters2", options)) - }, ApiClient.prototype.setSystemInfo = function(info) { - this._serverVersion = info.Version - }, ApiClient.prototype.serverVersion = function() { - return this._serverVersion - }, ApiClient.prototype.isMinServerVersion = function(version) { - var serverVersion = this.serverVersion(); - return !!serverVersion && compareVersions(serverVersion, version) >= 0 - }, ApiClient.prototype.handleMessageReceived = function(msg) { - onMessageReceivedInternal(this, msg) - }, ApiClient -}); diff --git a/src/libraries/apiclient/appStorage.js b/src/libraries/apiclient/appStorage.js deleted file mode 100644 index dacf38822f..0000000000 --- a/src/libraries/apiclient/appStorage.js +++ /dev/null @@ -1,53 +0,0 @@ -define([], function() { - "use strict"; - - function onCachePutFail(e) { - console.error("cannot put to a cache: " + e); - } - - function updateCache(instance) { - if (instance.cache) { - instance.cache.put("data", new Response(JSON.stringify(instance.localData))).catch(onCachePutFail); - } - } - - function onCacheOpened(result) { - this.cache = result; - this.localData = {}; - } - - function MyStore() { - - this.setItem = function(name, value) { - localStorage.setItem(name, value); - - if (this.localData && this.localData[name] !== value) { - this.localData[name] = value; - updateCache(this); - } - }; - - this.getItem = function(name) { - return localStorage.getItem(name); - }; - - this.removeItem = function(name) { - localStorage.removeItem(name); - - if (this.localData) { - delete this.localData[name]; - updateCache(this); - } - }; - - try { - if (self.caches) { - self.caches.open("embydata").then(onCacheOpened.bind(this)); - } - } catch (err) { - console.error("error opening cache: " + err); - } - } - - return new MyStore; -}); diff --git a/src/libraries/apiclient/connectionmanager.js b/src/libraries/apiclient/connectionmanager.js deleted file mode 100644 index dc5ef406e5..0000000000 --- a/src/libraries/apiclient/connectionmanager.js +++ /dev/null @@ -1,807 +0,0 @@ -define(["events", "apiclient", "appStorage"], function (events, apiClientFactory, appStorage) { - "use strict"; - - function getServerAddress(server, mode) { - switch (mode) { - case ConnectionMode.Local: - return server.LocalAddress; - - case ConnectionMode.Manual: - return server.ManualAddress; - - case ConnectionMode.Remote: - return server.RemoteAddress; - - default: - return server.ManualAddress || server.LocalAddress || server.RemoteAddress; - } - } - - function paramsToString(params) { - var values = []; - - for (var key in params) { - var value = params[key]; - - if (null !== value && void 0 !== value && "" !== value) { - values.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)); - } - } - - return values.join("&"); - } - - function resolveFailure(instance, resolve) { - resolve({ - State: "Unavailable", - }); - } - - function mergeServers(credentialProvider, list1, list2) { - for (var i = 0, length = list2.length; i < length; i++) { - credentialProvider.addOrUpdateServer(list1, list2[i]); - } - - return list1; - } - - function updateServerInfo(server, systemInfo) { - server.Name = systemInfo.ServerName; - if (systemInfo.Id) { - server.Id = systemInfo.Id; - } - - if (systemInfo.LocalAddress) { - server.LocalAddress = systemInfo.LocalAddress; - } - } - - function getEmbyServerUrl(baseUrl, handler) { - return baseUrl + "/" + handler; - } - - function getFetchPromise(request) { - var headers = request.headers || {}; - - if ("json" === request.dataType) { - headers.accept = "application/json"; - } - - var fetchRequest = { - headers: headers, - method: request.type, - credentials: "same-origin" - }; - var contentType = request.contentType; - - if (request.data) { - if ("string" == typeof request.data) { - fetchRequest.body = request.data; - } else { - fetchRequest.body = paramsToString(request.data); - contentType = contentType || "application/x-www-form-urlencoded; charset=UTF-8"; - } - } - - if (contentType) { - headers["Content-Type"] = contentType; - } - - if (request.timeout) { - return fetchWithTimeout(request.url, fetchRequest, request.timeout); - } - - return fetch(request.url, fetchRequest); - } - - function fetchWithTimeout(url, options, timeoutMs) { - console.debug("fetchWithTimeout: timeoutMs: " + timeoutMs + ", url: " + url); - return new Promise(function (resolve, reject) { - var timeout = setTimeout(reject, timeoutMs); - options = options || {}; - options.credentials = "same-origin"; - fetch(url, options).then(function (response) { - clearTimeout(timeout); - console.debug("fetchWithTimeout: succeeded connecting to url: " + url); - resolve(response); - }, function (error) { - clearTimeout(timeout); - console.error("fetchWithTimeout: timed out connecting to url: " + url); - reject(); - }); - }); - } - - function ajax(request) { - if (!request) { - throw new Error("Request cannot be null"); - } - - request.headers = request.headers || {}; - console.debug("ConnectionManager requesting url: " + request.url); - return getFetchPromise(request).then(function (response) { - console.debug("ConnectionManager response status: " + response.status + ", url: " + request.url); - - if (response.status < 400) { - if ("json" === request.dataType || "application/json" === request.headers.accept) { - return response.json(); - } - - return response; - } - - return Promise.reject(response); - }, function (err) { - console.error("ConnectionManager request failed to url: " + request.url); - throw err; - }); - } - - function replaceAll(originalString, strReplace, strWith) { - var reg = new RegExp(strReplace, "ig"); - return originalString.replace(reg, strWith); - } - - function normalizeAddress(address) { - address = address.trim(); - - if (0 !== address.toLowerCase().indexOf("http")) { - address = "http://" + address; - } - - address = replaceAll(address, "Http:", "http:"); - address = replaceAll(address, "Https:", "https:"); - - return address; - } - - function stringEqualsIgnoreCase(str1, str2) { - return (str1 || "").toLowerCase() === (str2 || "").toLowerCase(); - } - - function compareVersions(a, b) { - a = a.split("."); - b = b.split("."); - - for (var i = 0, length = Math.max(a.length, b.length); i < length; i++) { - var aVal = parseInt(a[i] || "0"); - var bVal = parseInt(b[i] || "0"); - - if (aVal < bVal) { - return -1; - } - - if (aVal > bVal) { - return 1; - } - } - - return 0; - } - - var defaultTimeout = 20000; - var ConnectionMode = { - Local: 0, - Remote: 1, - Manual: 2 - }; - - var ConnectionManager = function (credentialProvider, appName, appVersion, deviceName, deviceId, capabilities) { - - function onAuthenticated(apiClient, result, options, saveCredentials) { - var credentials = credentialProvider.credentials(); - var servers = credentials.Servers.filter(function (s) { - return s.Id === result.ServerId; - }); - var server = servers.length ? servers[0] : apiClient.serverInfo(); - - if (false !== options.updateDateLastAccessed) { - server.DateLastAccessed = new Date().getTime(); - } - - server.Id = result.ServerId; - - if (saveCredentials) { - server.UserId = result.User.Id; - server.AccessToken = result.AccessToken; - } else { - server.UserId = null; - server.AccessToken = null; - } - - credentialProvider.addOrUpdateServer(credentials.Servers, server); - credentialProvider.credentials(credentials); - apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection; - apiClient.serverInfo(server); - afterConnected(apiClient, options); - return onLocalUserSignIn(server, apiClient.serverAddress(), result.User); - } - - function afterConnected(apiClient, options) { - options = options || {}; - - if (false !== options.reportCapabilities) { - apiClient.reportCapabilities(capabilities); - } - - apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection; - - if (false !== options.enableWebSocket) { - console.debug("calling apiClient.ensureWebSocket"); - apiClient.ensureWebSocket(); - } - } - - function onLocalUserSignIn(server, serverUrl, user) { - self._getOrAddApiClient(server, serverUrl); - - var promise = self.onLocalUserSignedIn ? self.onLocalUserSignedIn.call(self, user) : Promise.resolve(); - return promise.then(function () { - events.trigger(self, "localusersignedin", [user]) - }) - } - - function validateAuthentication(server, serverUrl) { - return ajax({ - type: "GET", - url: getEmbyServerUrl(serverUrl, "System/Info"), - dataType: "json", - headers: { - "X-MediaBrowser-Token": server.AccessToken - } - }).then(function (systemInfo) { - updateServerInfo(server, systemInfo); - return Promise.resolve(); - }, function () { - server.UserId = null; - server.AccessToken = null; - return Promise.resolve(); - }); - } - - function getImageUrl(localUser) { - if (localUser && localUser.PrimaryImageTag) { - return { - url: self.getApiClient(localUser).getUserImageUrl(localUser.Id, { - tag: localUser.PrimaryImageTag, - type: "Primary" - }), - supportsParams: true - }; - } - - return { - url: null, - supportsParams: false - }; - } - - function logoutOfServer(apiClient) { - var serverInfo = apiClient.serverInfo() || {}; - var logoutInfo = { - serverId: serverInfo.Id - }; - return apiClient.logout().then(function () { - events.trigger(self, "localusersignedout", [logoutInfo]); - }, function () { - events.trigger(self, "localusersignedout", [logoutInfo]); - }); - } - - function findServers() { - return new Promise(function (resolve, reject) { - var onFinish = function (foundServers) { - var servers = foundServers.map(function (foundServer) { - var info = { - Id: foundServer.Id, - LocalAddress: convertEndpointAddressToManualAddress(foundServer) || foundServer.Address, - Name: foundServer.Name - }; - info.LastConnectionMode = info.ManualAddress ? ConnectionMode.Manual : ConnectionMode.Local; - return info; - }); - resolve(servers); - }; - - if (window.NativeShell && typeof window.NativeShell.findServers === 'function') { - window.NativeShell.findServers(1e3).then(onFinish, function () { - onFinish([]); - }); - } else { - resolve([]); - } - }); - } - - function convertEndpointAddressToManualAddress(info) { - if (info.Address && info.EndpointAddress) { - var address = info.EndpointAddress.split(":")[0]; - var parts = info.Address.split(":"); - - if (parts.length > 1) { - var portString = parts[parts.length - 1]; - - if (!isNaN(parseInt(portString))) { - address += ":" + portString; - } - } - - return normalizeAddress(address); - } - - return null; - } - - function getTryConnectPromise(url, connectionMode, state, resolve, reject) { - console.debug("getTryConnectPromise " + url); - ajax({ - url: getEmbyServerUrl(url, "system/info/public"), - timeout: defaultTimeout, - type: "GET", - dataType: "json" - }).then(function (result) { - if (!state.resolved) { - state.resolved = true; - console.debug("Reconnect succeeded to " + url); - resolve({ - url: url, - connectionMode: connectionMode, - data: result - }); - } - }, function () { - if (!state.resolved) { - console.error("Reconnect failed to " + url); - - if (++state.rejects >= state.numAddresses) { - reject(); - } - } - }); - } - - function tryReconnect(serverInfo) { - var addresses = []; - var addressesStrings = []; - - if (!serverInfo.manualAddressOnly && serverInfo.LocalAddress && -1 === addressesStrings.indexOf(serverInfo.LocalAddress)) { - addresses.push({ - url: serverInfo.LocalAddress, - mode: ConnectionMode.Local, - timeout: 0 - }); - addressesStrings.push(addresses[addresses.length - 1].url); - } - - if (serverInfo.ManualAddress && -1 === addressesStrings.indexOf(serverInfo.ManualAddress)) { - addresses.push({ - url: serverInfo.ManualAddress, - mode: ConnectionMode.Manual, - timeout: 100 - }); - addressesStrings.push(addresses[addresses.length - 1].url); - } - - if (!serverInfo.manualAddressOnly && serverInfo.RemoteAddress && -1 === addressesStrings.indexOf(serverInfo.RemoteAddress)) { - addresses.push({ - url: serverInfo.RemoteAddress, - mode: ConnectionMode.Remote, - timeout: 200 - }); - addressesStrings.push(addresses[addresses.length - 1].url); - } - - console.debug("tryReconnect: " + addressesStrings.join("|")); - return new Promise(function (resolve, reject) { - var state = {}; - state.numAddresses = addresses.length; - state.rejects = 0; - addresses.map(function (url) { - setTimeout(function () { - if (!state.resolved) { - getTryConnectPromise(url.url, url.mode, state, resolve, reject); - } - }, url.timeout); - }); - }); - } - - function onSuccessfulConnection(server, systemInfo, connectionMode, serverUrl, options, resolve) { - var credentials = credentialProvider.credentials(); - options = options || {}; - - afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, true, options, resolve); - } - - function afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, verifyLocalAuthentication, options, resolve) { - options = options || {}; - if (false === options.enableAutoLogin) { - server.UserId = null; - server.AccessToken = null; - } else if (verifyLocalAuthentication && server.AccessToken && false !== options.enableAutoLogin) { - return void validateAuthentication(server, serverUrl).then(function () { - afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, false, options, resolve); - }); - } - - updateServerInfo(server, systemInfo); - server.LastConnectionMode = connectionMode; - - if (false !== options.updateDateLastAccessed) { - server.DateLastAccessed = new Date().getTime(); - } - - credentialProvider.addOrUpdateServer(credentials.Servers, server); - credentialProvider.credentials(credentials); - var result = { - Servers: [] - }; - result.ApiClient = self._getOrAddApiClient(server, serverUrl); - result.ApiClient.setSystemInfo(systemInfo); - result.State = server.AccessToken && false !== options.enableAutoLogin ? "SignedIn" : "ServerSignIn"; - result.Servers.push(server); - result.ApiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection; - result.ApiClient.updateServerInfo(server, serverUrl); - - var resolveActions = function () { - resolve(result); - events.trigger(self, "connected", [result]); - }; - - if ("SignedIn" === result.State) { - afterConnected(result.ApiClient, options); - result.ApiClient.getCurrentUser().then(function (user) { - onLocalUserSignIn(server, serverUrl, user).then(resolveActions, resolveActions); - }, resolveActions); - } else { - resolveActions(); - } - } - - console.debug("Begin ConnectionManager constructor"); - var self = this; - this._apiClients = []; - self._minServerVersion = "3.2.33"; - - self.appVersion = function () { - return appVersion; - }; - - self.appName = function () { - return appName; - }; - - self.capabilities = function () { - return capabilities; - }; - - self.deviceId = function () { - return deviceId; - }; - - self.credentialProvider = function () { - return credentialProvider; - }; - - self.getServerInfo = function (id) { - return credentialProvider.credentials().Servers.filter(function (s) { - return s.Id === id; - })[0]; - }; - - self.getLastUsedServer = function () { - var servers = credentialProvider.credentials().Servers; - servers.sort(function (a, b) { - return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0); - }); - - if (servers.length) { - return servers[0]; - } - - return null; - }; - - self.addApiClient = function (apiClient) { - self._apiClients.push(apiClient); - - var existingServers = credentialProvider.credentials().Servers.filter(function (s) { - return stringEqualsIgnoreCase(s.ManualAddress, apiClient.serverAddress()) || stringEqualsIgnoreCase(s.LocalAddress, apiClient.serverAddress()) || stringEqualsIgnoreCase(s.RemoteAddress, apiClient.serverAddress()); - }); - var existingServer = existingServers.length ? existingServers[0] : apiClient.serverInfo(); - - existingServer.DateLastAccessed = new Date().getTime(); - existingServer.LastConnectionMode = ConnectionMode.Manual; - existingServer.ManualAddress = apiClient.serverAddress(); - if (apiClient.manualAddressOnly) { - existingServer.manualAddressOnly = true; - } - apiClient.serverInfo(existingServer); - apiClient.onAuthenticated = function (instance, result) { - return onAuthenticated(instance, result, {}, true); - }; - if (!existingServers.length) { - var credentials = credentialProvider.credentials(); - credentials.Servers = [existingServer]; - credentialProvider.credentials(credentials); - } - - events.trigger(self, "apiclientcreated", [apiClient]); - }; - - self.clearData = function () { - console.debug("connection manager clearing data"); - var credentials = credentialProvider.credentials(); - credentials.Servers = []; - credentialProvider.credentials(credentials); - }; - - self._getOrAddApiClient = function (server, serverUrl) { - var apiClient = self.getApiClient(server.Id); - - if (!apiClient) { - apiClient = new apiClientFactory(serverUrl, appName, appVersion, deviceName, deviceId); - self._apiClients.push(apiClient); - apiClient.serverInfo(server); - apiClient.onAuthenticated = function (instance, result) { - return onAuthenticated(instance, result, {}, true); - }; - - events.trigger(self, "apiclientcreated", [apiClient]); - } - - console.debug("returning instance from getOrAddApiClient"); - return apiClient; - }; - - self.getOrCreateApiClient = function (serverId) { - var credentials = credentialProvider.credentials(); - var servers = credentials.Servers.filter(function (s) { - return stringEqualsIgnoreCase(s.Id, serverId); - }); - - if (!servers.length) { - throw new Error("Server not found: " + serverId); - } - - var server = servers[0]; - return self._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode)); - }; - - self.user = function (apiClient) { - return new Promise(function (resolve, reject) { - function onLocalUserDone(e) { - if (apiClient && apiClient.getCurrentUserId()) { - apiClient.getCurrentUser().then(function (u) { - localUser = u; - var image = getImageUrl(localUser); - resolve({ - localUser: localUser, - name: localUser ? localUser.Name : null, - imageUrl: image.url, - supportsImageParams: image.supportsParams, - }); - }, onLocalUserDone); - } - } - var localUser; - if (apiClient && apiClient.getCurrentUserId()) { - onLocalUserDone(); - } - }); - }; - - self.logout = function () { - console.debug("begin connectionManager loguot"); - var promises = []; - - for (var i = 0, length = self._apiClients.length; i < length; i++) { - var apiClient = self._apiClients[i]; - - if (apiClient.accessToken()) { - promises.push(logoutOfServer(apiClient)); - } - } - - return Promise.all(promises).then(function () { - var credentials = credentialProvider.credentials(); - var servers = credentials.Servers.filter(function (u) { - return "Guest" !== u.UserLinkType; - }); - - for (var j = 0, numServers = servers.length; j < numServers; j++) { - var server = servers[j]; - server.UserId = null; - server.AccessToken = null; - server.ExchangeToken = null; - } - }); - }; - - self.getSavedServers = function () { - var credentials = credentialProvider.credentials(); - var servers = credentials.Servers.slice(0); - servers.sort(function (a, b) { - return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0); - }); - return servers; - }; - - self.getAvailableServers = function () { - console.debug("begin getAvailableServers"); - var credentials = credentialProvider.credentials(); - return Promise.all([findServers()]).then(function (responses) { - var foundServers = responses[0]; - var servers = credentials.Servers.slice(0); - mergeServers(credentialProvider, servers, foundServers); - servers.sort(function (a, b) { - return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0); - }); - credentials.Servers = servers; - credentialProvider.credentials(credentials); - return servers; - }); - }; - - self.connectToServers = function (servers, options) { - console.debug("begin connectToServers, with " + servers.length + " servers"); - var firstServer = servers.length ? servers[0] : null; - - if (firstServer) { - return self.connectToServer(firstServer, options).then(function (result) { - if ("Unavailable" === result.State) { - result.State = "ServerSelection"; - } - - console.debug("resolving connectToServers with result.State: " + result.State); - return result; - }); - } - - return Promise.resolve({ - Servers: servers, - State: "ServerSelection" - }); - }; - - self.connectToServer = function (server, options) { - console.debug("begin connectToServer"); - return new Promise(function (resolve, reject) { - options = options || {}; - tryReconnect(server).then(function (result) { - var serverUrl = result.url; - var connectionMode = result.connectionMode; - result = result.data; - - if (1 === compareVersions(self.minServerVersion(), result.Version)) { - console.debug("minServerVersion requirement not met. Server version: " + result.Version); - resolve({ - State: "ServerUpdateNeeded", - Servers: [server] - }); - } else { - if (server.Id && result.Id !== server.Id) { - console.debug("http request succeeded, but found a different server Id than what was expected"); - resolveFailure(self, resolve); - } else { - onSuccessfulConnection(server, result, connectionMode, serverUrl, options, resolve); - } - } - }, function () { - resolveFailure(self, resolve); - }); - }); - }; - - self.connectToAddress = function (address, options) { - function onFail() { - console.error("connectToAddress " + address + " failed"); - return Promise.resolve({ - State: "Unavailable", - }); - } - - if (!address) { - return Promise.reject(); - } - - address = normalizeAddress(address); - var instance = this; - var server = { - ManualAddress: address, - LastConnectionMode: ConnectionMode.Manual - }; - return self.connectToServer(server, options).catch(onFail); - }; - - self.deleteServer = function (serverId) { - if (!serverId) { - throw new Error("null serverId"); - } - - var server = credentialProvider.credentials().Servers.filter(function (s) { - return s.Id === serverId; - }); - server = server.length ? server[0] : null; - return new Promise(function (resolve, reject) { - function onDone() { - var credentials = credentialProvider.credentials(); - credentials.Servers = credentials.Servers.filter(function (s) { - return s.Id !== serverId; - }); - credentialProvider.credentials(credentials); - resolve(); - } - - if (!server.ConnectServerId) { - return void onDone(); - } - }); - }; - }; - - ConnectionManager.prototype.connect = function (options) { - console.debug("begin connect"); - var instance = this; - return instance.getAvailableServers().then(function (servers) { - return instance.connectToServers(servers, options); - }); - }; - - ConnectionManager.prototype.getApiClients = function () { - var servers = this.getSavedServers(); - - for (var i = 0, length = servers.length; i < length; i++) { - var server = servers[i]; - - if (server.Id) { - this._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode)); - } - } - - return this._apiClients; - }; - - ConnectionManager.prototype.getApiClient = function (item) { - if (!item) { - throw new Error("item or serverId cannot be null"); - } - - if (item.ServerId) { - item = item.ServerId; - } - - return this._apiClients.filter(function (a) { - var serverInfo = a.serverInfo(); - return !serverInfo || serverInfo.Id === item; - })[0]; - }; - - ConnectionManager.prototype.minServerVersion = function (val) { - if (val) { - this._minServerVersion = val; - } - - return this._minServerVersion; - }; - - ConnectionManager.prototype.handleMessageReceived = function (msg) { - var serverId = msg.ServerId; - - if (serverId) { - var apiClient = this.getApiClient(serverId); - - if (apiClient) { - if ("string" == typeof msg.Data) { - try { - msg.Data = JSON.parse(msg.Data); - } catch (err) {} - } - - apiClient.handleMessageReceived(msg); - } - } - }; - - return ConnectionManager; -}); diff --git a/src/libraries/apiclient/credentialprovider.js b/src/libraries/apiclient/credentialprovider.js deleted file mode 100644 index 4ae809cace..0000000000 --- a/src/libraries/apiclient/credentialprovider.js +++ /dev/null @@ -1,29 +0,0 @@ -define(["events", "appStorage"], function(events, appStorage) { - "use strict"; - - function ensure(instance, data) { - if (!instance._credentials) { - var json = appStorage.getItem(instance.key) || "{}"; - console.debug("credentials initialized with: " + json), instance._credentials = JSON.parse(json), instance._credentials.Servers = instance._credentials.Servers || [] - } - } - - function set(instance, data) { - data ? (instance._credentials = data, appStorage.setItem(instance.key, JSON.stringify(data))) : instance.clear(), events.trigger(instance, "credentialsupdated") - } - - function Credentials(key) { - this.key = key || "jellyfin_credentials" - } - return Credentials.prototype.clear = function() { - this._credentials = null, appStorage.removeItem(this.key) - }, Credentials.prototype.credentials = function(data) { - return data && set(this, data), ensure(this), this._credentials - }, Credentials.prototype.addOrUpdateServer = function(list, server) { - if (!server.Id) throw new Error("Server.Id cannot be null or empty"); - var existing = list.filter(function(s) { - return s.Id === server.Id - })[0]; - return existing ? (existing.DateLastAccessed = Math.max(existing.DateLastAccessed || 0, server.DateLastAccessed || 0), existing.UserLinkType = server.UserLinkType, server.AccessToken && (existing.AccessToken = server.AccessToken, existing.UserId = server.UserId), server.ExchangeToken && (existing.ExchangeToken = server.ExchangeToken), server.RemoteAddress && (existing.RemoteAddress = server.RemoteAddress), server.ManualAddress && (existing.ManualAddress = server.ManualAddress), server.LocalAddress && (existing.LocalAddress = server.LocalAddress), server.Name && (existing.Name = server.Name), null != server.LastConnectionMode && (existing.LastConnectionMode = server.LastConnectionMode), server.ConnectServerId && (existing.ConnectServerId = server.ConnectServerId), existing) : (list.push(server), server) - }, Credentials -}); diff --git a/src/libraries/apiclient/events.js b/src/libraries/apiclient/events.js deleted file mode 100644 index c109ee9c48..0000000000 --- a/src/libraries/apiclient/events.js +++ /dev/null @@ -1,30 +0,0 @@ -define([], function() { - "use strict"; - - function getCallbacks(obj, name) { - if (!obj) throw new Error("obj cannot be null!"); - obj._callbacks = obj._callbacks || {}; - var list = obj._callbacks[name]; - return list || (obj._callbacks[name] = [], list = obj._callbacks[name]), list - } - return { - on: function(obj, eventName, fn) { - getCallbacks(obj, eventName).push(fn) - }, - off: function(obj, eventName, fn) { - var list = getCallbacks(obj, eventName), - i = list.indexOf(fn); - 1 !== i && list.splice(i, 1) - }, - trigger: function(obj, eventName) { - var eventObject = { - type: eventName - }, - eventArgs = []; - eventArgs.push(eventObject); - for (var additionalArgs = arguments[2] || [], i = 0, length = additionalArgs.length; i < length; i++) eventArgs.push(additionalArgs[i]); - getCallbacks(obj, eventName).slice(0).forEach(function(c) { - c.apply(obj, eventArgs) - }) - } - } -}); diff --git a/src/libraries/apiclient/localassetmanager.js b/src/libraries/apiclient/localassetmanager.js deleted file mode 100644 index 6fb1d4722d..0000000000 --- a/src/libraries/apiclient/localassetmanager.js +++ /dev/null @@ -1,406 +0,0 @@ -define(["filerepository", "itemrepository", "useractionrepository", "transfermanager"], function(filerepository, itemrepository, useractionrepository, transfermanager) { - "use strict"; - - function getLocalItem(serverId, itemId) { - return console.debug("localassetmanager: begin getLocalItem"), itemrepository.get(serverId, itemId) - } - - function recordUserAction(action) { - return action.Id = createGuid(), useractionrepository.set(action.Id, action) - } - - function getUserActions(serverId) { - return useractionrepository.getByServerId(serverId) - } - - function deleteUserAction(action) { - return useractionrepository.remove(action.Id) - } - - function deleteUserActions(actions) { - var results = []; - return actions.forEach(function(action) { - results.push(deleteUserAction(action)) - }), Promise.all(results) - } - - function getServerItems(serverId) { - return console.debug("localassetmanager: begin getServerItems"), itemrepository.getAll(serverId) - } - - function getItemsFromIds(serverId, ids) { - var actions = ids.map(function(id) { - var strippedId = stripStart(id, "local:"); - return getLocalItem(serverId, strippedId) - }); - return Promise.all(actions).then(function(items) { - var libItems = items.map(function(locItem) { - return locItem.Item - }); - return Promise.resolve(libItems) - }) - } - - function getViews(serverId, userId) { - return itemrepository.getServerItemTypes(serverId, userId).then(function(types) { - var item, list = []; - return types.indexOf("Audio") > -1 && (item = { - Name: "Music", - ServerId: serverId, - Id: "localview:MusicView", - Type: "MusicView", - CollectionType: "music", - IsFolder: !0 - }, list.push(item)), types.indexOf("Photo") > -1 && (item = { - Name: "Photos", - ServerId: serverId, - Id: "localview:PhotosView", - Type: "PhotosView", - CollectionType: "photos", - IsFolder: !0 - }, list.push(item)), types.indexOf("Episode") > -1 && (item = { - Name: "TV", - ServerId: serverId, - Id: "localview:TVView", - Type: "TVView", - CollectionType: "tvshows", - IsFolder: !0 - }, list.push(item)), types.indexOf("Movie") > -1 && (item = { - Name: "Movies", - ServerId: serverId, - Id: "localview:MoviesView", - Type: "MoviesView", - CollectionType: "movies", - IsFolder: !0 - }, list.push(item)), types.indexOf("Video") > -1 && (item = { - Name: "Videos", - ServerId: serverId, - Id: "localview:VideosView", - Type: "VideosView", - CollectionType: "videos", - IsFolder: !0 - }, list.push(item)), types.indexOf("MusicVideo") > -1 && (item = { - Name: "Music Videos", - ServerId: serverId, - Id: "localview:MusicVideosView", - Type: "MusicVideosView", - CollectionType: "videos", - IsFolder: !0 - }, list.push(item)), Promise.resolve(list) - }) - } - - function updateFiltersForTopLevelView(parentId, mediaTypes, includeItemTypes, query) { - switch (parentId) { - case "MusicView": - return query.Recursive ? includeItemTypes.push("Audio") : includeItemTypes.push("MusicAlbum"), !0; - case "PhotosView": - return query.Recursive ? includeItemTypes.push("Photo") : includeItemTypes.push("PhotoAlbum"), !0; - case "TVView": - return query.Recursive ? includeItemTypes.push("Episode") : includeItemTypes.push("Series"), !0; - case "VideosView": - return query.Recursive, includeItemTypes.push("Video"), !0; - case "MoviesView": - return query.Recursive, includeItemTypes.push("Movie"), !0; - case "MusicVideosView": - return query.Recursive, includeItemTypes.push("MusicVideo"), !0 - } - return !1 - } - - function normalizeId(id) { - return id ? (id = stripStart(id, "localview:"), id = stripStart(id, "local:")) : null - } - - function normalizeIdList(val) { - return val ? val.split(",").map(normalizeId) : [] - } - - function shuffle(array) { - for (var temporaryValue, randomIndex, currentIndex = array.length; 0 !== currentIndex;) randomIndex = Math.floor(Math.random() * currentIndex), currentIndex -= 1, temporaryValue = array[currentIndex], array[currentIndex] = array[randomIndex], array[randomIndex] = temporaryValue; - return array - } - - function sortItems(items, query) { - if (!query.SortBy || 0 === query.SortBy.length) return items; - if ("Random" === query.SortBy) return shuffle(items); - var sortSpec = getSortSpec(query); - return items.sort(function(a, b) { - for (var i = 0; i < sortSpec.length; i++) { - var result = compareValues(a, b, sortSpec[i].Field, sortSpec[i].OrderDescending); - if (0 !== result) return result - } - return 0 - }), items - } - - function compareValues(a, b, field, orderDesc) { - if (!a.hasOwnProperty(field) || !b.hasOwnProperty(field)) return 0; - var valA = a[field], - valB = b[field], - result = 0; - return "string" == typeof valA || "string" == typeof valB ? (valA = valA || "", valB = valB || "", result = valA.toLowerCase().localeCompare(valB.toLowerCase())) : valA > valB ? result = 1 : valA < valB && (result = -1), orderDesc && (result *= -1), result - } - - function getSortSpec(query) { - for (var sortFields = (query.SortBy || "").split(","), sortOrders = (query.SortOrder || "").split(","), sortSpec = [], i = 0; i < sortFields.length; i++) { - var orderDesc = !1; - i < sortOrders.length && -1 !== sortOrders[i].toLowerCase().indexOf("desc") && (orderDesc = !0), sortSpec.push({ - Field: sortFields[i], - OrderDescending: orderDesc - }) - } - return sortSpec - } - - function getViewItems(serverId, userId, options) { - var searchParentId = options.ParentId; - searchParentId = normalizeId(searchParentId); - var seasonId = normalizeId(options.SeasonId || options.seasonId), - seriesId = normalizeId(options.SeriesId || options.seriesId), - albumIds = normalizeIdList(options.AlbumIds || options.albumIds), - includeItemTypes = options.IncludeItemTypes ? options.IncludeItemTypes.split(",") : [], - filters = options.Filters ? options.Filters.split(",") : [], - mediaTypes = options.MediaTypes ? options.MediaTypes.split(",") : []; - return updateFiltersForTopLevelView(searchParentId, mediaTypes, includeItemTypes, options) && (searchParentId = null), getServerItems(serverId).then(function(items) { - var itemsMap = new Map, - subtreeIdSet = new Set; - if (items.forEach(function(item) { - item.Item.LocalChildren = [], itemsMap.set(item.Item.Id, item.Item) - }), itemsMap.forEach(function(item, ignored, ignored2) { - if (item.ParentId && itemsMap.has(item.ParentId)) { - itemsMap.get(item.ParentId).LocalChildren.push(item) - } - }), options.Recursive && searchParentId && itemsMap.has(searchParentId)) { - var addSubtreeIds = function(recurseItem) { - subtreeIdSet.has(recurseItem.Id) || subtreeIdSet.add(recurseItem.Id), recurseItem.LocalChildren.forEach(function(childItem) { - addSubtreeIds(childItem) - }) - }, - searchParentItem = itemsMap.get(searchParentId); - addSubtreeIds(searchParentItem) - } - var resultItems = items.filter(function(item) { - return (!item.SyncStatus || "synced" === item.SyncStatus) && ((!mediaTypes.length || -1 !== mediaTypes.indexOf(item.Item.MediaType || "")) && ((!seriesId || item.Item.SeriesId === seriesId) && ((!seasonId || item.Item.SeasonId === seasonId) && ((!albumIds.length || -1 !== albumIds.indexOf(item.Item.AlbumId || "")) && ((!item.Item.IsFolder || -1 === filters.indexOf("IsNotFolder")) && (!(!item.Item.IsFolder && -1 !== filters.indexOf("IsFolder")) && ((!includeItemTypes.length || -1 !== includeItemTypes.indexOf(item.Item.Type || "")) && (!searchParentId || (options.Recursive ? subtreeIdSet.has(item.Item.Id) : item.Item.ParentId === searchParentId))))))))) - }).map(function(item2) { - return item2.Item - }); - return resultItems = sortItems(resultItems, options), options.Limit && (resultItems = resultItems.slice(0, options.Limit)), Promise.resolve(resultItems) - }) - } - - function removeObsoleteContainerItems(serverId) { - return getServerItems(serverId).then(function(items) { - var seriesItems = items.filter(function(item) { - return "series" === (item.Item.Type || "").toLowerCase() - }), - seasonItems = items.filter(function(item) { - return "season" === (item.Item.Type || "").toLowerCase() - }), - albumItems = items.filter(function(item) { - var type = (item.Item.Type || "").toLowerCase(); - return "musicalbum" === type || "photoalbum" === type - }), - requiredSeriesIds = items.filter(function(item) { - return "episode" === (item.Item.Type || "").toLowerCase() - }).map(function(item2) { - return item2.Item.SeriesId - }).filter(filterDistinct), - requiredSeasonIds = items.filter(function(item) { - return "episode" === (item.Item.Type || "").toLowerCase() - }).map(function(item2) { - return item2.Item.SeasonId - }).filter(filterDistinct), - requiredAlbumIds = items.filter(function(item) { - var type = (item.Item.Type || "").toLowerCase(); - return "audio" === type || "photo" === type - }).map(function(item2) { - return item2.Item.AlbumId - }).filter(filterDistinct), - obsoleteItems = []; - seriesItems.forEach(function(item) { - requiredSeriesIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item) - }), seasonItems.forEach(function(item) { - requiredSeasonIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item) - }), albumItems.forEach(function(item) { - requiredAlbumIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item) - }); - var p = Promise.resolve(); - return obsoleteItems.forEach(function(item) { - p = p.then(function() { - return itemrepository.remove(item.ServerId, item.Id) - }) - }), p - }) - } - - function removeLocalItem(localItem) { - return itemrepository.get(localItem.ServerId, localItem.Id).then(function(item) { - var onFileDeletedSuccessOrFail = function() { - return itemrepository.remove(localItem.ServerId, localItem.Id) - }, - p = Promise.resolve(); - return item.LocalPath && (p = p.then(function() { - return filerepository.deleteFile(item.LocalPath) - })), item && item.Item && item.Item.MediaSources && item.Item.MediaSources.forEach(function(mediaSource) { - mediaSource.MediaStreams && mediaSource.MediaStreams.length > 0 && mediaSource.MediaStreams.forEach(function(mediaStream) { - mediaStream.Path && (p = p.then(function() { - return filerepository.deleteFile(mediaStream.Path) - })) - }) - }), p.then(onFileDeletedSuccessOrFail, onFileDeletedSuccessOrFail) - }, function(item) { - return Promise.resolve() - }) - } - - function addOrUpdateLocalItem(localItem) { - return itemrepository.set(localItem.ServerId, localItem.Id, localItem) - } - - function getSubtitleSaveFileName(localItem, mediaPath, language, isForced, format) { - var name = getNameWithoutExtension(mediaPath); - name = filerepository.getValidFileName(name), language && (name += "." + language.toLowerCase()), isForced && (name += ".foreign"), name = name + "." + format.toLowerCase(); - var mediaFolder = filerepository.getParentPath(localItem.LocalPath); - return filerepository.combinePath(mediaFolder, name) - } - - function getItemFileSize(path) { - return filerepository.getItemFileSize(path) - } - - function getNameWithoutExtension(path) { - var fileName = path, - pos = fileName.lastIndexOf("."); - return pos > 0 && (fileName = fileName.substring(0, pos)), fileName - } - - function downloadFile(url, localItem) { - var imageUrl = getImageUrl(localItem.Item.ServerId, localItem.Item.Id, { - type: "Primary", - index: 0 - }); - return transfermanager.downloadFile(url, localItem, imageUrl) - } - - function downloadSubtitles(url, fileName) { - return transfermanager.downloadSubtitles(url, fileName) - } - - function getImageUrl(serverId, itemId, imageOptions) { - var imageType = imageOptions.type, - index = imageOptions.index, - pathArray = getImagePath(serverId, itemId, imageType, index); - return filerepository.getImageUrl(pathArray) - } - - function hasImage(serverId, itemId, imageType, index) { - var pathArray = getImagePath(serverId, itemId, imageType, index), - localFilePath = filerepository.getFullMetadataPath(pathArray); - return filerepository.fileExists(localFilePath).then(function(exists) { - return Promise.resolve(exists) - }, function(err) { - return Promise.resolve(!1) - }) - } - - function fileExists(localFilePath) { - return filerepository.fileExists(localFilePath) - } - - function downloadImage(localItem, url, serverId, itemId, imageType, index) { - var localPathParts = getImagePath(serverId, itemId, imageType, index); - return transfermanager.downloadImage(url, localPathParts) - } - - function isDownloadFileInQueue(path) { - return transfermanager.isDownloadFileInQueue(path) - } - - function getDownloadItemCount() { - return transfermanager.getDownloadItemCount() - } - - function getDirectoryPath(item) { - var parts = [], - itemtype = item.Type.toLowerCase(), - mediaType = (item.MediaType || "").toLowerCase(); - "episode" === itemtype || "series" === itemtype || "season" === itemtype ? parts.push("TV") : "video" === mediaType ? parts.push("Videos") : "audio" === itemtype || "musicalbum" === itemtype || "musicartist" === itemtype ? parts.push("Music") : "photo" === itemtype || "photoalbum" === itemtype ? parts.push("Photos") : null; - var albumArtist = item.AlbumArtist; - albumArtist && parts.push(albumArtist); - var seriesName = item.SeriesName; - seriesName && parts.push(seriesName); - var seasonName = item.SeasonName; - seasonName && parts.push(seasonName), item.Album && parts.push(item.Album), ("video" === mediaType && "episode" !== itemtype || item.IsFolder) && parts.push(item.Name); - for (var finalParts = [], i = 0; i < parts.length; i++) finalParts.push(filerepository.getValidFileName(parts[i])); - return finalParts - } - - function getImagePath(serverId, itemId, imageType, index) { - var parts = []; - parts.push("images"), index = index || 0, parts.push(itemId + "_" + imageType + "_" + index.toString()); - for (var finalParts = [], i = 0; i < parts.length; i++) finalParts.push(parts[i]); - return finalParts - } - - function getLocalFileName(item, originalFileName) { - var filename = originalFileName || item.Name; - return filerepository.getValidFileName(filename) - } - - function resyncTransfers() { - return transfermanager.resyncTransfers() - } - - function createGuid() { - var d = (new Date).getTime(); - return window.performance && "function" == typeof window.performance.now && (d += performance.now()), "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { - var r = (d + 16 * Math.random()) % 16 | 0; - return d = Math.floor(d / 16), ("x" === c ? r : 3 & r | 8).toString(16) - }) - } - - function startsWith(str, find) { - return !!(str && find && str.length > find.length && 0 === str.indexOf(find)) - } - - function stripStart(str, find) { - return startsWith(str, find) ? str.substr(find.length) : str - } - - function filterDistinct(value, index, self) { - return self.indexOf(value) === index - } - - function enableBackgroundCompletion() { - return transfermanager.enableBackgroundCompletion - } - return { - getLocalItem: getLocalItem, - getDirectoryPath: getDirectoryPath, - getLocalFileName: getLocalFileName, - recordUserAction: recordUserAction, - getUserActions: getUserActions, - deleteUserAction: deleteUserAction, - deleteUserActions: deleteUserActions, - removeLocalItem: removeLocalItem, - addOrUpdateLocalItem: addOrUpdateLocalItem, - downloadFile: downloadFile, - downloadSubtitles: downloadSubtitles, - hasImage: hasImage, - downloadImage: downloadImage, - getImageUrl: getImageUrl, - getSubtitleSaveFileName: getSubtitleSaveFileName, - getServerItems: getServerItems, - getItemFileSize: getItemFileSize, - isDownloadFileInQueue: isDownloadFileInQueue, - getDownloadItemCount: getDownloadItemCount, - getViews: getViews, - getViewItems: getViewItems, - resyncTransfers: resyncTransfers, - getItemsFromIds: getItemsFromIds, - removeObsoleteContainerItems: removeObsoleteContainerItems, - fileExists: fileExists, - enableBackgroundCompletion: enableBackgroundCompletion - } -}); diff --git a/src/libraries/apiclient/package.json b/src/libraries/apiclient/package.json deleted file mode 100644 index 67f9d49756..0000000000 --- a/src/libraries/apiclient/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "main": "apiclient.js" -} diff --git a/src/libraries/apiclient/sync/filerepository.js b/src/libraries/apiclient/sync/filerepository.js deleted file mode 100644 index 5b7088af31..0000000000 --- a/src/libraries/apiclient/sync/filerepository.js +++ /dev/null @@ -1,45 +0,0 @@ -define([], function() { - "use strict"; - - function getValidFileName(path) { - return path - } - - function getFullLocalPath(pathArray) { - return pathArray.join("/") - } - - function getPathFromArray(pathArray) { - return pathArray.join("/") - } - - function deleteFile(path) { - return Promise.resolve() - } - - function deleteDirectory(path) { - return Promise.resolve() - } - - function fileExists(path) { - return Promise.resolve() - } - - function getItemFileSize(path) { - return Promise.resolve(0) - } - - function getImageUrl(pathParts) { - return pathParts.join("/") - } - return { - getValidFileName: getValidFileName, - getFullLocalPath: getFullLocalPath, - getPathFromArray: getPathFromArray, - deleteFile: deleteFile, - deleteDirectory: deleteDirectory, - fileExists: fileExists, - getItemFileSize: getItemFileSize, - getImageUrl: getImageUrl - } -}); diff --git a/src/libraries/apiclient/sync/itemrepository.js b/src/libraries/apiclient/sync/itemrepository.js deleted file mode 100644 index db1f962876..0000000000 --- a/src/libraries/apiclient/sync/itemrepository.js +++ /dev/null @@ -1,123 +0,0 @@ -define([], function() { - "use strict"; - - function ServerDatabase(dbName, readyCallback) { - var request = indexedDB.open(dbName, dbVersion); - request.onerror = function(event) {}, request.onupgradeneeded = function(event) { - var db = event.target.result; - db.createObjectStore(dbName).transaction.oncomplete = function(event) { - readyCallback(db) - } - }, request.onsuccess = function(event) { - var db = event.target.result; - readyCallback(db) - } - } - - function getDbName(serverId) { - return "items_" + serverId - } - - function getDb(serverId, callback) { - var dbName = getDbName(serverId), - db = databases[dbName]; - if (db) return void callback(db); - new ServerDatabase(dbName, function(db) { - databases[dbName] = db, callback(db) - }) - } - - function getServerItemTypes(serverId, userId) { - return getAll(serverId, userId).then(function(all) { - return all.map(function(item2) { - return item2.Item.Type || "" - }).filter(filterDistinct) - }) - } - - function getAll(serverId, userId) { - return new Promise(function(resolve, reject) { - getDb(serverId, function(db) { - var request, storeName = getDbName(serverId), - transaction = db.transaction([storeName], "readonly"), - objectStore = transaction.objectStore(storeName); - if ("getAll" in objectStore) request = objectStore.getAll(null, 1e4), request.onsuccess = function(event) { - resolve(event.target.result) - }; - else { - var results = []; - request = objectStore.openCursor(), request.onsuccess = function(event) { - var cursor = event.target.result; - cursor ? (results.push(cursor.value), cursor.continue()) : resolve(results) - } - } - request.onerror = reject - }) - }) - } - - function get(serverId, key) { - return new Promise(function(resolve, reject) { - getDb(serverId, function(db) { - var storeName = getDbName(serverId), - transaction = db.transaction([storeName], "readonly"), - objectStore = transaction.objectStore(storeName), - request = objectStore.get(key); - request.onerror = reject, request.onsuccess = function(event) { - resolve(request.result) - } - }) - }) - } - - function set(serverId, key, val) { - return new Promise(function(resolve, reject) { - getDb(serverId, function(db) { - var storeName = getDbName(serverId), - transaction = db.transaction([storeName], "readwrite"), - objectStore = transaction.objectStore(storeName), - request = objectStore.put(val, key); - request.onerror = reject, request.onsuccess = resolve - }) - }) - } - - function remove(serverId, key) { - return new Promise(function(resolve, reject) { - getDb(serverId, function(db) { - var storeName = getDbName(serverId), - transaction = db.transaction([storeName], "readwrite"), - objectStore = transaction.objectStore(storeName), - request = objectStore.delete(key); - request.onerror = reject, request.onsuccess = resolve - }) - }) - } - - function clear(serverId) { - return new Promise(function(resolve, reject) { - getDb(serverId, function(db) { - var storeName = getDbName(serverId), - transaction = db.transaction([storeName], "readwrite"), - objectStore = transaction.objectStore(storeName), - request = objectStore.clear(); - request.onerror = reject, request.onsuccess = resolve - }) - }) - } - - function filterDistinct(value, index, self) { - return self.indexOf(value) === index - } - var indexedDB = self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB, - dbVersion = (self.IDBTransaction || self.webkitIDBTransaction || self.msIDBTransaction, self.IDBKeyRange || self.webkitIDBKeyRange || self.msIDBKeyRange, 1), - databases = {}; - return { - get: get, - set: set, - remove: remove, - clear: clear, - getAll: getAll, - getServerItemTypes: getServerItemTypes - } -}); diff --git a/src/libraries/apiclient/sync/localsync.js b/src/libraries/apiclient/sync/localsync.js deleted file mode 100644 index 14e9ff9c0f..0000000000 --- a/src/libraries/apiclient/sync/localsync.js +++ /dev/null @@ -1,17 +0,0 @@ -define(["connectionManager"], function(connectionManager) { - "use strict"; - var isSyncing; - return { - sync: function(options) { - return console.debug("localSync.sync starting..."), isSyncing ? Promise.resolve() : (isSyncing = !0, new Promise(function(resolve, reject) { - require(["multiserversync", "appSettings"], function(MultiServerSync, appSettings) { - options = options || {}, options.cameraUploadServers = appSettings.cameraUploadServers(), (new MultiServerSync).sync(connectionManager, options).then(function() { - isSyncing = null, resolve() - }, function(err) { - isSyncing = null, reject(err) - }) - }) - })) - } - } -}); diff --git a/src/libraries/apiclient/sync/mediasync.js b/src/libraries/apiclient/sync/mediasync.js deleted file mode 100644 index e6d9e781f3..0000000000 --- a/src/libraries/apiclient/sync/mediasync.js +++ /dev/null @@ -1,370 +0,0 @@ -define(["localassetmanager"], function(localassetmanager) { - "use strict"; - - function processDownloadStatus(apiClient, serverInfo, options) { - return console.debug("mediasync: begin processDownloadStatus"), localassetmanager.resyncTransfers().then(function() { - return localassetmanager.getServerItems(serverInfo.Id).then(function(items) { - console.debug("mediasync: begin processDownloadStatus getServerItems completed"); - var p = Promise.resolve(), - cnt = 0; - return items.filter(function(item) { - return "transferring" === item.SyncStatus || "queued" === item.SyncStatus - }).forEach(function(item) { - p = p.then(function() { - return reportTransfer(apiClient, item) - }), cnt++ - }), p.then(function() { - console.debug("mediasync: exit processDownloadStatus"); - console.debug("items reported: " + cnt.toString()); - return Promise.resolve(); - }) - }) - }) - } - - function reportTransfer(apiClient, item) { - return localassetmanager.getItemFileSize(item.LocalPath).then(function(size) { - return size > 0 ? apiClient.reportSyncJobItemTransferred(item.SyncJobItemId).then(function() { - return item.SyncStatus = "synced", console.debug("mediasync: reportSyncJobItemTransferred called for " + item.LocalPath), localassetmanager.addOrUpdateLocalItem(item) - }, function(error) { - return console.error("mediasync: mediasync error on reportSyncJobItemTransferred", error), item.SyncStatus = "error", localassetmanager.addOrUpdateLocalItem(item) - }) : localassetmanager.isDownloadFileInQueue(item.LocalPath).then(function(result) { - return result ? Promise.resolve() : (console.debug("mediasync: reportTransfer: Size is 0 and download no longer in queue. Deleting item."), localassetmanager.removeLocalItem(item).then(function() { - return console.debug("mediasync: reportTransfer: Item deleted."), Promise.resolve() - }, function(err2) { - return console.debug("mediasync: reportTransfer: Failed to delete item.", err2), Promise.resolve() - })) - }) - }, function(error) { - return console.error("mediasync: reportTransfer: error on getItemFileSize. Deleting item.", error), localassetmanager.removeLocalItem(item).then(function() { - return console.debug("mediasync: reportTransfer: Item deleted."), Promise.resolve() - }, function(err2) { - return console.error("mediasync: reportTransfer: Failed to delete item.", err2), Promise.resolve() - }) - }) - } - - function reportOfflineActions(apiClient, serverInfo) { - return console.debug("mediasync: begin reportOfflineActions"), localassetmanager.getUserActions(serverInfo.Id).then(function(actions) { - return actions.length ? apiClient.reportOfflineActions(actions).then(function() { - return localassetmanager.deleteUserActions(actions).then(function() { - return console.debug("mediasync: exit reportOfflineActions (actions reported and deleted.)"), Promise.resolve() - }) - }, function(err) { - return console.error("mediasync: error on apiClient.reportOfflineActions: " + err.toString()), localassetmanager.deleteUserActions(actions) - }) : (console.debug("mediasync: exit reportOfflineActions (no actions)"), Promise.resolve()) - }) - } - - function syncData(apiClient, serverInfo) { - return console.debug("mediasync: begin syncData"), localassetmanager.getServerItems(serverInfo.Id).then(function(items) { - var completedItems = items.filter(function(item) { - return item && ("synced" === item.SyncStatus || "error" === item.SyncStatus) - }), - request = { - TargetId: apiClient.deviceId(), - LocalItemIds: completedItems.map(function(xitem) { - return xitem.ItemId - }) - }; - return apiClient.syncData(request).then(function(result) { - return afterSyncData(apiClient, serverInfo, result).then(function() { - return console.debug("mediasync: exit syncData"), Promise.resolve() - }, function(err) { - return console.error("mediasync: error in syncData: " + err.toString()), Promise.resolve() - }) - }) - }) - } - - function afterSyncData(apiClient, serverInfo, syncDataResult) { - console.debug("mediasync: begin afterSyncData"); - var p = Promise.resolve(); - return syncDataResult.ItemIdsToRemove && syncDataResult.ItemIdsToRemove.length > 0 && syncDataResult.ItemIdsToRemove.forEach(function(itemId) { - p = p.then(function() { - return removeLocalItem(itemId, serverInfo.Id) - }) - }), p = p.then(function() { - return removeObsoleteContainerItems(serverInfo.Id) - }), p.then(function() { - return console.debug("mediasync: exit afterSyncData"), Promise.resolve() - }) - } - - function removeObsoleteContainerItems(serverId) { - return console.debug("mediasync: begin removeObsoleteContainerItems"), localassetmanager.removeObsoleteContainerItems(serverId) - } - - function removeLocalItem(itemId, serverId) { - return console.debug("mediasync: begin removeLocalItem"), localassetmanager.getLocalItem(serverId, itemId).then(function(item) { - return item ? localassetmanager.removeLocalItem(item) : Promise.resolve() - }, function(err2) { - return console.error("mediasync: removeLocalItem: Failed: ", err2), Promise.resolve() - }) - } - - function getNewMedia(apiClient, downloadCount) { - return console.debug("mediasync: begin getNewMedia"), apiClient.getReadySyncItems(apiClient.deviceId()).then(function(jobItems) { - console.debug("mediasync: getReadySyncItems returned " + jobItems.length + " items"); - var p = Promise.resolve(), - currentCount = downloadCount; - return jobItems.forEach(function(jobItem) { - currentCount++ <= 10 && (p = p.then(function() { - return getNewItem(jobItem, apiClient) - })) - }), p.then(function() { - return console.debug("mediasync: exit getNewMedia"), Promise.resolve() - }) - }, function(err) { - return console.error("mediasync: getReadySyncItems: Failed: ", err), Promise.resolve() - }) - } - - function afterMediaDownloaded(apiClient, jobItem, localItem) { - return console.debug("mediasync: begin afterMediaDownloaded"), getImages(apiClient, jobItem, localItem).then(function() { - var libraryItem = jobItem.Item; - return downloadParentItems(apiClient, jobItem, libraryItem).then(function() { - return getSubtitles(apiClient, jobItem, localItem) - }) - }) - } - - function createLocalItem(libraryItem, jobItem) { - console.debug("localassetmanager: begin createLocalItem"); - var item = { - Item: libraryItem, - ItemId: libraryItem.Id, - ServerId: libraryItem.ServerId, - Id: libraryItem.Id - }; - return jobItem && (item.SyncJobItemId = jobItem.SyncJobItemId), console.debug("localassetmanager: end createLocalItem"), item - } - - function getNewItem(jobItem, apiClient) { - console.debug("mediasync: begin getNewItem"); - var libraryItem = jobItem.Item; - return localassetmanager.getLocalItem(libraryItem.ServerId, libraryItem.Id).then(function(existingItem) { - if (existingItem && ("queued" === existingItem.SyncStatus || "transferring" === existingItem.SyncStatus || "synced" === existingItem.SyncStatus) && (console.debug("mediasync: getNewItem: getLocalItem found existing item"), localassetmanager.enableBackgroundCompletion())) return Promise.resolve(); - libraryItem.CanDelete = !1, libraryItem.CanDownload = !1, libraryItem.SupportsSync = !1, libraryItem.People = [], libraryItem.Chapters = [], libraryItem.Studios = [], libraryItem.SpecialFeatureCount = null, libraryItem.LocalTrailerCount = null, libraryItem.RemoteTrailers = []; - var localItem = createLocalItem(libraryItem, jobItem); - return localItem.SyncStatus = "queued", downloadMedia(apiClient, jobItem, localItem) - }) - } - - function downloadParentItems(apiClient, jobItem, libraryItem) { - var p = Promise.resolve(); - return libraryItem.SeriesId && (p = p.then(function() { - return downloadItem(apiClient, libraryItem.SeriesId) - })), libraryItem.SeasonId && (p = p.then(function() { - return downloadItem(apiClient, libraryItem.SeasonId).then(function(seasonItem) { - return libraryItem.SeasonPrimaryImageTag = (seasonItem.Item.ImageTags || {}).Primary, Promise.resolve() - }) - })), libraryItem.AlbumId && (p = p.then(function() { - return downloadItem(apiClient, libraryItem.AlbumId) - })), p - } - - function downloadItem(apiClient, itemId) { - return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(downloadedItem) { - downloadedItem.CanDelete = !1, downloadedItem.CanDownload = !1, downloadedItem.SupportsSync = !1, downloadedItem.People = [], downloadedItem.SpecialFeatureCount = null, downloadedItem.BackdropImageTags = null, downloadedItem.ParentBackdropImageTags = null, downloadedItem.ParentArtImageTag = null, downloadedItem.ParentLogoImageTag = null; - var localItem = createLocalItem(downloadedItem, null); - return localassetmanager.addOrUpdateLocalItem(localItem).then(function() { - return Promise.resolve(localItem) - }, function(err) { - return console.error("mediasync: downloadItem failed: " + err.toString()), Promise.resolve(null) - }) - }) - } - - function ensureLocalPathParts(localItem, jobItem) { - if (!localItem.LocalPathParts) { - var libraryItem = localItem.Item, - parts = localassetmanager.getDirectoryPath(libraryItem); - parts.push(localassetmanager.getLocalFileName(libraryItem, jobItem.OriginalFileName)), localItem.LocalPathParts = parts - } - } - - function downloadMedia(apiClient, jobItem, localItem) { - console.debug("mediasync: downloadMedia: start."); - var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/File", { - api_key: apiClient.accessToken() - }); - return ensureLocalPathParts(localItem, jobItem), localassetmanager.downloadFile(url, localItem).then(function(result) { - console.debug("mediasync: downloadMedia-downloadFile returned path: " + result.path); - var localPath = result.path, - libraryItem = localItem.Item; - if (localPath && libraryItem.MediaSources) - for (var i = 0; i < libraryItem.MediaSources.length; i++) { - var mediaSource = libraryItem.MediaSources[i]; - mediaSource.Path = localPath, mediaSource.Protocol = "File" - } - return localItem.LocalPath = localPath, localItem.SyncStatus = "transferring", localassetmanager.addOrUpdateLocalItem(localItem).then(function() { - return afterMediaDownloaded(apiClient, jobItem, localItem).then(function() { - return result.isComplete ? (localItem.SyncStatus = "synced", reportTransfer(apiClient, localItem)) : Promise.resolve() - }, function(err) { - return console.debug("mediasync: downloadMedia: afterMediaDownloaded failed: " + err), Promise.reject(err) - }) - }, function(err) { - return console.debug("mediasync: downloadMedia: addOrUpdateLocalItem failed: " + err), Promise.reject(err) - }) - }, function(err) { - return console.debug("mediasync: downloadMedia: localassetmanager.downloadFile failed: " + err), Promise.reject(err) - }) - } - - function getImages(apiClient, jobItem, localItem) { - console.debug("mediasync: begin getImages"); - var p = Promise.resolve(), - libraryItem = localItem.Item, - serverId = libraryItem.ServerId, - mainImageTag = (libraryItem.ImageTags || {}).Primary; - libraryItem.Id && mainImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.Id, mainImageTag, "Primary") - })); - var logoImageTag = (libraryItem.ImageTags || {}).Logo; - libraryItem.Id && logoImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.Id, logoImageTag, "Logo") - })); - var artImageTag = (libraryItem.ImageTags || {}).Art; - libraryItem.Id && artImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.Id, artImageTag, "Art") - })); - var bannerImageTag = (libraryItem.ImageTags || {}).Banner; - libraryItem.Id && bannerImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.Id, bannerImageTag, "Banner") - })); - var thumbImageTag = (libraryItem.ImageTags || {}).Thumb; - if (libraryItem.Id && thumbImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.Id, thumbImageTag, "Thumb") - })), libraryItem.Id && libraryItem.BackdropImageTags) - for (var i = 0; i < libraryItem.BackdropImageTags.length; i++); - return libraryItem.SeriesId && libraryItem.SeriesPrimaryImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.SeriesId, libraryItem.SeriesPrimaryImageTag, "Primary") - })), libraryItem.SeriesId && libraryItem.SeriesThumbImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.SeriesId, libraryItem.SeriesThumbImageTag, "Thumb") - })), libraryItem.SeasonId && libraryItem.SeasonPrimaryImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.SeasonId, libraryItem.SeasonPrimaryImageTag, "Primary") - })), libraryItem.AlbumId && libraryItem.AlbumPrimaryImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.AlbumId, libraryItem.AlbumPrimaryImageTag, "Primary") - })), libraryItem.ParentThumbItemId && libraryItem.ParentThumbImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.ParentThumbItemId, libraryItem.ParentThumbImageTag, "Thumb") - })), libraryItem.ParentPrimaryImageItemId && libraryItem.ParentPrimaryImageTag && (p = p.then(function() { - return downloadImage(localItem, apiClient, serverId, libraryItem.ParentPrimaryImageItemId, libraryItem.ParentPrimaryImageTag, "Primary") - })), p.then(function() { - return console.debug("mediasync: finished getImages"), localassetmanager.addOrUpdateLocalItem(localItem) - }, function(err) { - return console.error("mediasync: error getImages: " + err.toString()), Promise.resolve() - }) - } - - function downloadImage(localItem, apiClient, serverId, itemId, imageTag, imageType, index) { - return index = index || 0, localassetmanager.hasImage(serverId, itemId, imageType, index).then(function(hasImage) { - if (hasImage) return console.debug("mediasync: downloadImage - skip existing: " + itemId + " " + imageType + "_" + index.toString()), Promise.resolve(); - var maxWidth = 400; - "backdrop" === imageType && (maxWidth = null); - var imageUrl = apiClient.getScaledImageUrl(itemId, { - tag: imageTag, - type: imageType, - maxWidth: maxWidth, - api_key: apiClient.accessToken() - }); - return console.debug("mediasync: downloadImage " + itemId + " " + imageType + "_" + index.toString()), localassetmanager.downloadImage(localItem, imageUrl, serverId, itemId, imageType, index).then(function(result) { - return Promise.resolve(result) - }, function(err) { - return console.error("mediasync: error downloadImage: " + err.toString()), Promise.resolve() - }) - }, function(err) { - return console.error("mediasync: error downloadImage: " + err.toString()), Promise.resolve() - }) - } - - function getSubtitles(apiClient, jobItem, localItem) { - if (console.debug("mediasync: begin getSubtitles"), !jobItem.Item.MediaSources.length) return console.debug("mediasync: cannot download subtitles because video has no media source info."), Promise.resolve(); - var files = jobItem.AdditionalFiles.filter(function(f) { - return "Subtitles" === f.Type - }), - mediaSource = jobItem.Item.MediaSources[0], - p = Promise.resolve(); - return files.forEach(function(file) { - p = p.then(function() { - return getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource) - }) - }), p.then(function() { - return console.debug("mediasync: exit getSubtitles"), Promise.resolve() - }) - } - - function getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource) { - console.debug("mediasync: begin getItemSubtitle"); - var subtitleStream = mediaSource.MediaStreams.filter(function(m) { - return "Subtitle" === m.Type && m.Index === file.Index - })[0]; - if (!subtitleStream) return console.debug("mediasync: cannot download subtitles because matching stream info was not found."), Promise.resolve(); - var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/AdditionalFiles", { - Name: file.Name, - api_key: apiClient.accessToken() - }), - fileName = localassetmanager.getSubtitleSaveFileName(localItem, jobItem.OriginalFileName, subtitleStream.Language, subtitleStream.IsForced, subtitleStream.Codec); - return localassetmanager.downloadSubtitles(url, fileName).then(function(subtitleResult) { - return localItem.AdditionalFiles && localItem.AdditionalFiles.forEach(function(item) { - item.Name === file.Name && (item.Path = subtitleResult.path) - }), subtitleStream.Path = subtitleResult.path, subtitleStream.DeliveryMethod = "External", localassetmanager.addOrUpdateLocalItem(localItem) - }) - } - - function checkLocalFileExistence(apiClient, serverInfo, options) { - return options.checkFileExistence ? (console.debug("mediasync: begin checkLocalFileExistence"), localassetmanager.getServerItems(serverInfo.Id).then(function(items) { - var completedItems = items.filter(function(item) { - return item && ("synced" === item.SyncStatus || "error" === item.SyncStatus) - }), - p = Promise.resolve(); - return completedItems.forEach(function(completedItem) { - p = p.then(function() { - return localassetmanager.fileExists(completedItem.LocalPath).then(function(exists) { - return exists ? Promise.resolve() : localassetmanager.removeLocalItem(completedItem).then(function() { - return Promise.resolve() - }, function() { - return Promise.resolve() - }) - }) - }) - }), p - })) : Promise.resolve() - } - return function() { - var self = this; - "string" == typeof webWorkerBaseUrl && -1 !== webWorkerBaseUrl.indexOf("ms-appx://") ? self.sync = function(apiClient, serverInfo, options) { - return console.debug("mediasync: start sync"), checkLocalFileExistence(apiClient, serverInfo, options).then(function() { - return processDownloadStatus(apiClient, serverInfo, options).then(function() { - return localassetmanager.getDownloadItemCount().then(function(downloadCount) { - return !0 === options.syncCheckProgressOnly && downloadCount > 2 ? Promise.resolve() : reportOfflineActions(apiClient, serverInfo).then(function() { - return getNewMedia(apiClient, downloadCount).then(function() { - return syncData(apiClient, serverInfo).then(function() { - return console.debug("mediasync: Exit sync"), Promise.resolve() - }) - }) - }) - }) - }) - }, function(err) { - console.error(err.toString()) - }) - } : self.sync = function(apiClient, serverInfo, options) { - return console.debug("mediasync: Start sync"), checkLocalFileExistence(apiClient, serverInfo, options).then(function() { - return syncData(apiClient, serverInfo).then(function() { - return processDownloadStatus(apiClient, serverInfo, options).then(function() { - return localassetmanager.getDownloadItemCount().then(function(downloadCount) { - return !0 === options.syncCheckProgressOnly && downloadCount > 2 ? Promise.resolve() : reportOfflineActions(apiClient, serverInfo).then(function() { - return getNewMedia(apiClient, downloadCount).then(function() { - return syncData(apiClient, serverInfo) - }) - }) - }) - }) - }) - }, function(err) { - console.error(err.toString()) - }) - } - } -}); diff --git a/src/libraries/apiclient/sync/multiserversync.js b/src/libraries/apiclient/sync/multiserversync.js deleted file mode 100644 index ff01a5ad1d..0000000000 --- a/src/libraries/apiclient/sync/multiserversync.js +++ /dev/null @@ -1,22 +0,0 @@ -define(["serversync"], function(ServerSync) { - "use strict"; - - function syncNext(connectionManager, servers, index, options, resolve, reject) { - var length = servers.length; - if (index >= length) return console.debug("MultiServerSync.sync complete"), void resolve(); - var server = servers[index]; - console.debug("Creating ServerSync to server: " + server.Id), (new ServerSync).sync(connectionManager, server, options).then(function() { - console.debug("ServerSync succeeded to server: " + server.Id), syncNext(connectionManager, servers, index + 1, options, resolve, reject) - }, function(err) { - console.error("ServerSync failed to server: " + server.Id + ". " + err), syncNext(connectionManager, servers, index + 1, options, resolve, reject) - }) - } - - function MultiServerSync() {} - return MultiServerSync.prototype.sync = function(connectionManager, options) { - return console.debug("MultiServerSync.sync starting..."), new Promise(function(resolve, reject) { - var servers = connectionManager.getSavedServers(); - syncNext(connectionManager, servers, 0, options, resolve, reject) - }) - }, MultiServerSync -}); diff --git a/src/libraries/apiclient/sync/serversync.js b/src/libraries/apiclient/sync/serversync.js deleted file mode 100644 index 887b829cee..0000000000 --- a/src/libraries/apiclient/sync/serversync.js +++ /dev/null @@ -1,42 +0,0 @@ -define([], function() { - "use strict"; - - function performSync(connectionManager, server, options) { - console.debug("ServerSync.performSync to server: " + server.Id), options = options || {}; - var cameraUploadServers = options.cameraUploadServers || []; - console.debug("ServerSync cameraUploadServers: " + JSON.stringify(cameraUploadServers)); - var uploadPhotos = -1 !== cameraUploadServers.indexOf(server.Id); - return console.debug("ServerSync uploadPhotos: " + uploadPhotos), (uploadPhotos ? uploadContent(connectionManager, server, options) : Promise.resolve()).then(function() { - return syncMedia(connectionManager, server, options) - }) - } - - function uploadContent(connectionManager, server, options) { - return new Promise().reject(); - } - - function syncMedia(connectionManager, server, options) { - return new Promise(function(resolve, reject) { - require(["mediasync"], function(MediaSync) { - var apiClient = connectionManager.getApiClient(server.Id); - (new MediaSync).sync(apiClient, server, options).then(resolve, reject) - }) - }) - } - - function ServerSync() {} - return ServerSync.prototype.sync = function(connectionManager, server, options) { - if (!server.AccessToken && !server.ExchangeToken) return console.debug("Skipping sync to server " + server.Id + " because there is no saved authentication information."), Promise.resolve(); - var connectionOptions = { - updateDateLastAccessed: !1, - enableWebSocket: !1, - reportCapabilities: !1, - enableAutomaticBitrateDetection: !1 - }; - return connectionManager.connectToServer(server, connectionOptions).then(function(result) { - return "SignedIn" === result.State ? performSync(connectionManager, server, options) : (console.error("Unable to connect to server id: " + server.Id), Promise.reject()) - }, function(err) { - throw console.error("Unable to connect to server id: " + server.Id), err - }) - }, ServerSync -}); diff --git a/src/libraries/apiclient/sync/transfermanager.js b/src/libraries/apiclient/sync/transfermanager.js deleted file mode 100644 index d9889fffbd..0000000000 --- a/src/libraries/apiclient/sync/transfermanager.js +++ /dev/null @@ -1,30 +0,0 @@ -define([], function() { - "use strict"; - - function downloadFile(url, folder, localItem, imageUrl) { - return Promise.reject() - } - - function downloadSubtitles(url, folder, fileName) { - return Promise.reject() - } - - function downloadImage(url, folder, fileName) { - return Promise.reject() - } - - function resyncTransfers() { - return Promise.resolve() - } - - function getDownloadItemCount() { - return Promise.resolve(0) - } - return { - downloadFile: downloadFile, - downloadSubtitles: downloadSubtitles, - downloadImage: downloadImage, - resyncTransfers: resyncTransfers, - getDownloadItemCount: getDownloadItemCount - } -}); diff --git a/src/libraries/apiclient/sync/useractionrepository.js b/src/libraries/apiclient/sync/useractionrepository.js deleted file mode 100644 index 7b33ba1c35..0000000000 --- a/src/libraries/apiclient/sync/useractionrepository.js +++ /dev/null @@ -1,108 +0,0 @@ -define([], function() { - "use strict"; - - function getDb(callback) { - var db = databaseInstance; - if (db) return void callback(db); - var request = indexedDB.open(dbName, dbVersion); - request.onerror = function(event) {}, request.onupgradeneeded = function(event) { - var db = event.target.result; - db.createObjectStore(dbName).transaction.oncomplete = function(event) { - callback(db) - } - }, request.onsuccess = function(event) { - var db = event.target.result; - callback(db) - } - } - - function getByServerId(serverId) { - return getAll().then(function(items) { - return items.filter(function(item) { - return item.ServerId === serverId - }) - }) - } - - function getAll() { - return new Promise(function(resolve, reject) { - getDb(function(db) { - var request, storeName = dbName, - transaction = db.transaction([storeName], "readonly"), - objectStore = transaction.objectStore(storeName); - if ("getAll" in objectStore) request = objectStore.getAll(null, 1e4), request.onsuccess = function(event) { - resolve(event.target.result) - }; - else { - var results = []; - request = objectStore.openCursor(), request.onsuccess = function(event) { - var cursor = event.target.result; - cursor ? (results.push(cursor.value), cursor.continue()) : resolve(results) - } - } - request.onerror = reject - }) - }) - } - - function get(key) { - return new Promise(function(resolve, reject) { - getDb(function(db) { - var storeName = dbName, - transaction = db.transaction([storeName], "readonly"), - objectStore = transaction.objectStore(storeName), - request = objectStore.get(key); - request.onerror = reject, request.onsuccess = function(event) { - resolve(request.result) - } - }) - }) - } - - function set(key, val) { - return new Promise(function(resolve, reject) { - getDb(function(db) { - var storeName = dbName, - transaction = db.transaction([storeName], "readwrite"), - objectStore = transaction.objectStore(storeName), - request = objectStore.put(val, key); - request.onerror = reject, request.onsuccess = resolve - }) - }) - } - - function remove(key) { - return new Promise(function(resolve, reject) { - getDb(function(db) { - var storeName = dbName, - transaction = db.transaction([storeName], "readwrite"), - objectStore = transaction.objectStore(storeName), - request = objectStore.delete(key); - request.onerror = reject, request.onsuccess = resolve - }) - }) - } - - function clear() { - return new Promise(function(resolve, reject) { - getDb(function(db) { - var storeName = dbName, - transaction = db.transaction([storeName], "readwrite"), - objectStore = transaction.objectStore(storeName), - request = objectStore.clear(); - request.onerror = reject, request.onsuccess = resolve - }) - }) - } - var databaseInstance, indexedDB = self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB, - dbName = (self.IDBTransaction || self.webkitIDBTransaction || self.msIDBTransaction, self.IDBKeyRange || self.webkitIDBKeyRange || self.msIDBKeyRange, "useractions"), - dbVersion = 1; - return { - get: get, - set: set, - remove: remove, - clear: clear, - getAll: getAll, - getByServerId: getByServerId - } -}); diff --git a/src/components/navdrawer/navdrawer.css b/src/libraries/navdrawer/navdrawer.css similarity index 100% rename from src/components/navdrawer/navdrawer.css rename to src/libraries/navdrawer/navdrawer.css diff --git a/src/components/navdrawer/navdrawer.js b/src/libraries/navdrawer/navdrawer.js similarity index 99% rename from src/components/navdrawer/navdrawer.js rename to src/libraries/navdrawer/navdrawer.js index 9c15fbc184..d9c246b406 100644 --- a/src/components/navdrawer/navdrawer.js +++ b/src/libraries/navdrawer/navdrawer.js @@ -260,7 +260,6 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser, TouchMenuLA.prototype.showMask = function () { mask.classList.remove("hide"); - mask.offsetWidth; mask.classList.add("backdrop"); }; diff --git a/src/components/screensavermanager.js b/src/libraries/screensavermanager.js similarity index 100% rename from src/components/screensavermanager.js rename to src/libraries/screensavermanager.js diff --git a/src/components/scroller.js b/src/libraries/scroller.js similarity index 100% rename from src/components/scroller.js rename to src/libraries/scroller.js diff --git a/src/library.html b/src/library.html index 398613f9e6..1bca607249 100644 --- a/src/library.html +++ b/src/library.html @@ -6,7 +6,7 @@ ${ButtonScanAllLibraries} - ${Help} + ${Help}
diff --git a/src/list.html b/src/list.html index 375653c12d..464cd15530 100644 --- a/src/list.html +++ b/src/list.html @@ -7,45 +7,45 @@ ${HeaderPlayAll}
diff --git a/src/livetv.html b/src/livetv.html index 9986529b7f..fdf19bd915 100644 --- a/src/livetv.html +++ b/src/livetv.html @@ -7,7 +7,7 @@
@@ -16,7 +16,7 @@
@@ -25,7 +25,7 @@
@@ -34,7 +34,7 @@
@@ -43,7 +43,7 @@
@@ -52,7 +52,7 @@
@@ -63,7 +63,7 @@
- +
@@ -72,7 +72,7 @@
diff --git a/src/livetvsettings.html b/src/livetvsettings.html index 1b1557889e..02b960f08d 100644 --- a/src/livetvsettings.html +++ b/src/livetvsettings.html @@ -3,8 +3,8 @@
-

DVR

- ${Help} +

${HeaderDVR}

+ ${Help}
@@ -34,7 +34,7 @@
- +
${LabelRecordingPathHelp}
@@ -43,7 +43,7 @@
- +
@@ -51,7 +51,7 @@
- +
@@ -84,7 +84,7 @@
- +
diff --git a/src/livetvstatus.html b/src/livetvstatus.html index fc855f32b0..3aa27637d0 100644 --- a/src/livetvstatus.html +++ b/src/livetvstatus.html @@ -8,9 +8,9 @@ ${HeaderTunerDevices} - ${Help} + ${Help}
@@ -21,7 +21,7 @@

${HeaderGuideProviders}

diff --git a/src/livetvtuner.html b/src/livetvtuner.html index fecbda90aa..f45a3a1d04 100644 --- a/src/livetvtuner.html +++ b/src/livetvtuner.html @@ -5,7 +5,7 @@

${HeaderLiveTvTunerSetup}

- ${Help} + ${Help}
@@ -24,7 +24,7 @@
- +
diff --git a/src/movies.html b/src/movies.html index e552ca392f..a2221c510d 100644 --- a/src/movies.html +++ b/src/movies.html @@ -3,9 +3,9 @@
- - - + + +
@@ -46,8 +46,8 @@
- - + +
@@ -62,7 +62,7 @@
- +
@@ -74,9 +74,9 @@
- - - + + +
diff --git a/src/music.html b/src/music.html index 9a284a5f8f..1e22ae9f3a 100644 --- a/src/music.html +++ b/src/music.html @@ -37,11 +37,11 @@
- - - - - + + + + +
@@ -56,8 +56,8 @@
- - + +
@@ -72,8 +72,8 @@
- - + +
@@ -92,8 +92,8 @@
- - + +
diff --git a/src/mypreferencesmenu.html b/src/mypreferencesmenu.html index 4219059dd0..2c3ca0edd9 100644 --- a/src/mypreferencesmenu.html +++ b/src/mypreferencesmenu.html @@ -5,7 +5,7 @@

- person +
${ButtonProfile}
@@ -14,7 +14,7 @@
- tv +
${HeaderDisplay}
@@ -23,7 +23,7 @@
- home +
${HeaderHome}
@@ -32,7 +32,7 @@
- +
${TitlePlayback}
@@ -41,7 +41,7 @@
- +
${Subtitles}
@@ -50,7 +50,7 @@
- +
${ClientSettings}
@@ -61,7 +61,7 @@

${HeaderAdmin}

- dashboard +
${TabDashboard}
@@ -69,7 +69,7 @@
- +
${Metadata}
@@ -80,7 +80,7 @@

${HeaderUser}

- wifi +
${HeaderSelectServer}
@@ -88,7 +88,7 @@
- +
${ButtonSignOut}
diff --git a/src/networking.html b/src/networking.html index 1352e0ef5f..74ddefb87b 100644 --- a/src/networking.html +++ b/src/networking.html @@ -5,88 +5,110 @@
-
- -
${LanNetworksHelp}
-
-
- -
${LabelBindToLocalNetworkAddressHelp}
-
-
- -
${LabelLocalHttpServerPortNumberHelp}
-
-
- -
${LabelHttpsPortHelp}
-
+
+

${HeaderServerAddressSettings}

-
- -
${AllowRemoteAccessHelp}
-
-
- -
${AllowedRemoteAddressesHelp}
-
-
- -
-
- -
${LabelPublicHttpPortHelp}
-
-
- -
${LabelPublicHttpsPortHelp}
-
- -
- -
${LabelBaseUrlHelp}
-
- -
-
-
- -
- +
+ +
${LabelLocalHttpServerPortNumberHelp}
-
${LabelCustomCertificatePathHelp}
-
-
- -
${LabelCertificatePasswordHelp}
-
+
+ +
${LabelEnableHttpsHelp}
+
-
- -
+
+ +
${LabelHttpsPortHelp}
+
-
- -
${LabelEnableAutomaticPortMapHelp}
-
+
+ +
${LabelBaseUrlHelp}
+
+ +
+ +
${LabelBindToLocalNetworkAddressHelp}
+
+ +
+ +
${LanNetworksHelp}
+
+
+ +
+

${HeaderHttpsSettings}

+ +
+ +
${LabelRequireHttpsHelp}
+
+ +
+
+
+ +
+ +
+
${LabelCustomCertificatePathHelp}
+
+ +
+ +
${LabelCertificatePasswordHelp}
+
+
+ +
+

${HeaderRemoteAccessSettings}

+ +
+ +
${AllowRemoteAccessHelp}
+
+
+ +
${AllowedRemoteAddressesHelp}
+
+
+ +
+ +
+ +
${LabelEnableAutomaticPortMapHelp}
+
+
+ +
${LabelPublicHttpPortHelp}
+
+
+ +
${LabelPublicHttpsPortHelp}
+
+
+ + -
- + - + - - - - - - - - - - - - -
-
-
/
-
+ + + +
+ + + + +
- -
- -
-
+
-
-
@@ -155,21 +161,22 @@
- -
-
-
-

${TabPlaylist}

- +
-
+
+
+
+
+
+
-
-
-
-
diff --git a/src/plugins/backdropScreensaver/plugin.js b/src/plugins/backdropScreensaver/plugin.js new file mode 100644 index 0000000000..88bfa1f4b7 --- /dev/null +++ b/src/plugins/backdropScreensaver/plugin.js @@ -0,0 +1,52 @@ +/* eslint-disable indent */ +import connectionManager from 'connectionManager'; + +class BackdropScreensaver { + constructor() { + this.name = 'Backdrop ScreenSaver'; + this.type = 'screensaver'; + this.id = 'backdropscreensaver'; + this.supportsAnonymous = false; + } + show() { + const query = { + ImageTypes: 'Backdrop', + EnableImageTypes: 'Backdrop', + IncludeItemTypes: 'Movie,Series,MusicArtist', + SortBy: 'Random', + Recursive: true, + Fields: 'Taglines', + ImageTypeLimit: 1, + StartIndex: 0, + Limit: 200 + }; + + const apiClient = connectionManager.currentApiClient(); + apiClient.getItems(apiClient.getCurrentUserId(), query).then((result) => { + + if (result.Items.length) { + + import('slideshow').then(({default: Slideshow}) => { + const newSlideShow = new Slideshow({ + showTitle: true, + cover: true, + items: result.Items + }); + + newSlideShow.show(); + this.currentSlideshow = newSlideShow; + }).catch(console.error); + } + }); + } + + hide() { + if (this.currentSlideshow) { + this.currentSlideshow.hide(); + this.currentSlideshow = null; + } + } + } +/* eslint-enable indent */ + +export default BackdropScreensaver; diff --git a/src/plugins/bookPlayer/plugin.js b/src/plugins/bookPlayer/plugin.js new file mode 100644 index 0000000000..41c651cebf --- /dev/null +++ b/src/plugins/bookPlayer/plugin.js @@ -0,0 +1,284 @@ +import connectionManager from 'connectionManager'; +import loading from 'loading'; +import keyboardnavigation from 'keyboardnavigation'; +import dialogHelper from 'dialogHelper'; +import events from 'events'; +import 'css!./style'; +import 'material-icons'; +import 'paper-icon-button-light'; + +import TableOfContent from './tableOfContent'; + +export class BookPlayer { + constructor() { + this.name = 'Book Player'; + this.type = 'mediaplayer'; + this.id = 'bookplayer'; + this.priority = 1; + + this.onDialogClosed = this.onDialogClosed.bind(this); + this.openTableOfContents = this.openTableOfContents.bind(this); + this.onWindowKeyUp = this.onWindowKeyUp.bind(this); + } + + play(options) { + this._progress = 0; + this._loaded = false; + + loading.show(); + let elem = this.createMediaElement(); + return this.setCurrentSrc(elem, options); + } + + stop() { + this.unbindEvents(); + + let elem = this._mediaElement; + let tocElement = this._tocElement; + let rendition = this._rendition; + + if (elem) { + dialogHelper.close(elem); + this._mediaElement = null; + } + + if (tocElement) { + tocElement.destroy(); + this._tocElement = null; + } + + if (rendition) { + rendition.destroy(); + } + + // Hide loader in case player was not fully loaded yet + loading.hide(); + this._cancellationToken.shouldCancel = true; + } + + currentItem() { + return this._currentItem; + } + + currentTime() { + return this._progress * 1000; + } + + duration() { + return 1000; + } + + getBufferedRanges() { + return [{ + start: 0, + end: 10000000 + }]; + } + + volume() { + return 100; + } + + isMuted() { + return false; + } + + paused() { + return false; + } + + seekable() { + return true; + } + + onWindowKeyUp(e) { + let key = keyboardnavigation.getKeyName(e); + let rendition = this._rendition; + let book = rendition.book; + + switch (key) { + case 'l': + case 'ArrowRight': + case 'Right': + if (this._loaded) { + book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next(); + } + break; + case 'j': + case 'ArrowLeft': + case 'Left': + if (this._loaded) { + book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev(); + } + break; + case 'Escape': + if (this._tocElement) { + // Close table of contents on ESC if it is open + this._tocElement.destroy(); + } else { + // Otherwise stop the entire book player + this.stop(); + } + break; + } + } + + onDialogClosed() { + this.stop(); + } + + bindMediaElementEvents() { + let elem = this._mediaElement; + + elem.addEventListener('close', this.onDialogClosed, {once: true}); + elem.querySelector('.btnBookplayerExit').addEventListener('click', this.onDialogClosed, {once: true}); + elem.querySelector('.btnBookplayerToc').addEventListener('click', this.openTableOfContents); + } + + bindEvents() { + this.bindMediaElementEvents(); + + document.addEventListener('keyup', this.onWindowKeyUp); + // FIXME: I don't really get why document keyup event is not triggered when epub is in focus + this._rendition.on('keyup', this.onWindowKeyUp); + } + + unbindMediaElementEvents() { + let elem = this._mediaElement; + + elem.removeEventListener('close', this.onDialogClosed); + elem.querySelector('.btnBookplayerExit').removeEventListener('click', this.onDialogClosed); + elem.querySelector('.btnBookplayerToc').removeEventListener('click', this.openTableOfContents); + } + + unbindEvents() { + if (this._mediaElement) { + this.unbindMediaElementEvents(); + } + document.removeEventListener('keyup', this.onWindowKeyUp); + if (this._rendition) { + this._rendition.off('keyup', this.onWindowKeyUp); + } + } + + openTableOfContents() { + if (this._loaded) { + this._tocElement = new TableOfContent(this); + } + } + + createMediaElement() { + let elem = this._mediaElement; + + if (elem) { + return elem; + } + + elem = document.getElementById('bookPlayer'); + + if (!elem) { + elem = dialogHelper.createDialog({ + exitAnimationDuration: 400, + size: 'fullscreen', + autoFocus: false, + scrollY: false, + exitAnimation: 'fadeout', + removeOnClose: true + }); + elem.id = 'bookPlayer'; + + let html = ''; + html += '
'; + html += ''; + html += '
'; + html += '
'; + html += ''; + html += '
'; + + elem.innerHTML = html; + + dialogHelper.open(elem); + } + + this._mediaElement = elem; + + return elem; + } + + setCurrentSrc(elem, options) { + let item = options.items[0]; + this._currentItem = item; + this.streamInfo = { + started: true, + ended: false, + mediaSource: { + Id: item.Id + } + }; + + let serverId = item.ServerId; + let apiClient = connectionManager.getApiClient(serverId); + + return new Promise((resolve, reject) => { + require(['epubjs'], (epubjs) => { + let downloadHref = apiClient.getItemDownloadUrl(item.Id); + let book = epubjs.default(downloadHref, {openAs: 'epub'}); + let rendition = book.renderTo(elem, {width: '100%', height: '97%'}); + + this._currentSrc = downloadHref; + this._rendition = rendition; + let cancellationToken = { + shouldCancel: false + }; + this._cancellationToken = cancellationToken; + + return rendition.display().then(() => { + let epubElem = document.querySelector('.epub-container'); + epubElem.style.display = 'none'; + + this.bindEvents(); + + return this._rendition.book.locations.generate(1024).then(async () => { + if (cancellationToken.shouldCancel) { + return reject(); + } + + const percentageTicks = options.startPositionTicks / 10000000; + if (percentageTicks !== 0.0) { + const resumeLocation = book.locations.cfiFromPercentage(percentageTicks); + await rendition.display(resumeLocation); + } + + this._loaded = true; + epubElem.style.display = 'block'; + rendition.on('relocated', (locations) => { + this._progress = book.locations.percentageFromCfi(locations.start.cfi); + + events.trigger(this, 'timeupdate'); + }); + + loading.hide(); + + return resolve(); + }); + }, () => { + console.error('Failed to display epub'); + return reject(); + }); + }); + }); + } + + canPlayMediaType(mediaType) { + return (mediaType || '').toLowerCase() === 'book'; + } + + canPlayItem(item) { + if (item.Path && (item.Path.endsWith('epub'))) { + return true; + } + return false; + } +} + +export default BookPlayer; diff --git a/src/plugins/bookPlayer/style.css b/src/plugins/bookPlayer/style.css new file mode 100644 index 0000000000..e37b995f31 --- /dev/null +++ b/src/plugins/bookPlayer/style.css @@ -0,0 +1,39 @@ +#bookPlayer { + position: relative; + height: 100%; + width: 100%; + overflow: auto; + z-index: 100; + background: #fff; +} + +.topRightActionButtons { + right: 0.5vh; + top: 0.5vh; + z-index: 1002; + position: absolute; +} + +.topLeftActionButtons { + left: 0.5vh; + top: 0.5vh; + z-index: 1002; + position: absolute; +} + +.bookplayerButtonIcon { + color: black; + opacity: 0.7; +} + +#dialogToc { + background-color: white; +} + +.toc li { + margin-bottom: 5px; +} + +.bookplayerErrorMsg { + text-align: center; +} diff --git a/src/plugins/bookPlayer/tableOfContent.js b/src/plugins/bookPlayer/tableOfContent.js new file mode 100644 index 0000000000..6a35966b1b --- /dev/null +++ b/src/plugins/bookPlayer/tableOfContent.js @@ -0,0 +1,90 @@ +import dialogHelper from 'dialogHelper'; + +export default class TableOfContent { + constructor(bookPlayer) { + this._bookPlayer = bookPlayer; + this._rendition = bookPlayer._rendition; + + this.onDialogClosed = this.onDialogClosed.bind(this); + + this.createMediaElement(); + } + + destroy() { + let elem = this._elem; + if (elem) { + this.unbindEvents(); + dialogHelper.close(elem); + } + + this._bookPlayer._tocElement = null; + } + + bindEvents() { + let elem = this._elem; + + elem.addEventListener('close', this.onDialogClosed, {once: true}); + elem.querySelector('.btnBookplayerTocClose').addEventListener('click', this.onDialogClosed, {once: true}); + } + + unbindEvents() { + let elem = this._elem; + + elem.removeEventListener('close', this.onDialogClosed); + elem.querySelector('.btnBookplayerTocClose').removeEventListener('click', this.onDialogClosed); + } + + onDialogClosed() { + this.destroy(); + } + + replaceLinks(contents, f) { + let links = contents.querySelectorAll('a[href]'); + + links.forEach((link) => { + let href = link.getAttribute('href'); + + link.onclick = () => { + f(href); + return false; + }; + }); + } + + createMediaElement() { + let rendition = this._rendition; + + let elem = dialogHelper.createDialog({ + size: 'small', + autoFocus: false, + removeOnClose: true + }); + elem.id = 'dialogToc'; + + let tocHtml = '
'; + tocHtml += ''; + tocHtml += '
'; + tocHtml += '
    '; + rendition.book.navigation.forEach((chapter) => { + tocHtml += '
  • '; + // Remove '../' from href + let link = chapter.href.startsWith('../') ? chapter.href.substr(3) : chapter.href; + tocHtml += `${chapter.label}`; + tocHtml += '
  • '; + }); + tocHtml += '
'; + elem.innerHTML = tocHtml; + + this.replaceLinks(elem, (href) => { + let relative = rendition.book.path.relative(href); + rendition.display(relative); + this.destroy(); + }); + + this._elem = elem; + + this.bindEvents(); + + dialogHelper.open(elem); + } +} diff --git a/src/components/chromecast/chromecasthelpers.js b/src/plugins/chromecastPlayer/chromecastHelpers.js similarity index 81% rename from src/components/chromecast/chromecasthelpers.js rename to src/plugins/chromecastPlayer/chromecastHelpers.js index 9967a4d96c..ca2d27c977 100644 --- a/src/components/chromecast/chromecasthelpers.js +++ b/src/plugins/chromecastPlayer/chromecastHelpers.js @@ -37,69 +37,69 @@ define(['events'], function (events) { // 5) It wasn't as smart as it could have been about what should be part of a // URL and what should be part of human language. - var protocols = "(?:(?:http|https|rtsp|ftp):\\/\\/)"; + var protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)'; var credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters) + "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters) - + "\\@)"; + + '\\@)'; // IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452 // by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License // http://intermapper.com/ - var ipv6 = "(" - + "(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))" - + "|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))" - + "|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))" - + "|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" - + "|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" - + "|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" - + "|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" - + "|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))" - + ")(%.+)?"; + var ipv6 = '(' + + '(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))' + + '|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))' + + '|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))' + + '|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + + '|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + + '|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + + '|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + + '|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' + + ')(%.+)?'; - var ipv4 = "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\." - + "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\." - + "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\." - + "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])"; + var ipv4 = '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.' + + '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.' + + '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.' + + '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])'; // This would have been a lot cleaner if JS RegExp supported conditionals... var linkRegExpString = // begin match for protocol / username / password / host - "(?:" + '(?:' // ============================ // If we have a recognized protocol at the beginning of the URL, we're // more relaxed about what we accept, because we assume the user wants // this to be a URL, and we're not accidentally matching human language - + protocols + "?" + + protocols + '?' // optional username:password@ - + credentials + "?" + + credentials + '?' // IP address (both v4 and v6) - + "(?:" + + '(?:' // IPv6 + ipv6 // IPv4 - + "|" + ipv4 + + '|' + ipv4 - + ")" + + ')' // end match for protocol / username / password / host - + ")" + + ')' // optional port number - + "(?:\\:\\d{1,5})?" + + '(?:\\:\\d{1,5})?' // plus optional path and query params (no unicode allowed here?) - + "(?:" - + "\\/(?:" + + '(?:' + + '\\/(?:' // some characters we'll accept because it's unlikely human language // would use them after a URL unless they were part of the url - + "(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])" - + "|(?:\\%[a-f0-9]{2})" + + '(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])' + + '|(?:\\%[a-f0-9]{2})' // some characters are much more likely to be used AFTER a url and // were not intended to be included in the url itself. Mostly end // of sentence type things. It's also likely that the URL would @@ -108,9 +108,9 @@ define(['events'], function (events) { // they must be followed by another character that we're reasonably // sure is part of the url + "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))" - + ")*" - + "|\\b|\$" - + ")"; + + ')*' + + '|\\b|\$' + + ')'; // regex = XRegExp(regex,'gi'); var linkRegExp = RegExp(linkRegExpString, 'gi'); @@ -120,7 +120,7 @@ define(['events'], function (events) { // if url doesn't begin with a known protocol, add http by default function ensureProtocol(url) { if (!url.match(protocolRegExp)) { - url = "http://" + url; + url = 'http://' + url; } return url; } @@ -190,7 +190,7 @@ define(['events'], function (events) { return apiClient.getPublicSystemInfo().then(function (info) { var localAddress = info.LocalAddress; if (!localAddress) { - console.debug("No valid local address returned, defaulting to external one"); + console.debug('No valid local address returned, defaulting to external one'); localAddress = serverAddress; } addToCache(serverAddress, localAddress); diff --git a/src/components/chromecast/chromecastplayer.js b/src/plugins/chromecastPlayer/plugin.js similarity index 92% rename from src/components/chromecast/chromecastplayer.js rename to src/plugins/chromecastPlayer/plugin.js index 18103e433f..b3f75f7a6d 100644 --- a/src/components/chromecast/chromecastplayer.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -5,7 +5,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' var currentResolve; var currentReject; - var PlayerName = 'Chromecast'; + var PlayerName = 'Google Cast'; function sendConnectionResult(isOk) { @@ -54,7 +54,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' // production version registered with google // replace this value if you want to test changes on another instance - var applicationID = "F007D354"; + var applicationStable = 'F007D354'; + var applicationNightly = '6F511C87'; var messageNamespace = 'urn:x-cast:com.connectsdk'; @@ -99,6 +100,11 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' return; } + var applicationID = applicationStable; + if (userSettings.chromecastVersion() === 'nightly') { + applicationID = applicationNightly; + } + // request session var sessionRequest = new chrome.cast.SessionRequest(applicationID); var apiConfig = new chrome.cast.ApiConfig(sessionRequest, @@ -114,14 +120,14 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' */ CastPlayer.prototype.onInitSuccess = function () { this.isInitialized = true; - console.debug("chromecast init success"); + console.debug('chromecast init success'); }; /** * Generic error callback function */ CastPlayer.prototype.onError = function () { - console.debug("chromecast error"); + console.debug('chromecast error'); }; /** @@ -177,10 +183,10 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' */ CastPlayer.prototype.receiverListener = function (e) { if (e === 'available') { - console.debug("chromecast receiver found"); + console.debug('chromecast receiver found'); this.hasReceivers = true; } else { - console.debug("chromecast receiver list empty"); + console.debug('chromecast receiver list empty'); this.hasReceivers = false; } }; @@ -195,8 +201,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' this.session = null; this.deviceState = DEVICE_STATE.IDLE; this.castPlayerState = PLAYER_STATE.IDLE; - document.removeEventListener("volumeupbutton", onVolumeUpKeyDown, false); - document.removeEventListener("volumedownbutton", onVolumeDownKeyDown, false); + document.removeEventListener('volumeupbutton', onVolumeUpKeyDown, false); + document.removeEventListener('volumedownbutton', onVolumeDownKeyDown, false); console.debug('sessionUpdateListener: setting currentMediaSession to null'); this.currentMediaSession = null; @@ -211,7 +217,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' * session request in opt_sessionRequest. */ CastPlayer.prototype.launchApp = function () { - console.debug("chromecast launching app..."); + console.debug('chromecast launching app...'); chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this)); }; @@ -220,7 +226,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' * @param {Object} e A chrome.cast.Session object */ CastPlayer.prototype.onRequestSessionSuccess = function (e) { - console.debug("chromecast session success: " + e.sessionId); + console.debug('chromecast session success: ' + e.sessionId); this.onSessionConnected(e); }; @@ -232,8 +238,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' this.session.addMediaListener(this.sessionMediaListener.bind(this)); this.session.addUpdateListener(this.sessionUpdateListener.bind(this)); - document.addEventListener("volumeupbutton", onVolumeUpKeyDown, false); - document.addEventListener("volumedownbutton", onVolumeDownKeyDown, false); + document.addEventListener('volumeupbutton', onVolumeUpKeyDown, false); + document.addEventListener('volumedownbutton', onVolumeDownKeyDown, false); events.trigger(this, 'connect'); this.sendMessage({ @@ -262,7 +268,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' * Callback function for launch error */ CastPlayer.prototype.onLaunchError = function () { - console.debug("chromecast launch error"); + console.debug('chromecast launch error'); this.deviceState = DEVICE_STATE.ERROR; sendConnectionResult(false); }; @@ -284,8 +290,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' this.deviceState = DEVICE_STATE.IDLE; this.castPlayerState = PLAYER_STATE.IDLE; - document.removeEventListener("volumeupbutton", onVolumeUpKeyDown, false); - document.removeEventListener("volumedownbutton", onVolumeDownKeyDown, false); + document.removeEventListener('volumeupbutton', onVolumeUpKeyDown, false); + document.removeEventListener('volumedownbutton', onVolumeDownKeyDown, false); this.currentMediaSession = null; }; @@ -296,7 +302,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' */ CastPlayer.prototype.loadMedia = function (options, command) { if (!this.session) { - console.debug("no session"); + console.debug('no session'); return Promise.reject(); } @@ -386,7 +392,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' */ CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) { - console.debug("chromecast new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')'); + console.debug('chromecast new media session ID:' + mediaSession.mediaSessionId + ' (' + how + ')'); this.currentMediaSession = mediaSession; if (how === 'loadMedia') { @@ -405,7 +411,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' * @param {!Boolean} e true/false */ CastPlayer.prototype.onMediaStatusUpdate = function (e) { - console.debug("chromecast updating media: " + e); + console.debug('chromecast updating media: ' + e); if (e === false) { this.castPlayerState = PLAYER_STATE.IDLE; } @@ -482,7 +488,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' } else { query.Limit = query.Limit || 100; - query.ExcludeLocationTypes = "Virtual"; + query.ExcludeLocationTypes = 'Virtual'; query.EnableTotalRecordCount = false; return apiClient.getItems(userId, query); @@ -506,13 +512,13 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' instance._castPlayer = new CastPlayer(); // To allow the native android app to override - document.dispatchEvent(new CustomEvent("chromecastloaded", { + document.dispatchEvent(new CustomEvent('chromecastloaded', { detail: { player: instance } })); - events.on(instance._castPlayer, "connect", function (e) { + events.on(instance._castPlayer, 'connect', function (e) { if (currentResolve) { sendConnectionResult(true); @@ -525,22 +531,22 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' instance.lastPlayerData = null; }); - events.on(instance._castPlayer, "playbackstart", function (e, data) { + events.on(instance._castPlayer, 'playbackstart', function (e, data) { console.debug('cc: playbackstart'); instance._castPlayer.initializeCastPlayer(); var state = instance.getPlayerStateInternal(data); - events.trigger(instance, "playbackstart", [state]); + events.trigger(instance, 'playbackstart', [state]); }); - events.on(instance._castPlayer, "playbackstop", function (e, data) { + events.on(instance._castPlayer, 'playbackstop', function (e, data) { console.debug('cc: playbackstop'); var state = instance.getPlayerStateInternal(data); - events.trigger(instance, "playbackstop", [state]); + events.trigger(instance, 'playbackstop', [state]); var state = instance.lastPlayerData.PlayState || {}; var volume = state.VolumeLevel || 0.5; @@ -553,12 +559,12 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' instance.lastPlayerData.PlayState.IsMuted = mute; }); - events.on(instance._castPlayer, "playbackprogress", function (e, data) { + events.on(instance._castPlayer, 'playbackprogress', function (e, data) { console.debug('cc: positionchange'); var state = instance.getPlayerStateInternal(data); - events.trigger(instance, "timeupdate", [state]); + events.trigger(instance, 'timeupdate', [state]); }); bindEventForRelay(instance, 'timeupdate'); @@ -567,12 +573,12 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' bindEventForRelay(instance, 'volumechange'); bindEventForRelay(instance, 'repeatmodechange'); - events.on(instance._castPlayer, "playstatechange", function (e, data) { + events.on(instance._castPlayer, 'playstatechange', function (e, data) { console.debug('cc: playstatechange'); var state = instance.getPlayerStateInternal(data); - events.trigger(instance, "pause", [state]); + events.trigger(instance, 'pause', [state]); }); } @@ -630,24 +636,24 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' name: PlayerName, id: PlayerName, playerName: PlayerName, - playableMediaTypes: ["Audio", "Video"], + playableMediaTypes: ['Audio', 'Video'], isLocalPlayer: false, appName: PlayerName, deviceName: appName, supportedCommands: [ - "VolumeUp", - "VolumeDown", - "Mute", - "Unmute", - "ToggleMute", - "SetVolume", - "SetAudioStreamIndex", - "SetSubtitleStreamIndex", - "DisplayContent", - "SetRepeatMode", - "EndSession", - "PlayMediaSource", - "PlayTrailers" + 'VolumeUp', + 'VolumeDown', + 'Mute', + 'Unmute', + 'ToggleMute', + 'SetVolume', + 'SetAudioStreamIndex', + 'SetSubtitleStreamIndex', + 'DisplayContent', + 'SetRepeatMode', + 'EndSession', + 'PlayMediaSource', + 'PlayTrailers' ] }; }; @@ -667,7 +673,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' console.debug(JSON.stringify(data)); if (triggerStateChange) { - events.trigger(this, "statechange", [data]); + events.trigger(this, 'statechange', [data]); } return data; diff --git a/src/components/playback/experimentalwarnings.js b/src/plugins/experimentalWarnings/plugin.js similarity index 69% rename from src/components/playback/experimentalwarnings.js rename to src/plugins/experimentalWarnings/plugin.js index 2d1ef53c19..632e38208c 100644 --- a/src/components/playback/experimentalwarnings.js +++ b/src/plugins/experimentalWarnings/plugin.js @@ -1,5 +1,5 @@ define(['connectionManager', 'globalize', 'userSettings', 'apphost'], function (connectionManager, globalize, userSettings, appHost) { - "use strict"; + 'use strict'; function getRequirePromise(deps) { @@ -44,24 +44,15 @@ define(['connectionManager', 'globalize', 'userSettings', 'apphost'], function ( } function showBlurayMessage() { - - var message = - 'Playback of Bluray folders in this app is experimental. Some titles may not work at all. For a better experience, consider converting to mkv video files, or use an Jellyfin app with native Bluray folder support.'; - return showMessage(message, 'blurayexpirementalinfo', 'nativeblurayplayback'); + return showMessage(globalize.translate('UnsupportedPlayback'), 'blurayexpirementalinfo', 'nativeblurayplayback'); } function showDvdMessage() { - - var message = - 'Playback of Dvd folders in this app is experimental. Some titles may not work at all. For a better experience, consider converting to mkv video files, or use an Jellyfin app with native Dvd folder support.'; - return showMessage(message, 'dvdexpirementalinfo', 'nativedvdplayback'); + return showMessage(globalize.translate('UnsupportedPlayback'), 'dvdexpirementalinfo', 'nativedvdplayback'); } function showIsoMessage() { - - var message = - 'Playback of ISO files in this app is experimental. Some titles may not work at all. For a better experience, consider converting to mkv video files, or use an Jellyfin app with native ISO support.'; - return showMessage(message, 'isoexpirementalinfo', 'nativeisoplayback'); + return showMessage(globalize.translate('UnsupportedPlayback'), 'isoexpirementalinfo', 'nativeisoplayback'); } function ExpirementalPlaybackWarnings() { diff --git a/src/components/htmlaudioplayer/plugin.js b/src/plugins/htmlAudioPlayer/plugin.js similarity index 90% rename from src/components/htmlaudioplayer/plugin.js rename to src/plugins/htmlAudioPlayer/plugin.js index 8cae76bbee..8265987e28 100644 --- a/src/components/htmlaudioplayer/plugin.js +++ b/src/plugins/htmlAudioPlayer/plugin.js @@ -1,5 +1,5 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelper'], function (events, browser, require, appHost, appSettings, htmlMediaHelper) { - "use strict"; + 'use strict'; function getDefaultProfile() { return new Promise(function (resolve, reject) { @@ -101,7 +101,7 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp self._timeUpdated = false; self._currentTime = null; - var elem = createMediaElement(options); + var elem = createMediaElement(); return setCurrentSrc(elem, options); }; @@ -136,7 +136,10 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp requireHlsPlayer(function () { var hls = new Hls({ - manifestLoadingTimeOut: 20000 + manifestLoadingTimeOut: 20000, + xhrSetup: function(xhr, url) { + xhr.withCredentials = true; + } //appendErrorMaxRetry: 6, //debug: true }); @@ -155,6 +158,9 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp elem.autoplay = true; + // Safari will not send cookies without this + elem.crossOrigin = 'use-credentials'; + return htmlMediaHelper.applySrc(elem, val, options).then(function () { self._currentSrc = val; @@ -171,6 +177,7 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp elem.addEventListener('pause', onPause); elem.addEventListener('playing', onPlaying); elem.addEventListener('play', onPlay); + elem.addEventListener('waiting', onWaiting); } function unBindEvents(elem) { @@ -180,6 +187,7 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp elem.removeEventListener('pause', onPause); elem.removeEventListener('playing', onPlaying); elem.removeEventListener('play', onPlay); + elem.removeEventListener('waiting', onWaiting); } self.stop = function (destroyPlayer) { @@ -294,6 +302,10 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp events.trigger(self, 'pause'); } + function onWaiting() { + events.trigger(self, 'waiting'); + } + function onError() { var errorCode = this.error ? (this.error.code || 0) : 0; @@ -450,6 +462,21 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp return false; }; + HtmlAudioPlayer.prototype.setPlaybackRate = function (value) { + var mediaElement = this._mediaElement; + if (mediaElement) { + mediaElement.playbackRate = value; + } + }; + + HtmlAudioPlayer.prototype.getPlaybackRate = function () { + var mediaElement = this._mediaElement; + if (mediaElement) { + return mediaElement.playbackRate; + } + return null; + }; + HtmlAudioPlayer.prototype.setVolume = function (val) { var mediaElement = this._mediaElement; if (mediaElement) { @@ -493,5 +520,26 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp }; + var supportedFeatures; + + function getSupportedFeatures() { + var list = []; + var audio = document.createElement('audio'); + + if (typeof audio.playbackRate === 'number') { + list.push('PlaybackRate'); + } + + return list; + } + + HtmlAudioPlayer.prototype.supports = function (feature) { + if (!supportedFeatures) { + supportedFeatures = getSupportedFeatures(); + } + + return supportedFeatures.indexOf(feature) !== -1; + }; + return HtmlAudioPlayer; }); diff --git a/src/components/htmlvideoplayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js similarity index 92% rename from src/components/htmlvideoplayer/plugin.js rename to src/plugins/htmlVideoPlayer/plugin.js index a153d9a147..f8ba7c415c 100644 --- a/src/components/htmlvideoplayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1,5 +1,5 @@ -define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackManager', 'appRouter', 'appSettings', 'connectionManager', 'htmlMediaHelper', 'itemHelper', 'fullscreenManager', 'globalize'], function (browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager, htmlMediaHelper, itemHelper, fullscreenManager, globalize) { - "use strict"; +define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackManager', 'appRouter', 'appSettings', 'connectionManager', 'htmlMediaHelper', 'itemHelper', 'screenfull', 'globalize'], function (browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager, htmlMediaHelper, itemHelper, screenfull, globalize) { + 'use strict'; /* globals cast */ var mediaManager; @@ -106,18 +106,27 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa }); } + function hidePrePlaybackPage() { + let animatedPage = document.querySelector('.page:not(.hide)'); + animatedPage.classList.add('hide'); + // At this point, we must hide the scrollbar placeholder, so it's not being displayed while the item is being loaded + document.body.classList.remove('force-scroll'); + } + function zoomIn(elem) { return new Promise(function (resolve, reject) { var duration = 240; elem.style.animation = 'htmlvideoplayer-zoomin ' + duration + 'ms ease-in normal'; + hidePrePlaybackPage(); dom.addEventListener(elem, dom.whichAnimationEvent(), resolve, { once: true }); }); } - function normalizeTrackEventText(text) { - return text.replace(/\\N/gi, '\n'); + function normalizeTrackEventText(text, useHtml) { + var result = text.replace(/\\N/gi, '\n').replace(/\r/gi, ''); + return useHtml ? result.replace(/\n/gi, '
') : result; } function setTracks(elem, tracks, item, mediaSource) { @@ -211,7 +220,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa function incrementFetchQueue() { if (self._fetchQueue <= 0) { self.isFetching = true; - events.trigger(self, "beginFetch"); + events.trigger(self, 'beginFetch'); } self._fetchQueue++; @@ -222,7 +231,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa if (self._fetchQueue <= 0) { self.isFetching = false; - events.trigger(self, "endFetch"); + events.trigger(self, 'endFetch'); } } @@ -272,14 +281,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa } self.play = function (options) { - - if (browser.msie) { - if (options.playMethod === 'Transcode' && !window.MediaSource) { - alert('Playback of this content is not supported in Internet Explorer. For a better experience, try a modern browser such as Microsoft Edge, Google Chrome, Firefox or Opera.'); - return Promise.reject(); - } - } - self._started = false; self._timeUpdated = false; @@ -289,7 +290,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa return createMediaElement(options).then(function (elem) { - return updateVideoUrl(options, options.mediaSource).then(function () { + return updateVideoUrl(options).then(function () { return setCurrentSrc(elem, options); }); }); @@ -329,7 +330,10 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa requireHlsPlayer(function () { var hls = new Hls({ - manifestLoadingTimeOut: 20000 + manifestLoadingTimeOut: 20000, + xhrSetup: function(xhr, xhr_url) { + xhr.withCredentials = true; + } //appendErrorMaxRetry: 6, //debug: true }); @@ -465,7 +469,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa console.debug('content type: ' + contentType); host.onError = function (errorCode) { - console.error("fatal Error - " + errorCode); + console.error('fatal Error - ' + errorCode); }; mediaElement.autoplay = false; @@ -550,6 +554,9 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa elem.autoplay = true; + // Safari will not send cookies without this + elem.crossOrigin = 'use-credentials'; + return htmlMediaHelper.applySrc(elem, val, options).then(function () { self._currentSrc = val; @@ -599,8 +606,9 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa var offsetValue = parseFloat(offset); // if .ass currently rendering - if (currentAssRenderer) { + if (currentSubtitlesOctopus) { updateCurrentTrackOffset(offsetValue); + currentSubtitlesOctopus.timeOffset = (self._currentPlayOptions.transcodingOffsetTicks || 0) / 10000000 + offsetValue; } else { var trackElement = getTextTrack(); // if .vtt currently rendering @@ -609,7 +617,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa } else if (currentTrackEvents) { setTrackEventsSubtitleOffset(currentTrackEvents, offsetValue); } else { - console.debug("No available track, cannot apply offset: ", offsetValue); + console.debug('No available track, cannot apply offset: ', offsetValue); } } }; @@ -783,6 +791,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa videoElement.removeEventListener('play', onPlay); videoElement.removeEventListener('click', onClick); videoElement.removeEventListener('dblclick', onDblClick); + videoElement.removeEventListener('waiting', onWaiting); videoElement.parentNode.removeChild(videoElement); } @@ -793,7 +802,9 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa dlg.parentNode.removeChild(dlg); } - fullscreenManager.exitFullscreen(); + if (screenfull.isEnabled) { + screenfull.exit(); + } }; function onEnded() { @@ -832,7 +843,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa function onNavigatedToOsd() { var dlg = videoDialog; if (dlg) { - dlg.classList.remove('videoPlayerContainer-withBackdrop'); dlg.classList.remove('videoPlayerContainer-onTop'); onStartedAndNavigatedToOsd(); @@ -855,7 +865,13 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa loading.hide(); - htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks); + htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks, function () { + if (currentSubtitlesOctopus) { + currentSubtitlesOctopus.timeOffset = (self._currentPlayOptions.transcodingOffsetTicks || 0) / 10000000 + currentTrackOffset; + currentSubtitlesOctopus.resize(); + currentSubtitlesOctopus.resetRenderAheadCache(false); + } + }); if (self._currentPlayOptions.fullscreen) { @@ -863,7 +879,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa } else { appRouter.setTransparency('backdrop'); - videoDialog.classList.remove('videoPlayerContainer-withBackdrop'); videoDialog.classList.remove('videoPlayerContainer-onTop'); onStartedAndNavigatedToOsd(); @@ -905,6 +920,10 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa events.trigger(self, 'pause'); } + function onWaiting() { + events.trigger(self, 'waiting'); + } + function onError() { var errorCode = this.error ? (this.error.code || 0) : 0; var errorMessage = this.error ? (this.error.message || '') : ''; @@ -1055,11 +1074,12 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa fonts: attachments.map(function (i) { return apiClient.getUrl(i.DeliveryUrl); }), - workerUrl: appRouter.baseUrl() + "/libraries/subtitles-octopus-worker.js", - legacyWorkerUrl: appRouter.baseUrl() + "/libraries/subtitles-octopus-worker-legacy.js", + workerUrl: appRouter.baseUrl() + '/libraries/subtitles-octopus-worker.js', + legacyWorkerUrl: appRouter.baseUrl() + '/libraries/subtitles-octopus-worker-legacy.js', onError: function() { htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror'); }, + timeOffset: (self._currentPlayOptions.transcodingOffsetTicks || 0) / 10000000, // new octopus options; override all, even defaults renderMode: 'blend', @@ -1209,7 +1229,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa data.TrackEvents.forEach(function (trackEvent) { var trackCueObject = window.VTTCue || window.TextTrackCue; - var cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text)); + var cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text, false)); trackElement.addCue(cue); }); @@ -1219,11 +1239,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa function updateSubtitleText(timeMs) { - // handle offset for ass tracks - if (currentTrackOffset) { - timeMs += (currentTrackOffset * 1000); - } - var clock = currentClock; if (clock) { try { @@ -1250,8 +1265,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa } if (selectedTrackEvent && selectedTrackEvent.Text) { - - subtitleTextElement.innerHTML = normalizeTrackEventText(selectedTrackEvent.Text); + subtitleTextElement.innerHTML = normalizeTrackEventText(selectedTrackEvent.Text, true); subtitleTextElement.classList.remove('hide'); } else { @@ -1285,12 +1299,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa function createMediaElement(options) { - if (browser.tv || browser.iOS || browser.mobile) { - // too slow - // also on iOS, the backdrop image doesn't look right - // on android mobile, it works, but can be slow to have the video surface fully cover the backdrop - options.backdropUrl = null; - } return new Promise(function (resolve, reject) { var dlg = document.querySelector('.videoPlayerContainer'); @@ -1305,11 +1313,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa dlg.classList.add('videoPlayerContainer'); - if (options.backdropUrl) { - dlg.classList.add('videoPlayerContainer-withBackdrop'); - dlg.style.backgroundImage = "url('" + options.backdropUrl + "')"; - } - if (options.fullscreen) { dlg.classList.add('videoPlayerContainer-onTop'); } @@ -1343,6 +1346,10 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa videoElement.addEventListener('play', onPlay); videoElement.addEventListener('click', onClick); videoElement.addEventListener('dblclick', onDblClick); + videoElement.addEventListener('waiting', onWaiting); + if (options.backdropUrl) { + videoElement.poster = options.backdropUrl; + } document.body.insertBefore(dlg, document.body.firstChild); videoDialog = dlg; @@ -1362,15 +1369,11 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa resolve(videoElement); }); } else { + hidePrePlaybackPage(); resolve(videoElement); } }); } else { - if (options.backdropUrl) { - dlg.classList.add('videoPlayerContainer-withBackdrop'); - dlg.style.backgroundImage = "url('" + options.backdropUrl + "')"; - } - resolve(dlg.querySelector('video')); } }); @@ -1412,15 +1415,8 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa var list = []; var video = document.createElement('video'); - if (video.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === "function" || document.pictureInPictureEnabled) { + if (video.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === 'function' || document.pictureInPictureEnabled) { list.push('PictureInPicture'); - } else if (browser.ipad) { - // Unfortunately this creates a false positive on devices where its' not actually supported - if (navigator.userAgent.toLowerCase().indexOf('os 9') === -1) { - if (video.webkitSupportsPresentationMode && video.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === "function") { - list.push('PictureInPicture'); - } - } } else if (window.Windows) { if (Windows.UI.ViewManagement.ApplicationView.getForCurrentView().isViewModeSupported(Windows.UI.ViewManagement.ApplicationViewMode.compactOverlay)) { list.push('PictureInPicture'); @@ -1431,8 +1427,12 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa list.push('AirPlay'); } + if (typeof video.playbackRate === 'number') { + list.push('PlaybackRate'); + } + list.push('SetBrightness'); - list.push("SetAspectRatio"); + list.push('SetAspectRatio'); return list; } @@ -1517,8 +1517,8 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa Windows.UI.ViewManagement.ApplicationView.getForCurrentView().tryEnterViewModeAsync(Windows.UI.ViewManagement.ApplicationViewMode.default); } } else { - if (video && video.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === "function") { - video.webkitSetPresentationMode(isEnabled ? "picture-in-picture" : "inline"); + if (video && video.webkitSupportsPresentationMode && typeof video.webkitSetPresentationMode === 'function') { + video.webkitSetPresentationMode(isEnabled ? 'picture-in-picture' : 'inline'); } } }; @@ -1532,7 +1532,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa } else { var video = this._mediaElement; if (video) { - return video.webkitPresentationMode === "picture-in-picture"; + return video.webkitPresentationMode === 'picture-in-picture'; } } @@ -1555,11 +1555,11 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa if (video) { if (isEnabled) { video.requestAirPlay().catch(function(err) { - console.error("Error requesting AirPlay", err); + console.error('Error requesting AirPlay', err); }); } else { document.exitAirPLay().catch(function(err) { - console.error("Error exiting AirPlay", err); + console.error('Error exiting AirPlay', err); }); } } @@ -1651,6 +1651,21 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa return false; }; + HtmlVideoPlayer.prototype.setPlaybackRate = function (value) { + var mediaElement = this._mediaElement; + if (mediaElement) { + mediaElement.playbackRate = value; + } + }; + + HtmlVideoPlayer.prototype.getPlaybackRate = function () { + var mediaElement = this._mediaElement; + if (mediaElement) { + return mediaElement.playbackRate; + } + return null; + }; + HtmlVideoPlayer.prototype.setVolume = function (val) { var mediaElement = this._mediaElement; if (mediaElement) { @@ -1691,29 +1706,29 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa HtmlVideoPlayer.prototype.setAspectRatio = function (val) { var mediaElement = this._mediaElement; if (mediaElement) { - if ("auto" === val) { - mediaElement.style.removeProperty("object-fit"); + if ('auto' === val) { + mediaElement.style.removeProperty('object-fit'); } else { - mediaElement.style["object-fit"] = val; + mediaElement.style['object-fit'] = val; } } this._currentAspectRatio = val; }; HtmlVideoPlayer.prototype.getAspectRatio = function () { - return this._currentAspectRatio || "auto"; + return this._currentAspectRatio || 'auto'; }; HtmlVideoPlayer.prototype.getSupportedAspectRatios = function () { return [{ - name: "Auto", - id: "auto" + name: 'Auto', + id: 'auto' }, { - name: "Cover", - id: "cover" + name: 'Cover', + id: 'cover' }, { - name: "Fill", - id: "fill" + name: 'Fill', + id: 'fill' }]; }; @@ -1762,7 +1777,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa if (protocol) { mediaCategory.stats.push({ - label: globalize.translate("LabelProtocol"), + label: globalize.translate('LabelProtocol'), value: protocol }); } @@ -1772,12 +1787,12 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa if (this._hlsPlayer || this._shakaPlayer) { mediaCategory.stats.push({ - label: globalize.translate("LabelStreamType"), + label: globalize.translate('LabelStreamType'), value: 'HLS' }); } else { mediaCategory.stats.push({ - label: globalize.translate("LabelStreamType"), + label: globalize.translate('LabelStreamType'), value: 'Video' }); } @@ -1795,7 +1810,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa // Don't show player dimensions on smart TVs because the app UI could be lower resolution than the video and this causes users to think there is a problem if (width && height && !browser.tv) { videoCategory.stats.push({ - label: globalize.translate("LabelPlayerDimensions"), + label: globalize.translate('LabelPlayerDimensions'), value: width + 'x' + height }); } @@ -1805,7 +1820,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa if (width && height) { videoCategory.stats.push({ - label: globalize.translate("LabelVideoResolution"), + label: globalize.translate('LabelVideoResolution'), value: width + 'x' + height }); } @@ -1815,13 +1830,13 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa var droppedVideoFrames = playbackQuality.droppedVideoFrames || 0; videoCategory.stats.push({ - label: globalize.translate("LabelDroppedFrames"), + label: globalize.translate('LabelDroppedFrames'), value: droppedVideoFrames }); var corruptedVideoFrames = playbackQuality.corruptedVideoFrames || 0; videoCategory.stats.push({ - label: globalize.translate("LabelCorruptedFrames"), + label: globalize.translate('LabelCorruptedFrames'), value: corruptedVideoFrames }); } diff --git a/src/components/htmlvideoplayer/style.css b/src/plugins/htmlVideoPlayer/style.css similarity index 82% rename from src/components/htmlvideoplayer/style.css rename to src/plugins/htmlVideoPlayer/style.css index 5ecf4af66a..b83a7816f5 100644 --- a/src/components/htmlvideoplayer/style.css +++ b/src/plugins/htmlVideoPlayer/style.css @@ -6,20 +6,9 @@ right: 0; display: flex; align-items: center; -} - -.videoPlayerContainer:not(.videoPlayerContainer-withBackdrop) { background: #000 !important; } -.videoPlayerContainer-withBackdrop { - background-repeat: no-repeat; - background-position: center center; - background-size: cover; - background-attachment: fixed; - background-color: #000; -} - .videoPlayerContainer-onTop { z-index: 1000; } diff --git a/src/plugins/logoScreensaver/plugin.js b/src/plugins/logoScreensaver/plugin.js new file mode 100644 index 0000000000..f9dd901982 --- /dev/null +++ b/src/plugins/logoScreensaver/plugin.js @@ -0,0 +1,192 @@ +define(['pluginManager'], function (pluginManager) { + + return function () { + + var self = this; + + self.name = 'Logo ScreenSaver'; + self.type = 'screensaver'; + self.id = 'logoscreensaver'; + self.supportsAnonymous = true; + + var interval; + + function animate() { + + var animations = [ + + bounceInLeft, + bounceInRight, + swing, + tada, + wobble, + rotateIn, + rotateOut + ]; + + var elem = document.querySelector('.logoScreenSaverImage'); + + if (elem && elem.animate) { + var random = getRandomInt(0, animations.length - 1); + + animations[random](elem, 1); + } + } + + function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + function bounceInLeft(elem, iterations) { + var keyframes = [ + { transform: 'translate3d(-3000px, 0, 0)', opacity: '0', offset: 0 }, + { transform: 'translate3d(25px, 0, 0)', opacity: '1', offset: 0.6 }, + { transform: 'translate3d(-100px, 0, 0)', offset: 0.75 }, + { transform: 'translate3d(5px, 0, 0)', offset: 0.9 }, + { transform: 'none', opacity: '1', offset: 1 }]; + var timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' }; + return elem.animate(keyframes, timing); + } + + function bounceInRight(elem, iterations) { + var keyframes = [ + { transform: 'translate3d(3000px, 0, 0)', opacity: '0', offset: 0 }, + { transform: 'translate3d(-25px, 0, 0)', opacity: '1', offset: 0.6 }, + { transform: 'translate3d(100px, 0, 0)', offset: 0.75 }, + { transform: 'translate3d(-5px, 0, 0)', offset: 0.9 }, + { transform: 'none', opacity: '1', offset: 1 }]; + var timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' }; + return elem.animate(keyframes, timing); + } + + function shake(elem, iterations) { + var keyframes = [ + { transform: 'translate3d(0, 0, 0)', offset: 0 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.1 }, + { transform: 'translate3d(10px, 0, 0)', offset: 0.2 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.3 }, + { transform: 'translate3d(10px, 0, 0)', offset: 0.4 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.5 }, + { transform: 'translate3d(10px, 0, 0)', offset: 0.6 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.7 }, + { transform: 'translate3d(10px, 0, 0)', offset: 0.8 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.9 }, + { transform: 'translate3d(0, 0, 0)', offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function swing(elem, iterations) { + var keyframes = [ + { transform: 'translate(0%)', offset: 0 }, + { transform: 'rotate3d(0, 0, 1, 15deg)', offset: 0.2 }, + { transform: 'rotate3d(0, 0, 1, -10deg)', offset: 0.4 }, + { transform: 'rotate3d(0, 0, 1, 5deg)', offset: 0.6 }, + { transform: 'rotate3d(0, 0, 1, -5deg)', offset: 0.8 }, + { transform: 'rotate3d(0, 0, 1, 0deg)', offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function tada(elem, iterations) { + var keyframes = [ + { transform: 'scale3d(1, 1, 1)', offset: 0 }, + { transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.1 }, + { transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.2 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.3 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.4 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.5 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.6 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.7 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.8 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.9 }, + { transform: 'scale3d(1, 1, 1)', offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function wobble(elem, iterations) { + var keyframes = [ + { transform: 'translate(0%)', offset: 0 }, + { transform: 'translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)', offset: 0.15 }, + { transform: 'translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)', offset: 0.45 }, + { transform: 'translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)', offset: 0.6 }, + { transform: 'translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)', offset: 0.75 }, + { transform: 'translateX(0%)', offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function rotateIn(elem, iterations) { + var transformOrigin = elem.style['transform-origin']; + var keyframes = [{ transform: 'rotate3d(0, 0, 1, -200deg)', opacity: '0', transformOrigin: 'center', offset: 0 }, + { transform: 'none', opacity: '1', transformOrigin: 'center', offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function rotateOut(elem, iterations) { + var transformOrigin = elem.style['transform-origin']; + var keyframes = [{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 0 }, + { transform: 'rotate3d(0, 0, 1, 200deg)', opacity: '0', transformOrigin: 'center', offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + + } + + function fadeOut(elem, iterations) { + var keyframes = [ + { opacity: '1', offset: 0 }, + { opacity: '0', offset: 1 }]; + var timing = { duration: 400, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function stopInterval() { + if (interval) { + clearInterval(interval); + interval = null; + } + } + + self.show = function () { + + require(['css!' + pluginManager.mapPath(self, 'style.css')], function () { + + var elem = document.querySelector('.logoScreenSaver'); + + if (!elem) { + elem = document.createElement('div'); + elem.classList.add('logoScreenSaver'); + document.body.appendChild(elem); + + elem.innerHTML = ''; + } + + stopInterval(); + interval = setInterval(animate, 3000); + }); + }; + + self.hide = function () { + + stopInterval(); + + var elem = document.querySelector('.logoScreenSaver'); + + if (elem) { + + var onAnimationFinish = function () { + elem.parentNode.removeChild(elem); + }; + + if (elem.animate) { + var animation = fadeOut(elem, 1); + animation.onfinish = onAnimationFinish; + } else { + onAnimationFinish(); + } + } + }; + }; +}); diff --git a/src/components/logoscreensaver/style.css b/src/plugins/logoScreensaver/style.css similarity index 100% rename from src/components/logoscreensaver/style.css rename to src/plugins/logoScreensaver/style.css diff --git a/src/plugins/photoPlayer/plugin.js b/src/plugins/photoPlayer/plugin.js new file mode 100644 index 0000000000..d8d4ee70ef --- /dev/null +++ b/src/plugins/photoPlayer/plugin.js @@ -0,0 +1,45 @@ +import connectionManager from 'connectionManager'; + +export default class PhotoPlayer { + constructor() { + this.name = 'Photo Player'; + this.type = 'mediaplayer'; + this.id = 'photoplayer'; + this.priority = 1; + } + + play(options) { + + return new Promise(function (resolve, reject) { + + import('slideshow').then(({default: slideshow}) => { + + var index = options.startIndex || 0; + + var apiClient = connectionManager.currentApiClient(); + + apiClient.getCurrentUser().then(function(result) { + + var newSlideShow = new slideshow({ + showTitle: false, + cover: false, + items: options.items, + startIndex: index, + interval: 11000, + interactive: true, + user: result + }); + + newSlideShow.show(); + + resolve(); + }); + }); + }); + } + + canPlayMediaType(mediaType) { + + return (mediaType || '').toLowerCase() === 'photo'; + } +} diff --git a/src/components/playback/playaccessvalidation.js b/src/plugins/playAccessValidation/plugin.js similarity index 98% rename from src/components/playback/playaccessvalidation.js rename to src/plugins/playAccessValidation/plugin.js index 46a6f7546c..5148d2b821 100644 --- a/src/components/playback/playaccessvalidation.js +++ b/src/plugins/playAccessValidation/plugin.js @@ -1,5 +1,5 @@ define(['connectionManager', 'globalize'], function (connectionManager, globalize) { - "use strict"; + 'use strict'; function getRequirePromise(deps) { return new Promise(function (resolve, reject) { diff --git a/src/components/sessionplayer.js b/src/plugins/sessionPlayer/plugin.js similarity index 99% rename from src/components/sessionplayer.js rename to src/plugins/sessionPlayer/plugin.js index bbb22a9654..489b57493d 100644 --- a/src/components/sessionplayer.js +++ b/src/plugins/sessionPlayer/plugin.js @@ -79,7 +79,7 @@ define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'] instance.isUpdating = true; var apiClient = getCurrentApiClient(instance); - apiClient.sendMessage("SessionsStop"); + apiClient.sendMessage('SessionsStop'); if (instance.pollInterval) { clearInterval(instance.pollInterval); instance.pollInterval = null; @@ -158,7 +158,7 @@ define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'] instance.isUpdating = true; var apiClient = getCurrentApiClient(instance); - apiClient.sendMessage("SessionsStart", "100,800"); + apiClient.sendMessage('SessionsStart', '100,800'); if (instance.pollInterval) { clearInterval(instance.pollInterval); instance.pollInterval = null; diff --git a/src/components/youtubeplayer/plugin.js b/src/plugins/youtubePlayer/plugin.js similarity index 99% rename from src/components/youtubeplayer/plugin.js rename to src/plugins/youtubePlayer/plugin.js index fd9c05292e..5b5e1b1a3c 100644 --- a/src/components/youtubeplayer/plugin.js +++ b/src/plugins/youtubePlayer/plugin.js @@ -1,5 +1,5 @@ define(['require', 'events', 'browser', 'appRouter', 'loading'], function (require, events, browser, appRouter, loading) { - "use strict"; + 'use strict'; /* globals YT */ function zoomIn(elem, iterations) { @@ -182,7 +182,7 @@ define(['require', 'events', 'browser', 'appRouter', 'loading'], function (requi if (!window.YT) { var tag = document.createElement('script'); - tag.src = "https://www.youtube.com/iframe_api"; + tag.src = 'https://www.youtube.com/iframe_api'; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); } else { diff --git a/src/components/youtubeplayer/style.css b/src/plugins/youtubePlayer/style.css similarity index 100% rename from src/components/youtubeplayer/style.css rename to src/plugins/youtubePlayer/style.css diff --git a/src/scheduledtask.html b/src/scheduledtask.html index f5bd6550f2..c7e82594ce 100644 --- a/src/scheduledtask.html +++ b/src/scheduledtask.html @@ -4,7 +4,7 @@ @@ -13,14 +13,14 @@

${HeaderTaskTriggers}

-
+

${HeaderAddScheduledTaskTrigger}

diff --git a/src/components/alphanumericshortcuts/alphanumericshortcuts.js b/src/scripts/alphanumericshortcuts.js similarity index 100% rename from src/components/alphanumericshortcuts/alphanumericshortcuts.js rename to src/scripts/alphanumericshortcuts.js diff --git a/src/scripts/apploader.js b/src/scripts/apploader.js index e8a63217a8..0353c9535c 100644 --- a/src/scripts/apploader.js +++ b/src/scripts/apploader.js @@ -1,14 +1,14 @@ (function() { - "use strict"; + 'use strict'; function injectScriptElement(src, onload) { if (!src) { return; } - var script = document.createElement("script"); + var script = document.createElement('script'); if (self.dashboardVersion) { - src += "?v=" + self.dashboardVersion; + src += `?v=${self.dashboardVersion}`; } script.src = src; @@ -21,18 +21,28 @@ function loadSite() { injectScriptElement( - "./libraries/alameda.js", + './libraries/alameda.js', function() { // onload of require library - injectScriptElement("./scripts/site.js"); + injectScriptElement('./scripts/site.js'); } ); } + try { + Promise.resolve(); + } catch (ex) { + // this checks for several cases actually, typical is + // Promise() being missing on some legacy browser, and a funky one + // is Promise() present but buggy on WebOS 2 + window.Promise = undefined; + self.Promise = undefined; + } + if (!self.Promise) { // Load Promise polyfill if they are not natively supported injectScriptElement( - "./libraries/npo.js", + './libraries/npo.js', loadSite ); } else { diff --git a/src/scripts/autobackdrops.js b/src/scripts/autoBackdrops.js similarity index 71% rename from src/scripts/autobackdrops.js rename to src/scripts/autoBackdrops.js index 09f89fad6f..abd86c9dbf 100644 --- a/src/scripts/autobackdrops.js +++ b/src/scripts/autoBackdrops.js @@ -1,26 +1,28 @@ -define(["backdrop", "userSettings", "libraryMenu"], function (backdrop, userSettings, libraryMenu) { - "use strict"; +define(['backdrop', 'userSettings', 'libraryMenu'], function (backdrop, userSettings, libraryMenu) { + 'use strict'; + + var cache = {}; function enabled() { return userSettings.enableBackdrops(); } function getBackdropItemIds(apiClient, userId, types, parentId) { - var key = "backdrops2_" + userId + (types || "") + (parentId || ""); + var key = `backdrops2_${userId + (types || '') + (parentId || '')}`; var data = cache[key]; if (data) { - console.debug("Found backdrop id list in cache. Key: " + key); + console.debug(`Found backdrop id list in cache. Key: ${key}`); data = JSON.parse(data); return Promise.resolve(data); } var options = { - SortBy: "IsFavoriteOrLiked,Random", + SortBy: 'IsFavoriteOrLiked,Random', Limit: 20, Recursive: true, IncludeItemTypes: types, - ImageTypes: "Backdrop", + ImageTypes: 'Backdrop', ParentId: parentId, EnableTotalRecordCount: false }; @@ -54,18 +56,17 @@ define(["backdrop", "userSettings", "libraryMenu"], function (backdrop, userSett } } - var cache = {}; - pageClassOn("pageshow", "page", function () { + pageClassOn('pageshow', 'page', function () { var page = this; - if (!page.classList.contains("selfBackdropPage")) { - if (page.classList.contains("backdropPage")) { + if (!page.classList.contains('selfBackdropPage')) { + if (page.classList.contains('backdropPage')) { if (enabled()) { - var type = page.getAttribute("data-backdroptype"); - var parentId = page.classList.contains("globalBackdropPage") ? "" : libraryMenu.getTopParentId(); + var type = page.getAttribute('data-backdroptype'); + var parentId = page.classList.contains('globalBackdropPage') ? '' : libraryMenu.getTopParentId(); showBackdrop(type, parentId); } else { - page.classList.remove("backdropPage"); + page.classList.remove('backdropPage'); backdrop.clear(); } } else { diff --git a/src/scripts/browser.js b/src/scripts/browser.js index 19153bb19f..a377f08fdb 100644 --- a/src/scripts/browser.js +++ b/src/scripts/browser.js @@ -14,15 +14,11 @@ define([], function () { return true; } - if (userAgent.indexOf('nintendo') !== -1) { - return true; - } - if (userAgent.indexOf('viera') !== -1) { return true; } - if (userAgent.indexOf('webos') !== -1) { + if (userAgent.indexOf('web0s') !== -1) { return true; } @@ -53,45 +49,6 @@ define([], function () { return false; } - function isStyleSupported(prop, value) { - - if (typeof window === 'undefined') { - return false; - } - - // If no value is supplied, use "inherit" - value = arguments.length === 2 ? value : 'inherit'; - // Try the native standard method first - if ('CSS' in window && 'supports' in window.CSS) { - return window.CSS.supports(prop, value); - } - // Check Opera's native method - if ('supportsCSS' in window) { - return window.supportsCSS(prop, value); - } - - // need try/catch because it's failing on tizen - - try { - // Convert to camel-case for DOM interactions - var camel = prop.replace(/-([a-z]|[0-9])/ig, function (all, letter) { - return (letter + '').toUpperCase(); - }); - // Check if the property is supported - var support = (camel in el.style); - // Create test element - var el = document.createElement('div'); - // Assign the property and value to invoke - // the CSS interpreter - el.style.cssText = prop + ':' + value; - // Ensure both the property and value are - // supported and return - return support && (el.style[camel] !== ''); - } catch (err) { - return false; - } - } - function hasKeyboard(browser) { if (browser.touch) { @@ -132,7 +89,7 @@ define([], function () { var _supportsCssAnimation; var _supportsCssAnimationWithPrefix; function supportsCssAnimation(allowPrefix) { - + // TODO: Assess if this is still needed, as all of our targets should natively support CSS animations. if (allowPrefix) { if (_supportsCssAnimationWithPrefix === true || _supportsCssAnimationWithPrefix === false) { return _supportsCssAnimationWithPrefix; @@ -184,8 +141,7 @@ define([], function () { /(chrome)[ \/]([\w.]+)/.exec(ua) || /(safari)[ \/]([\w.]+)/.exec(ua) || /(firefox)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || []; var versionMatch = /(version)[ \/]([\w.]+)/.exec(ua); @@ -196,18 +152,10 @@ define([], function () { /(android)/.exec(ua) || []; - var browser = match[1] || ""; + var browser = match[1] || ''; - if (browser === "edge") { - platform_match = [""]; - } else { - if (ua.indexOf("windows phone") !== -1 || ua.indexOf("iemobile") !== -1) { - - // http://www.neowin.net/news/ie11-fakes-user-agent-to-fool-gmail-in-windows-phone-81-gdr1-update - browser = "msie"; - } else if (ua.indexOf("like gecko") !== -1 && ua.indexOf('webkit') === -1 && ua.indexOf('opera') === -1 && ua.indexOf('chrome') === -1 && ua.indexOf('safari') === -1) { - browser = "msie"; - } + if (browser === 'edge') { + platform_match = ['']; } if (browser === 'opr') { @@ -219,7 +167,7 @@ define([], function () { version = versionMatch[2]; } - version = version || match[2] || "0"; + version = version || match[2] || '0'; var versionMajor = parseInt(version.split('.')[0]); @@ -230,7 +178,7 @@ define([], function () { return { browser: browser, version: version, - platform: platform_match[0] || "", + platform: platform_match[0] || '', versionMajor: versionMajor }; }; @@ -250,11 +198,11 @@ define([], function () { browser[matched.platform] = true; } - if (!browser.chrome && !browser.msie && !browser.edge && !browser.opera && userAgent.toLowerCase().indexOf("webkit") !== -1) { + if (!browser.chrome && !browser.edge && !browser.opera && userAgent.toLowerCase().indexOf('webkit') !== -1) { browser.safari = true; } - if (userAgent.toLowerCase().indexOf("playstation 4") !== -1) { + if (userAgent.toLowerCase().indexOf('playstation 4') !== -1) { browser.ps4 = true; browser.tv = true; } @@ -263,7 +211,10 @@ define([], function () { browser.mobile = true; } - browser.xboxOne = userAgent.toLowerCase().indexOf('xbox') !== -1; + if (userAgent.toLowerCase().indexOf('xbox') !== -1) { + browser.xboxOne = true; + browser.tv = true; + } browser.animate = typeof document !== 'undefined' && document.documentElement.animate != null; browser.tizen = userAgent.toLowerCase().indexOf('tizen') !== -1 || self.tizen != null; browser.web0s = userAgent.toLowerCase().indexOf('Web0S'.toLowerCase()) !== -1; @@ -283,15 +234,12 @@ define([], function () { browser.tv = isTv(); browser.operaTv = browser.tv && userAgent.toLowerCase().indexOf('opr/') !== -1; - if (!isStyleSupported('display', 'flex')) { - browser.noFlex = true; - } - if (browser.mobile || browser.tv) { browser.slow = true; } if (typeof document !== 'undefined') { + /* eslint-disable-next-line compat/compat */ if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0)) { browser.touch = true; } diff --git a/src/scripts/browserdeviceprofile.js b/src/scripts/browserDeviceProfile.js similarity index 97% rename from src/scripts/browserdeviceprofile.js rename to src/scripts/browserDeviceProfile.js index 570c0d2fa1..a550b69198 100644 --- a/src/scripts/browserdeviceprofile.js +++ b/src/scripts/browserDeviceProfile.js @@ -188,20 +188,6 @@ define(['browser'], function (browser) { 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; @@ -230,9 +216,6 @@ define(['browser'], function (browser) { 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': @@ -311,9 +294,9 @@ define(['browser'], function (browser) { try { var isTizenUhd = webapis.productinfo.isUdPanelSupported(); isTizenFhd = !isTizenUhd; - console.debug("isTizenFhd = " + isTizenFhd); + console.debug('isTizenFhd = ' + isTizenFhd); } catch (error) { - console.error("isUdPanelSupported() error code = " + error.code); + console.error('isUdPanelSupported() error code = ' + error.code); } } @@ -334,6 +317,7 @@ define(['browser'], function (browser) { var canPlayVp8 = videoTestElement.canPlayType('video/webm; codecs="vp8"').replace(/no/, ''); var canPlayVp9 = videoTestElement.canPlayType('video/webm; codecs="vp9"').replace(/no/, ''); + var canPlayAv1 = videoTestElement.canPlayType('video/webm; codecs="av1"').replace(/no/, ''); var webmAudioCodecs = ['vorbis']; var canPlayMkv = testCanPlayMkv(videoTestElement); @@ -592,6 +576,15 @@ define(['browser'], function (browser) { }); } + if (canPlayAv1) { + profile.DirectPlayProfiles.push({ + Container: 'webm', + Type: 'Video', + AudioCodec: webmAudioCodecs.join(','), + VideoCodec: 'AV1' + }); + } + profile.TranscodingProfiles = []; var hlsBreakOnNonKeyFrames = browser.iOS || browser.osx || browser.edge || !canPlayNativeHls() ? true : false; @@ -887,6 +880,16 @@ define(['browser'], function (browser) { Method: 'External' }); } + if (options.enableSsaRender) { + profile.SubtitleProfiles.push({ + Format: 'ass', + Method: 'External' + }); + profile.SubtitleProfiles.push({ + Format: 'ssa', + Method: 'External' + }); + } profile.ResponseProfiles = []; profile.ResponseProfiles.push({ diff --git a/src/scripts/datetime.js b/src/scripts/datetime.js index 8a7666844c..34ff23fe63 100644 --- a/src/scripts/datetime.js +++ b/src/scripts/datetime.js @@ -41,12 +41,12 @@ define(['globalize'], function (globalize) { } // if there's a timezone, calculate it - if (d[8] !== "Z" && d[10]) { + if (d[8] !== 'Z' && d[10]) { var offset = d[10] * 60 * 60 * 1000; if (d[11]) { offset += d[11] * 60 * 1000; } - if (d[9] === "-") { + if (d[9] === '-') { ms -= offset; } else { ms += offset; @@ -159,13 +159,13 @@ define(['globalize'], function (globalize) { var optionList = getOptionList(options); if (optionList.length === 1 && optionList[0].name === 'weekday') { var weekday = []; - weekday[0] = "Sun"; - weekday[1] = "Mon"; - weekday[2] = "Tue"; - weekday[3] = "Wed"; - weekday[4] = "Thu"; - weekday[5] = "Fri"; - weekday[6] = "Sat"; + weekday[0] = 'Sun'; + weekday[1] = 'Mon'; + weekday[2] = 'Tue'; + weekday[3] = 'Wed'; + weekday[4] = 'Thu'; + weekday[5] = 'Fri'; + weekday[6] = 'Sat'; return weekday[date.getDay()]; } diff --git a/src/scripts/deleteHelper.js b/src/scripts/deleteHelper.js new file mode 100644 index 0000000000..e13eb9c4ae --- /dev/null +++ b/src/scripts/deleteHelper.js @@ -0,0 +1,54 @@ +import connectionManager from 'connectionManager'; +import confirm from 'confirm'; +import appRouter from 'appRouter'; +import globalize from 'globalize'; + +function alertText(options) { + + return new Promise(function (resolve, reject) { + + require(['alert'], function (alert) { + alert(options).then(resolve, resolve); + }); + }); +} + +export function deleteItem(options) { + + const item = options.item; + const parentId = item.SeasonId || item.SeriesId || item.ParentId; + + let apiClient = connectionManager.getApiClient(item.ServerId); + + return confirm({ + + title: globalize.translate('HeaderDeleteItem'), + text: globalize.translate('ConfirmDeleteItem'), + confirmText: globalize.translate('Delete'), + primary: 'delete' + + }).then(function () { + + return apiClient.deleteItem(item.Id).then(function () { + + if (options.navigate) { + if (parentId) { + appRouter.showItem(parentId, item.ServerId); + } else { + appRouter.goHome(); + } + } + }, function (err) { + + let result = function () { + return Promise.reject(err); + }; + + return alertText(globalize.translate('ErrorDeletingItem')).then(result, result); + }); + }); +} + +export default { + deleteItem: deleteItem +}; diff --git a/src/scripts/dfnshelper.js b/src/scripts/dfnshelper.js index 379190842b..6ad2ee9709 100644 --- a/src/scripts/dfnshelper.js +++ b/src/scripts/dfnshelper.js @@ -1,104 +1,62 @@ -import { ar, be, bg, ca, cs, da, de, el, enGB, enUS, es, faIR, fi, fr, frCA, he, hi, hr, hu, id, it, kk, ko, lt, ms, nb, nl, pl, ptBR, pt, ro, ru, sk, sl, sv, tr, uk, vi, zhCN, zhTW } from 'date-fns/locale'; -import globalize from 'globalize'; - -export function getLocale() { - switch (globalize.getCurrentLocale()) { - case 'ar': - return ar; - case 'be-by': - return be; - case 'bg-bg': - return bg; - case 'ca': - return ca; - case 'cs': - return cs; - case 'da': - return da; - case 'de': - return de; - case 'el': - return el; - case 'en-gb': - return enGB; - case 'en-us': - return enUS; - case 'es': - return es; - case 'es-ar': - return es; - case 'es-mx': - return es; - case 'fa': - return faIR; - case 'fi': - return fi; - case 'fr': - return fr; - case 'fr-ca': - return frCA; - case 'gsw': - return de; - case 'he': - return he; - case 'hi-in': - return hi; - case 'hr': - return hr; - case 'hu': - return hu; - case 'id': - return id; - case 'it': - return it; - case 'kk': - return kk; - case 'ko': - return ko; - case 'lt-lt': - return lt; - case 'ms': - return ms; - case 'nb': - return nb; - case 'nl': - return nl; - case 'pl': - return pl; - case 'pt-br': - return ptBR; - case 'pt-pt': - return pt; - case 'ro': - return ro; - case 'ru': - return ru; - case 'sk': - return sk; - case 'sl-si': - return sl; - case 'sv': - return sv; - case 'tr': - return tr; - case 'uk': - return uk; - case 'vi': - return vi; - case 'zh-cn': - return zhCN; - case 'zh-hk': - return zhCN; - case 'zh-tw': - return zhTW; - default: - return enUS; - } -} - -export const localeWithSuffix = { addSuffix: true, locale: getLocale() }; - -export default { - getLocale: getLocale, - localeWithSuffix: localeWithSuffix -}; +import { ar, be, bg, ca, cs, da, de, el, enGB, enUS, es, faIR, fi, fr, frCA, he, hi, hr, hu, id, it, ja, kk, ko, lt, ms, nb, + nl, pl, ptBR, pt, ro, ru, sk, sl, sv, tr, uk, vi, zhCN, zhTW } from 'date-fns/locale'; +import globalize from 'globalize'; + +const dateLocales = (locale) => ({ + 'ar': ar, + 'be-by': be, + 'bg-bg': bg, + 'ca': ca, + 'cs': cs, + 'da': da, + 'de': de, + 'el': el, + 'en-gb': enGB, + 'en-us': enUS, + 'es': es, + 'es-ar': es, + 'es-mx': es, + 'fa': faIR, + 'fi': fi, + 'fr': fr, + 'fr-ca': frCA, + 'gsw': de, + 'he': he, + 'hi-in': hi, + 'hr': hr, + 'hu': hu, + 'id': id, + 'it': it, + 'ja': ja, + 'kk': kk, + 'ko': ko, + 'lt-lt': lt, + 'ms': ms, + 'nb': nb, + 'nl': nl, + 'pl': pl, + 'pt-br': ptBR, + 'pt-pt': pt, + 'ro': ro, + 'ru': ru, + 'sk': sk, + 'sl-si': sl, + 'sv': sv, + 'tr': tr, + 'uk': uk, + 'vi': vi, + 'zh-cn': zhCN, + 'zh-hk': zhCN, + 'zh-tw': zhTW +})[locale]; + +export function getLocale() { + return dateLocales(globalize.getCurrentLocale()) || enUS; +} + +export const localeWithSuffix = { addSuffix: true, locale: getLocale() }; + +export default { + getLocale: getLocale, + localeWithSuffix: localeWithSuffix +}; diff --git a/src/components/dom.js b/src/scripts/dom.js similarity index 92% rename from src/components/dom.js rename to src/scripts/dom.js index a9ce5d53a2..a3d157f337 100644 --- a/src/components/dom.js +++ b/src/scripts/dom.js @@ -93,7 +93,7 @@ supportsCaptureOption = true; } }); - window.addEventListener("test", null, opts); + window.addEventListener('test', null, opts); } catch (e) { console.debug('error checking capture support'); } @@ -158,7 +158,7 @@ if (!windowSizeEventsBound) { windowSizeEventsBound = true; - addEventListener(window, "orientationchange", clearWindowSize, { passive: true }); + addEventListener(window, 'orientationchange', clearWindowSize, { passive: true }); addEventListener(window, 'resize', clearWindowSize, { passive: true }); } } @@ -204,12 +204,12 @@ return _animationEvent; } - const el = document.createElement("div"); + const el = document.createElement('div'); const animations = { - "animation": "animationend", - "OAnimation": "oAnimationEnd", - "MozAnimation": "animationend", - "WebkitAnimation": "webkitAnimationEnd" + 'animation': 'animationend', + 'OAnimation': 'oAnimationEnd', + 'MozAnimation': 'animationend', + 'WebkitAnimation': 'webkitAnimationEnd' }; for (let t in animations) { if (el.style[t] !== undefined) { @@ -244,12 +244,12 @@ return _transitionEvent; } - const el = document.createElement("div"); + const el = document.createElement('div'); const transitions = { - "transition": "transitionend", - "OTransition": "oTransitionEnd", - "MozTransition": "transitionend", - "WebkitTransition": "webkitTransitionEnd" + 'transition': 'transitionend', + 'OTransition': 'oTransitionEnd', + 'MozTransition': 'transitionend', + 'WebkitTransition': 'webkitTransitionEnd' }; for (let t in transitions) { if (el.style[t] !== undefined) { diff --git a/src/scripts/editorsidebar.js b/src/scripts/editorsidebar.js index b7cb4b5c57..8168389566 100644 --- a/src/scripts/editorsidebar.js +++ b/src/scripts/editorsidebar.js @@ -1,5 +1,5 @@ -define(["datetime", "jQuery", "material-icons"], function (datetime, $) { - "use strict"; +define(['datetime', 'jQuery', 'globalize', 'material-icons'], function (datetime, $, globalize) { + 'use strict'; function getNode(item, folderState, selected) { var htmlName = getNodeInnerHtml(item); @@ -7,7 +7,7 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { id: item.Id, text: htmlName, state: { - opened: item.IsFolder && folderState == "open", + opened: item.IsFolder && folderState == 'open', selected: selected }, li_attr: { @@ -17,7 +17,7 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { }; if (item.IsFolder) { node.children = [{ - text: "Loading...", + text: 'Loading...', icon: false }]; node.icon = false; @@ -36,30 +36,30 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { function getNodeInnerHtml(item) { var name = item.Name; if (item.Number) { - name = item.Number + " - " + name; + name = item.Number + ' - ' + name; } - if (item.IndexNumber != null && item.Type != "Season") { - name = item.IndexNumber + " - " + name; + if (item.IndexNumber != null && item.Type != 'Season') { + name = item.IndexNumber + ' - ' + name; } var htmlName = "
"; if (item.IsFolder) { - htmlName += 'folder'; - } else if (item.MediaType === "Video") { - htmlName += 'movie'; - } else if (item.MediaType === "Audio") { - htmlName += 'audiotrack'; - } else if (item.Type === "TvChannel") { - htmlName += ''; - } else if (item.MediaType === "Photo") { - htmlName += 'photo'; - } else if (item.MediaType === "Book") { - htmlName += 'book'; + htmlName += ''; + } else if (item.MediaType === 'Video') { + htmlName += ''; + } else if (item.MediaType === 'Audio') { + htmlName += ''; + } else if (item.Type === 'TvChannel') { + htmlName += ''; + } else if (item.MediaType === 'Photo') { + htmlName += ''; + } else if (item.MediaType === 'Book') { + htmlName += ''; } if (item.LockData) { - htmlName += 'lock'; + htmlName += ''; } htmlName += name; - htmlName += "
"; + htmlName += '
'; return htmlName; } @@ -69,36 +69,36 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { }).then(function (result) { var nodes = []; nodes.push({ - id: "MediaFolders", - text: Globalize.translate("HeaderMediaFolders"), + id: 'MediaFolders', + text: globalize.translate('HeaderMediaFolders'), state: { opened: true }, li_attr: { - itemtype: "mediafolders", + itemtype: 'mediafolders', loadedFromServer: true }, icon: false }); if (result.TotalRecordCount) { nodes.push({ - id: "livetv", - text: Globalize.translate("HeaderLiveTV"), + id: 'livetv', + text: globalize.translate('HeaderLiveTV'), state: { opened: false }, li_attr: { - itemtype: "livetv" + itemtype: 'livetv' }, children: [{ - text: "Loading...", + text: 'Loading...', icon: false }], icon: false }); } callback.call(scope, nodes); - nodesToLoad.push("MediaFolders"); + nodesToLoad.push('MediaFolders'); }); } @@ -108,7 +108,7 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { AddCurrentProgram: false }).then(function (result) { var nodes = result.Items.map(function (i) { - var state = openItems.indexOf(i.Id) == -1 ? "closed" : "open"; + var state = openItems.indexOf(i.Id) == -1 ? 'closed' : 'open'; return getNode(i, state, false); }); callback(nodes); @@ -116,9 +116,9 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { } function loadMediaFolders(page, scope, openItems, callback) { - ApiClient.getJSON(ApiClient.getUrl("Library/MediaFolders")).then(function (result) { + ApiClient.getJSON(ApiClient.getUrl('Library/MediaFolders')).then(function (result) { var nodes = result.Items.map(function (n) { - var state = openItems.indexOf(n.Id) == -1 ? "closed" : "open"; + var state = openItems.indexOf(n.Id) == -1 ? 'closed' : 'open'; return getNode(n, state, false); }); callback.call(scope, nodes); @@ -132,21 +132,21 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { function loadNode(page, scope, node, openItems, selectedId, currentUser, callback) { var id = node.id; - if (id == "#") { + if (id == '#') { loadChildrenOfRootNode(page, scope, callback); return; } - if (id == "livetv") { + if (id == 'livetv') { loadLiveTvChannels(id, openItems, callback); return; } - if (id == "MediaFolders") { + if (id == 'MediaFolders') { loadMediaFolders(page, scope, openItems, callback); return; } var query = { ParentId: id, - Fields: "Settings", + Fields: 'Settings', IsVirtualUnaired: false, IsMissing: false, EnableTotalRecordCount: false, @@ -154,12 +154,12 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { EnableUserData: false }; var itemtype = node.li_attr.itemtype; - if (itemtype != "Season" && itemtype != "Series") { - query.SortBy = "SortName"; + if (itemtype != 'Season' && itemtype != 'Series') { + query.SortBy = 'SortName'; } ApiClient.getItems(Dashboard.getCurrentUserId(), query).then(function (result) { var nodes = result.Items.map(function (n) { - var state = openItems.indexOf(n.Id) == -1 ? "closed" : "open"; + var state = openItems.indexOf(n.Id) == -1 ? 'closed' : 'open'; return getNode(n, state, n.Id == selectedId); }); callback.call(scope, nodes); @@ -172,14 +172,14 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { } function scrollToNode(id) { - var elem = $("#" + id)[0]; + var elem = $('#' + id)[0]; if (elem) { elem.scrollIntoView(); } } function initializeTree(page, currentUser, openItems, selectedId) { - require(["jstree"], function () { + require(['jstree'], function () { initializeTreeInternal(page, currentUser, openItems, selectedId); }); } @@ -192,41 +192,41 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { serverItemType: node.li_attr.serveritemtype, collectionType: node.li_attr.collectiontype }; - if (eventData.itemType != "livetv" && eventData.itemType != "mediafolders") { + if (eventData.itemType != 'livetv' && eventData.itemType != 'mediafolders') { { - this.dispatchEvent(new CustomEvent("itemclicked", { + this.dispatchEvent(new CustomEvent('itemclicked', { detail: eventData, bubbles: true, cancelable: false })); } - document.querySelector(".editPageSidebar").classList.add("editPageSidebar-withcontent"); + document.querySelector('.editPageSidebar').classList.add('editPageSidebar-withcontent'); } else { - document.querySelector(".editPageSidebar").classList.remove("editPageSidebar-withcontent"); + document.querySelector('.editPageSidebar').classList.remove('editPageSidebar-withcontent'); } } function onNodeOpen(event, data) { - var page = $(this).parents(".page")[0]; + var page = $(this).parents('.page')[0]; var node = data.node; - if (node.children && node.children) { + if (node.children) { loadNodesToLoad(page, node); } - if (node.li_attr && node.id != "#" && !node.li_attr.loadedFromServer) { + if (node.li_attr && node.id != '#' && !node.li_attr.loadedFromServer) { node.li_attr.loadedFromServer = true; - $.jstree.reference(".libraryTree", page).load_node(node.id, loadNodeCallback); + $.jstree.reference('.libraryTree', page).load_node(node.id, loadNodeCallback); } } function onNodeLoad(event, data) { - var page = $(this).parents(".page")[0]; + var page = $(this).parents('.page')[0]; var node = data.node; - if (node.children && node.children) { + if (node.children) { loadNodesToLoad(page, node); } - if (node.li_attr && node.id != "#" && !node.li_attr.loadedFromServer) { + if (node.li_attr && node.id != '#' && !node.li_attr.loadedFromServer) { node.li_attr.loadedFromServer = true; - $.jstree.reference(".libraryTree", page).load_node(node.id, loadNodeCallback); + $.jstree.reference('.libraryTree', page).load_node(node.id, loadNodeCallback); } } @@ -234,18 +234,18 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { nodesToLoad = []; selectedNodeId = null; $.jstree.destroy(); - $(".libraryTree", page).jstree({ - "plugins": ["wholerow"], + $('.libraryTree', page).jstree({ + 'plugins': ['wholerow'], core: { check_callback: true, data: function (node, callback) { loadNode(page, this, node, openItems, selectedId, currentUser, callback); }, themes: { - variant: "large" + variant: 'large' } } - }).off("select_node.jstree", onNodeSelect).on("select_node.jstree", onNodeSelect).off("open_node.jstree", onNodeOpen).on("open_node.jstree", onNodeOpen).off("load_node.jstree", onNodeLoad).on("load_node.jstree", onNodeLoad); + }).off('select_node.jstree', onNodeSelect).on('select_node.jstree', onNodeSelect).off('open_node.jstree', onNodeOpen).on('open_node.jstree', onNodeOpen).off('load_node.jstree', onNodeLoad).on('load_node.jstree', onNodeLoad); } function loadNodesToLoad(page, node) { @@ -256,7 +256,7 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { nodesToLoad = nodesToLoad.filter(function (n) { return n != child; }); - $.jstree.reference(".libraryTree", page).load_node(child, loadNodeCallback); + $.jstree.reference('.libraryTree', page).load_node(child, loadNodeCallback); } } } @@ -270,14 +270,14 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { } function updateEditorNode(page, item) { - var elem = $("#" + item.Id + ">a", page)[0]; + var elem = $('#' + item.Id + '>a', page)[0]; if (elem == null) { return; } - $(".editorNode", elem).remove(); + $('.editorNode', elem).remove(); $(elem).append(getNodeInnerHtml(item)); if (item.IsFolder) { - var tree = jQuery.jstree._reference(".libraryTree"); + var tree = jQuery.jstree._reference('.libraryTree'); var currentNode = tree._get_node(null, false); tree.refresh(currentNode); } @@ -292,15 +292,15 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { return itemId; } var url = window.location.hash || window.location.href; - return getParameterByName("id", url); + return getParameterByName('id', url); } var nodesToLoad = []; var selectedNodeId; - $(document).on("itemsaved", ".metadataEditorPage", function (e, item) { + $(document).on('itemsaved', '.metadataEditorPage', function (e, item) { updateEditorNode(this, item); - }).on("pagebeforeshow", ".metadataEditorPage", function () { - require(["css!assets/css/metadataeditor.css"]); - }).on("pagebeforeshow", ".metadataEditorPage", function () { + }).on('pagebeforeshow', '.metadataEditorPage', function () { + require(['css!assets/css/metadataeditor.css']); + }).on('pagebeforeshow', '.metadataEditorPage', function () { var page = this; Dashboard.getCurrentUser().then(function (user) { var id = getCurrentItemId(); @@ -315,9 +315,9 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) { initializeTree(page, user, []); } }); - }).on("pagebeforehide", ".metadataEditorPage", function () { + }).on('pagebeforehide', '.metadataEditorPage', function () { var page = this; - $(".libraryTree", page).off("select_node.jstree", onNodeSelect).off("open_node.jstree", onNodeOpen).off("load_node.jstree", onNodeLoad); + $('.libraryTree', page).off('select_node.jstree', onNodeSelect).off('open_node.jstree', onNodeOpen).off('load_node.jstree', onNodeLoad); }); var itemId; window.MetadataEditor = { diff --git a/src/components/filedownloader.js b/src/scripts/fileDownloader.js similarity index 69% rename from src/components/filedownloader.js rename to src/scripts/fileDownloader.js index a8b45ab106..f2ca42211f 100644 --- a/src/components/filedownloader.js +++ b/src/scripts/fileDownloader.js @@ -1,10 +1,10 @@ -import multiDownload from "multi-download"; +import multiDownload from 'multi-download'; export function download(items) { if (window.NativeShell) { items.map(function (item) { - window.NativeShell.downloadFile(item.url); + window.NativeShell.downloadFile(item); }); } else { multiDownload(items.map(function (item) { diff --git a/src/components/filesystem.js b/src/scripts/filesystem.js similarity index 100% rename from src/components/filesystem.js rename to src/scripts/filesystem.js diff --git a/src/components/input/gamepadtokey.js b/src/scripts/gamepadtokey.js similarity index 89% rename from src/components/input/gamepadtokey.js rename to src/scripts/gamepadtokey.js index 5356bcbb45..089e5c81e9 100644 --- a/src/components/input/gamepadtokey.js +++ b/src/scripts/gamepadtokey.js @@ -20,7 +20,7 @@ // # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // # THE SOFTWARE. require(['apphost'], function (appHost) { - "use strict"; + 'use strict'; var _GAMEPAD_A_BUTTON_INDEX = 0; var _GAMEPAD_B_BUTTON_INDEX = 1; @@ -28,16 +28,16 @@ require(['apphost'], function (appHost) { var _GAMEPAD_DPAD_DOWN_BUTTON_INDEX = 13; var _GAMEPAD_DPAD_LEFT_BUTTON_INDEX = 14; var _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX = 15; - var _GAMEPAD_A_KEY = "GamepadA"; - var _GAMEPAD_B_KEY = "GamepadB"; - var _GAMEPAD_DPAD_UP_KEY = "GamepadDPadUp"; - var _GAMEPAD_DPAD_DOWN_KEY = "GamepadDPadDown"; - var _GAMEPAD_DPAD_LEFT_KEY = "GamepadDPadLeft"; - var _GAMEPAD_DPAD_RIGHT_KEY = "GamepadDPadRight"; - var _GAMEPAD_LEFT_THUMBSTICK_UP_KEY = "GamepadLeftThumbStickUp"; - var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY = "GamepadLeftThumbStickDown"; - var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY = "GamepadLeftThumbStickLeft"; - var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY = "GamepadLeftThumbStickRight"; + var _GAMEPAD_A_KEY = 'GamepadA'; + var _GAMEPAD_B_KEY = 'GamepadB'; + var _GAMEPAD_DPAD_UP_KEY = 'GamepadDPadUp'; + var _GAMEPAD_DPAD_DOWN_KEY = 'GamepadDPadDown'; + var _GAMEPAD_DPAD_LEFT_KEY = 'GamepadDPadLeft'; + var _GAMEPAD_DPAD_RIGHT_KEY = 'GamepadDPadRight'; + var _GAMEPAD_LEFT_THUMBSTICK_UP_KEY = 'GamepadLeftThumbStickUp'; + var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY = 'GamepadLeftThumbStickDown'; + var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY = 'GamepadLeftThumbStickLeft'; + var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY = 'GamepadLeftThumbStickRight'; var _GAMEPAD_A_KEYCODE = 0; var _GAMEPAD_B_KEYCODE = 27; var _GAMEPAD_DPAD_UP_KEYCODE = 38; @@ -184,7 +184,7 @@ require(['apphost'], function (appHost) { function allowInput() { // This would be nice but always seems to return true with electron - if (!isElectron && document.hidden) { + if (!isElectron && document.hidden) { /* eslint-disable-line compat/compat */ return false; } @@ -234,7 +234,7 @@ require(['apphost'], function (appHost) { } if (fire && keyCode) { - raiseEvent("keydown", key, keyCode); + raiseEvent('keydown', key, keyCode); } } else if (newPressedState === false && oldPressedState === true) { @@ -243,7 +243,7 @@ require(['apphost'], function (appHost) { // button up if (keyCode) { - raiseEvent("keyup", key, keyCode); + raiseEvent('keyup', key, keyCode); } if (clickonKeyUp) { clickElement(document.activeElement || window); @@ -254,7 +254,7 @@ require(['apphost'], function (appHost) { var inputLoopTimer; function runInputLoop() { // Get the latest gamepad state. - var gamepads = navigator.getGamepads(); + var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */ for (var i = 0, len = gamepads.length; i < len; i++) { var gamepad = gamepads[i]; if (!gamepad) { @@ -362,7 +362,7 @@ require(['apphost'], function (appHost) { } function isGamepadConnected() { - var gamepads = navigator.getGamepads(); + var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */ for (var i = 0, len = gamepads.length; i < len; i++) { var gamepad = gamepads[i]; if (gamepad && gamepad.connected) { @@ -373,36 +373,38 @@ require(['apphost'], function (appHost) { } function onFocusOrGamepadAttach(e) { + /* eslint-disable-next-line compat/compat */ if (isGamepadConnected() && document.hasFocus()) { - console.log("Gamepad connected! Starting input loop"); + console.log('Gamepad connected! Starting input loop'); startInputLoop(); } } function onFocusOrGamepadDetach(e) { + /* eslint-disable-next-line compat/compat */ if (!isGamepadConnected() || !document.hasFocus()) { - console.log("Gamepad disconnected! No other gamepads are connected, stopping input loop"); + console.log('Gamepad disconnected! No other gamepads are connected, stopping input loop'); stopInputLoop(); } else { - console.log("Gamepad disconnected! There are gamepads still connected."); + console.log('Gamepad disconnected! There are gamepads still connected.'); } } // Event listeners for any change in gamepads' state. - window.addEventListener("gamepaddisconnected", onFocusOrGamepadDetach); - window.addEventListener("gamepadconnected", onFocusOrGamepadAttach); - window.addEventListener("blur", onFocusOrGamepadDetach); - window.addEventListener("focus", onFocusOrGamepadAttach); + window.addEventListener('gamepaddisconnected', onFocusOrGamepadDetach); + window.addEventListener('gamepadconnected', onFocusOrGamepadAttach); + window.addEventListener('blur', onFocusOrGamepadDetach); + window.addEventListener('focus', onFocusOrGamepadAttach); onFocusOrGamepadAttach(); // The gamepadInputEmulation is a string property that exists in JavaScript UWAs and in WebViews in UWAs. // It won't exist in Win8.1 style apps or browsers. - if (window.navigator && typeof window.navigator.gamepadInputEmulation === "string") { + if (window.navigator && typeof window.navigator.gamepadInputEmulation === 'string') { // We want the gamepad to provide gamepad VK keyboard events rather than moving a // mouse like cursor. Set to "keyboard", the gamepad will provide such keyboard events // and provide input to the DOM navigator.getGamepads API. - window.navigator.gamepadInputEmulation = "gamepad"; + window.navigator.gamepadInputEmulation = 'gamepad'; } }); diff --git a/src/scripts/imagehelper.js b/src/scripts/imagehelper.js index 790862b223..be3fd2834e 100644 --- a/src/scripts/imagehelper.js +++ b/src/scripts/imagehelper.js @@ -3,72 +3,73 @@ import browser from 'browser'; export function getDeviceIcon(device) { - var baseUrl = "assets/img/devices/"; + var baseUrl = 'assets/img/devices/'; switch (device.AppName || device.Client) { - case "Samsung Smart TV": - return baseUrl + "samsung.svg"; - case "Xbox One": - return baseUrl + "xbox.svg"; - case "Sony PS4": - return baseUrl + "playstation.svg"; - case "Kodi": - return baseUrl + "kodi.svg"; - case "Jellyfin Android": - return baseUrl + "android.svg"; - case "Jellyfin Web": + case 'Samsung Smart TV': + return baseUrl + 'samsung.svg'; + case 'Xbox One': + return baseUrl + 'xbox.svg'; + case 'Sony PS4': + return baseUrl + 'playstation.svg'; + case 'Kodi': + return baseUrl + 'kodi.svg'; + case 'Jellyfin Android': + case 'Android TV': + return baseUrl + 'android.svg'; + case 'Jellyfin Web': switch (device.Name || device.DeviceName) { - case "Opera": - case "Opera TV": - case "Opera Android": - return baseUrl + "opera.svg"; - case "Chrome": - case "Chrome Android": - return baseUrl + "chrome.svg"; - case "Firefox": - case "Firefox Android": - return baseUrl + "firefox.svg"; - case "Safari": - case "Safari iPad": - case "Safari iPhone": - return baseUrl + "safari.svg"; - case "Edge": - return baseUrl + "edge.svg"; - case "Internet Explorer": - return baseUrl + "msie.svg"; + case 'Opera': + case 'Opera TV': + case 'Opera Android': + return baseUrl + 'opera.svg'; + case 'Chrome': + case 'Chrome Android': + return baseUrl + 'chrome.svg'; + case 'Firefox': + case 'Firefox Android': + return baseUrl + 'firefox.svg'; + case 'Safari': + case 'Safari iPad': + case 'Safari iPhone': + return baseUrl + 'safari.svg'; + case 'Edge': + return baseUrl + 'edge.svg'; + case 'Internet Explorer': + return baseUrl + 'msie.svg'; default: - return baseUrl + "html5.svg"; + return baseUrl + 'html5.svg'; } default: - return baseUrl + "other.svg"; + return baseUrl + 'other.svg'; } } export function getLibraryIcon(library) { switch (library) { - case "movies": - return "video_library"; - case "music": - return "library_music"; - case "photos": - return "photo_library"; - case "livetv": - return "live_tv"; - case "tvshows": - return "tv"; - case "trailers": - return "local_movies"; - case "homevideos": - return "photo_library"; - case "musicvideos": - return "music_video"; - case "books": - return "library_books"; - case "channels": - return "videocam"; - case "playlists": - return "view_list"; + case 'movies': + return 'video_library'; + case 'music': + return 'library_music'; + case 'photos': + return 'photo_library'; + case 'livetv': + return 'live_tv'; + case 'tvshows': + return 'tv'; + case 'trailers': + return 'local_movies'; + case 'homevideos': + return 'photo_library'; + case 'musicvideos': + return 'music_video'; + case 'books': + return 'library_books'; + case 'channels': + return 'videocam'; + case 'playlists': + return 'view_list'; default: - return "folder"; + return 'folder'; } } diff --git a/src/scripts/inputManager.js b/src/scripts/inputManager.js index 4d62ff9f20..8af52c77e8 100644 --- a/src/scripts/inputManager.js +++ b/src/scripts/inputManager.js @@ -1,44 +1,49 @@ -define(['playbackManager', 'focusManager', 'appRouter', 'dom', 'apphost'], function (playbackManager, focusManager, appRouter, dom, appHost) { - 'use strict'; +import playbackManager from 'playbackManager'; +import focusManager from 'focusManager'; +import appRouter from 'appRouter'; +import dom from 'dom'; +import appHost from 'apphost'; - var lastInputTime = new Date().getTime(); +/* eslint-disable indent */ - function notify() { + let lastInputTime = new Date().getTime(); + + export function notify() { lastInputTime = new Date().getTime(); handleCommand('unknown'); } - function notifyMouseMove() { + export function notifyMouseMove() { lastInputTime = new Date().getTime(); } - function idleTime() { + export function idleTime() { return new Date().getTime() - lastInputTime; } - function select(sourceElement) { + export function select(sourceElement) { sourceElement.click(); } - var eventListenerCount = 0; - function on(scope, fn) { + let eventListenerCount = 0; + export function on(scope, fn) { eventListenerCount++; dom.addEventListener(scope, 'command', fn, {}); } - function off(scope, fn) { + export function off(scope, fn) { if (eventListenerCount) { eventListenerCount--; } dom.removeEventListener(scope, 'command', fn, {}); } - var commandTimes = {}; + let commandTimes = {}; function checkCommandTime(command) { - var last = commandTimes[command] || 0; - var now = new Date().getTime(); + const last = commandTimes[command] || 0; + const now = new Date().getTime(); if ((now - last) < 1000) { return false; @@ -48,11 +53,11 @@ define(['playbackManager', 'focusManager', 'appRouter', 'dom', 'apphost'], funct return true; } - function handleCommand(name, options) { + export function handleCommand(commandName, options) { lastInputTime = new Date().getTime(); - var sourceElement = (options ? options.sourceElement : null); + let sourceElement = (options ? options.sourceElement : null); if (sourceElement) { sourceElement = focusManager.focusableParent(sourceElement); @@ -61,7 +66,7 @@ define(['playbackManager', 'focusManager', 'appRouter', 'dom', 'apphost'], funct if (!sourceElement) { sourceElement = document.activeElement || window; - var dlg = document.querySelector('.dialogContainer .dialog.opened'); + const dlg = document.querySelector('.dialogContainer .dialog.opened'); if (dlg && (!sourceElement || !dlg.contains(sourceElement))) { sourceElement = dlg; @@ -69,183 +74,182 @@ define(['playbackManager', 'focusManager', 'appRouter', 'dom', 'apphost'], funct } if (eventListenerCount) { - var customEvent = new CustomEvent("command", { + const customEvent = new CustomEvent('command', { detail: { - command: name + command: commandName }, bubbles: true, cancelable: true }); - var eventResult = sourceElement.dispatchEvent(customEvent); + const eventResult = sourceElement.dispatchEvent(customEvent); if (!eventResult) { // event cancelled return; } } - switch (name) { - case 'up': + const keyActions = (command) => ({ + 'up': () => { focusManager.moveUp(sourceElement); - break; - case 'down': + }, + 'down': () => { focusManager.moveDown(sourceElement); - break; - case 'left': + }, + 'left': () => { focusManager.moveLeft(sourceElement); - break; - case 'right': + }, + 'right': () => { focusManager.moveRight(sourceElement); - break; - case 'home': + }, + 'home': () => { appRouter.goHome(); - break; - case 'settings': + }, + 'settings': () => { appRouter.showSettings(); - break; - case 'back': + }, + 'back': () => { if (appRouter.canGoBack()) { appRouter.back(); } else if (appHost.supports('exit')) { appHost.exit(); } - break; - case 'forward': - break; - case 'select': + }, + 'select': () => { select(sourceElement); - break; - case 'pageup': - break; - case 'pagedown': - break; - case 'end': - break; - case 'menu': - break; - case 'info': - break; - case 'nextchapter': + }, + 'nextchapter': () => { playbackManager.nextChapter(); - break; - case 'next': - case 'nexttrack': + }, + 'next': () => { playbackManager.nextTrack(); - break; - case 'previous': - case 'previoustrack': + }, + 'nexttrack': () => { + playbackManager.nextTrack(); + }, + 'previous': () => { playbackManager.previousTrack(); - break; - case 'previouschapter': + }, + 'previoustrack': () => { + playbackManager.previousTrack(); + }, + 'previouschapter': () => { playbackManager.previousChapter(); - break; - case 'guide': + }, + 'guide': () => { appRouter.showGuide(); - break; - case 'recordedtv': + }, + 'recordedtv': () => { appRouter.showRecordedTV(); - break; - case 'record': - break; - case 'livetv': + }, + 'livetv': () => { appRouter.showLiveTV(); - break; - case 'mute': + }, + 'mute': () => { playbackManager.setMute(true); - break; - case 'unmute': + }, + 'unmute': () => { playbackManager.setMute(false); - break; - case 'togglemute': + }, + 'togglemute': () => { playbackManager.toggleMute(); - break; - case 'channelup': + }, + 'channelup': () => { playbackManager.channelUp(); - break; - case 'channeldown': + }, + 'channeldown': () => { playbackManager.channelDown(); - break; - case 'volumedown': + }, + 'volumedown': () => { playbackManager.volumeDown(); - break; - case 'volumeup': + }, + 'volumeup': () => { playbackManager.volumeUp(); - break; - case 'play': + }, + 'play': () => { playbackManager.unpause(); - break; - case 'pause': + }, + 'pause': () => { playbackManager.pause(); - break; - case 'playpause': + }, + 'playpause': () => { playbackManager.playPause(); - break; - case 'stop': + }, + 'stop': () => { if (checkCommandTime('stop')) { playbackManager.stop(); } - break; - case 'changezoom': + }, + 'changezoom': () => { playbackManager.toggleAspectRatio(); - break; - case 'changeaudiotrack': + }, + 'changeaudiotrack': () => { playbackManager.changeAudioStream(); - break; - case 'changesubtitletrack': + }, + 'changesubtitletrack': () => { playbackManager.changeSubtitleStream(); - break; - case 'search': + }, + 'search': () => { appRouter.showSearch(); - break; - case 'favorites': + }, + 'favorites': () => { appRouter.showFavorites(); - break; - case 'fastforward': + }, + 'fastforward': () => { playbackManager.fastForward(); - break; - case 'rewind': + }, + 'rewind': () => { playbackManager.rewind(); - break; - case 'togglefullscreen': + }, + 'togglefullscreen': () => { playbackManager.toggleFullscreen(); - break; - case 'disabledisplaymirror': + }, + 'disabledisplaymirror': () => { playbackManager.enableDisplayMirroring(false); - break; - case 'enabledisplaymirror': + }, + 'enabledisplaymirror': () => { playbackManager.enableDisplayMirroring(true); - break; - case 'toggledisplaymirror': + }, + 'toggledisplaymirror': () => { playbackManager.toggleDisplayMirroring(); - break; - case 'nowplaying': + }, + 'nowplaying': () => { appRouter.showNowPlaying(); - break; - case 'repeatnone': + }, + 'repeatnone': () => { playbackManager.setRepeatMode('RepeatNone'); - break; - case 'repeatall': + }, + 'repeatall': () => { playbackManager.setRepeatMode('RepeatAll'); - break; - case 'repeatone': + }, + 'repeatone': () => { playbackManager.setRepeatMode('RepeatOne'); - break; - default: - break; + } + })[command]; + + const action = keyActions(commandName); + if (action !== undefined) { + action.call(); + } else { + console.debug(`inputManager: tried to process command with no action assigned: ${commandName}`); } } + // Alias for backward compatibility + export const trigger = handleCommand; + dom.addEventListener(document, 'click', notify, { passive: true }); - return { - trigger: handleCommand, - handle: handleCommand, - notify: notify, - notifyMouseMove: notifyMouseMove, - idleTime: idleTime, - on: on, - off: off - }; -}); +/* eslint-enable indent */ + +export default { + trigger: handleCommand, + handle: handleCommand, + notify: notify, + notifyMouseMove: notifyMouseMove, + idleTime: idleTime, + on: on, + off: off +}; diff --git a/src/scripts/itembynamedetailpage.js b/src/scripts/itembynamedetailpage.js index ea760900ec..44eca55589 100644 --- a/src/scripts/itembynamedetailpage.js +++ b/src/scripts/itembynamedetailpage.js @@ -1,105 +1,105 @@ -define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryBrowser", "emby-itemscontainer", "emby-button"], function (connectionManager, listView, cardBuilder, imageLoader, libraryBrowser) { - "use strict"; +define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryBrowser', 'globalize', 'emby-itemscontainer', 'emby-button'], function (connectionManager, listView, cardBuilder, imageLoader, libraryBrowser, globalize) { + 'use strict'; function renderItems(page, item) { var sections = []; if (item.ArtistCount) { sections.push({ - name: Globalize.translate("TabArtists"), - type: "MusicArtist" + name: globalize.translate('TabArtists'), + type: 'MusicArtist' }); } - if (item.ProgramCount && "Person" == item.Type) { + if (item.ProgramCount && 'Person' == item.Type) { sections.push({ - name: Globalize.translate("HeaderUpcomingOnTV"), - type: "Program" + name: globalize.translate('HeaderUpcomingOnTV'), + type: 'Program' }); } if (item.MovieCount) { sections.push({ - name: Globalize.translate("TabMovies"), - type: "Movie" + name: globalize.translate('TabMovies'), + type: 'Movie' }); } if (item.SeriesCount) { sections.push({ - name: Globalize.translate("TabShows"), - type: "Series" + name: globalize.translate('TabShows'), + type: 'Series' }); } if (item.EpisodeCount) { sections.push({ - name: Globalize.translate("TabEpisodes"), - type: "Episode" + name: globalize.translate('TabEpisodes'), + type: 'Episode' }); } if (item.TrailerCount) { sections.push({ - name: Globalize.translate("TabTrailers"), - type: "Trailer" + name: globalize.translate('TabTrailers'), + type: 'Trailer' }); } if (item.AlbumCount) { sections.push({ - name: Globalize.translate("TabAlbums"), - type: "MusicAlbum" + name: globalize.translate('TabAlbums'), + type: 'MusicAlbum' }); } if (item.MusicVideoCount) { sections.push({ - name: Globalize.translate("TabMusicVideos"), - type: "MusicVideo" + name: globalize.translate('TabMusicVideos'), + type: 'MusicVideo' }); } - var elem = page.querySelector("#childrenContent"); + var elem = page.querySelector('#childrenContent'); elem.innerHTML = sections.map(function (section) { - var html = ""; - var sectionClass = "verticalSection"; + var html = ''; + var sectionClass = 'verticalSection'; - if ("Audio" === section.type) { - sectionClass += " verticalSection-extrabottompadding"; + if ('Audio' === section.type) { + sectionClass += ' verticalSection-extrabottompadding'; } html += '
'; html += '
'; html += '

'; html += section.name; - html += "

"; - html += '"; - html += "
"; + html += ''; + html += ''; + html += '
'; html += '
'; - html += "
"; - return html += "
"; - }).join(""); - var sectionElems = elem.querySelectorAll(".verticalSection"); + html += '
'; + return html += '
'; + }).join(''); + var sectionElems = elem.querySelectorAll('.verticalSection'); for (var i = 0, length = sectionElems.length; i < length; i++) { - renderSection(page, item, sectionElems[i], sectionElems[i].getAttribute("data-type")); + renderSection(page, item, sectionElems[i], sectionElems[i].getAttribute('data-type')); } } function renderSection(page, item, element, type) { switch (type) { - case "Program": + case 'Program': loadItems(element, item, type, { - MediaTypes: "", - IncludeItemTypes: "Program", - PersonTypes: "", - ArtistIds: "", - AlbumArtistIds: "", + MediaTypes: '', + IncludeItemTypes: 'Program', + PersonTypes: '', + ArtistIds: '', + AlbumArtistIds: '', Limit: 10, - SortBy: "StartDate" + SortBy: 'StartDate' }, { - shape: "overflowBackdrop", + shape: 'overflowBackdrop', showTitle: true, centerText: true, overlayMoreButton: true, @@ -111,17 +111,17 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB }); break; - case "Movie": + case 'Movie': loadItems(element, item, type, { - MediaTypes: "", - IncludeItemTypes: "Movie", - PersonTypes: "", - ArtistIds: "", - AlbumArtistIds: "", + MediaTypes: '', + IncludeItemTypes: 'Movie', + PersonTypes: '', + ArtistIds: '', + AlbumArtistIds: '', Limit: 10, - SortBy: "SortName" + SortBy: 'SortName' }, { - shape: "overflowPortrait", + shape: 'overflowPortrait', showTitle: true, centerText: true, overlayMoreButton: true, @@ -130,68 +130,68 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB }); break; - case "MusicVideo": + case 'MusicVideo': loadItems(element, item, type, { - MediaTypes: "", - IncludeItemTypes: "MusicVideo", - PersonTypes: "", - ArtistIds: "", - AlbumArtistIds: "", + MediaTypes: '', + IncludeItemTypes: 'MusicVideo', + PersonTypes: '', + ArtistIds: '', + AlbumArtistIds: '', Limit: 10, - SortBy: "SortName" + SortBy: 'SortName' }, { - shape: "overflowPortrait", + shape: 'overflowPortrait', showTitle: true, centerText: true, overlayPlayButton: true }); break; - case "Trailer": + case 'Trailer': loadItems(element, item, type, { - MediaTypes: "", - IncludeItemTypes: "Trailer", - PersonTypes: "", - ArtistIds: "", - AlbumArtistIds: "", + MediaTypes: '', + IncludeItemTypes: 'Trailer', + PersonTypes: '', + ArtistIds: '', + AlbumArtistIds: '', Limit: 10, - SortBy: "SortName" + SortBy: 'SortName' }, { - shape: "overflowPortrait", + shape: 'overflowPortrait', showTitle: true, centerText: true, overlayPlayButton: true }); break; - case "Series": + case 'Series': loadItems(element, item, type, { - MediaTypes: "", - IncludeItemTypes: "Series", - PersonTypes: "", - ArtistIds: "", - AlbumArtistIds: "", + MediaTypes: '', + IncludeItemTypes: 'Series', + PersonTypes: '', + ArtistIds: '', + AlbumArtistIds: '', Limit: 10, - SortBy: "SortName" + SortBy: 'SortName' }, { - shape: "overflowPortrait", + shape: 'overflowPortrait', showTitle: true, centerText: true, overlayMoreButton: true }); break; - case "MusicAlbum": + case 'MusicAlbum': loadItems(element, item, type, { - MediaTypes: "", - IncludeItemTypes: "MusicAlbum", - PersonTypes: "", - ArtistIds: "", - AlbumArtistIds: "", - SortOrder: "Descending", - SortBy: "ProductionYear,Sortname" + MediaTypes: '', + IncludeItemTypes: 'MusicAlbum', + PersonTypes: '', + ArtistIds: '', + AlbumArtistIds: '', + SortOrder: 'Descending', + SortBy: 'ProductionYear,Sortname' }, { - shape: "overflowSquare", + shape: 'overflowSquare', playFromHere: true, showTitle: true, showYear: true, @@ -201,17 +201,17 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB }); break; - case "MusicArtist": + case 'MusicArtist': loadItems(element, item, type, { - MediaTypes: "", - IncludeItemTypes: "MusicArtist", - PersonTypes: "", - ArtistIds: "", - AlbumArtistIds: "", + MediaTypes: '', + IncludeItemTypes: 'MusicArtist', + PersonTypes: '', + ArtistIds: '', + AlbumArtistIds: '', Limit: 8, - SortBy: "SortName" + SortBy: 'SortName' }, { - shape: "overflowSquare", + shape: 'overflowSquare', playFromHere: true, showTitle: true, showParentTitle: true, @@ -221,17 +221,17 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB }); break; - case "Episode": + case 'Episode': loadItems(element, item, type, { - MediaTypes: "", - IncludeItemTypes: "Episode", - PersonTypes: "", - ArtistIds: "", - AlbumArtistIds: "", + MediaTypes: '', + IncludeItemTypes: 'Episode', + PersonTypes: '', + ArtistIds: '', + AlbumArtistIds: '', Limit: 6, - SortBy: "SortName" + SortBy: 'SortName' }, { - shape: "overflowBackdrop", + shape: 'overflowBackdrop', showTitle: true, showParentTitle: true, centerText: true, @@ -239,17 +239,17 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB }); break; - case "Audio": + case 'Audio': loadItems(element, item, type, { - MediaTypes: "", - IncludeItemTypes: "Audio", - PersonTypes: "", - ArtistIds: "", - AlbumArtistIds: "", - SortBy: "AlbumArtist,Album,SortName" + MediaTypes: '', + IncludeItemTypes: 'Audio', + PersonTypes: '', + ArtistIds: '', + AlbumArtistIds: '', + SortBy: 'AlbumArtist,Album,SortName' }, { playFromHere: true, - action: "playallfromhere", + action: 'playallfromhere', smallIcon: true, artist: true }); @@ -259,27 +259,27 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB function loadItems(element, item, type, query, listOptions) { query = getQuery(query, item); getItemsFunction(query, item)(query.StartIndex, query.Limit, query.Fields).then(function (result) { - var html = ""; + var html = ''; if (query.Limit && result.TotalRecordCount > query.Limit) { - var link = element.querySelector("a"); - link.classList.remove("hide"); - link.setAttribute("href", getMoreItemsHref(item, type)); + var link = element.querySelector('a'); + link.classList.remove('hide'); + link.setAttribute('href', getMoreItemsHref(item, type)); } else { - element.querySelector("a").classList.add("hide"); + element.querySelector('a').classList.add('hide'); } listOptions.items = result.Items; - var itemsContainer = element.querySelector(".itemsContainer"); + var itemsContainer = element.querySelector('.itemsContainer'); - if ("Audio" == type) { + if ('Audio' == type) { html = listView.getListViewHtml(listOptions); - itemsContainer.classList.remove("vertical-wrap"); - itemsContainer.classList.add("vertical-list"); + itemsContainer.classList.remove('vertical-wrap'); + itemsContainer.classList.add('vertical-list'); } else { html = cardBuilder.getCardsHtml(listOptions); - itemsContainer.classList.add("vertical-wrap"); - itemsContainer.classList.remove("vertical-list"); + itemsContainer.classList.add('vertical-wrap'); + itemsContainer.classList.remove('vertical-list'); } itemsContainer.innerHTML = html; @@ -288,51 +288,51 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB } function getMoreItemsHref(item, type) { - if ("Genre" == item.Type) { - return "list.html?type=" + type + "&genreId=" + item.Id + "&serverId=" + item.ServerId; + if ('Genre' == item.Type) { + return 'list.html?type=' + type + '&genreId=' + item.Id + '&serverId=' + item.ServerId; } - if ("MusicGenre" == item.Type) { - return "list.html?type=" + type + "&musicGenreId=" + item.Id + "&serverId=" + item.ServerId; + if ('MusicGenre' == item.Type) { + return 'list.html?type=' + type + '&musicGenreId=' + item.Id + '&serverId=' + item.ServerId; } - if ("Studio" == item.Type) { - return "list.html?type=" + type + "&studioId=" + item.Id + "&serverId=" + item.ServerId; + if ('Studio' == item.Type) { + return 'list.html?type=' + type + '&studioId=' + item.Id + '&serverId=' + item.ServerId; } - if ("MusicArtist" == item.Type) { - return "list.html?type=" + type + "&artistId=" + item.Id + "&serverId=" + item.ServerId; + if ('MusicArtist' == item.Type) { + return 'list.html?type=' + type + '&artistId=' + item.Id + '&serverId=' + item.ServerId; } - if ("Person" == item.Type) { - return "list.html?type=" + type + "&personId=" + item.Id + "&serverId=" + item.ServerId; + if ('Person' == item.Type) { + return 'list.html?type=' + type + '&personId=' + item.Id + '&serverId=' + item.ServerId; } - return "list.html?type=" + type + "&parentId=" + item.Id + "&serverId=" + item.ServerId; + return 'list.html?type=' + type + '&parentId=' + item.Id + '&serverId=' + item.ServerId; } function addCurrentItemToQuery(query, item) { - if (item.Type == "Person") { + if (item.Type == 'Person') { query.PersonIds = item.Id; - } else if (item.Type == "Genre") { + } else if (item.Type == 'Genre') { query.Genres = item.Name; - } else if (item.Type == "MusicGenre") { + } else if (item.Type == 'MusicGenre') { query.Genres = item.Name; - } else if (item.Type == "GameGenre") { + } else if (item.Type == 'GameGenre') { query.Genres = item.Name; - } else if (item.Type == "Studio") { + } else if (item.Type == 'Studio') { query.StudioIds = item.Id; - } else if (item.Type == "MusicArtist") { + } else if (item.Type == 'MusicArtist') { query.AlbumArtistIds = item.Id; } } function getQuery(options, item) { var query = { - SortOrder: "Ascending", - IncludeItemTypes: "", + SortOrder: 'Ascending', + IncludeItemTypes: '', Recursive: true, - Fields: "AudioInfo,SeriesInfo,ParentId,PrimaryImageAspectRatio,BasicSyncInfo", + Fields: 'AudioInfo,SeriesInfo,ParentId,PrimaryImageAspectRatio,BasicSyncInfo', Limit: 100, StartIndex: 0, CollapseBoxSetItems: false @@ -349,12 +349,12 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB query.Limit = limit; if (fields) { - query.Fields += "," + fields; + query.Fields += ',' + fields; } var apiClient = connectionManager.getApiClient(item.ServerId); - if ("MusicArtist" === query.IncludeItemTypes) { + if ('MusicArtist' === query.IncludeItemTypes) { query.IncludeItemTypes = null; return apiClient.getAlbumArtists(apiClient.getCurrentUserId(), query); } diff --git a/src/components/input/keyboardnavigation.js b/src/scripts/keyboardNavigation.js similarity index 53% rename from src/components/input/keyboardnavigation.js rename to src/scripts/keyboardNavigation.js index d356854a3e..6664afed53 100644 --- a/src/components/input/keyboardnavigation.js +++ b/src/scripts/keyboardNavigation.js @@ -3,49 +3,49 @@ * @module components/input/keyboardnavigation */ -import inputManager from "inputManager"; -import layoutManager from "layoutManager"; +import inputManager from 'inputManager'; +import layoutManager from 'layoutManager'; /** * Key name mapping. */ const KeyNames = { - 13: "Enter", - 19: "Pause", - 27: "Escape", - 32: "Space", - 37: "ArrowLeft", - 38: "ArrowUp", - 39: "ArrowRight", - 40: "ArrowDown", + 13: 'Enter', + 19: 'Pause', + 27: 'Escape', + 32: 'Space', + 37: 'ArrowLeft', + 38: 'ArrowUp', + 39: 'ArrowRight', + 40: 'ArrowDown', // MediaRewind (Tizen/WebOS) - 412: "MediaRewind", + 412: 'MediaRewind', // MediaStop (Tizen/WebOS) - 413: "MediaStop", + 413: 'MediaStop', // MediaPlay (Tizen/WebOS) - 415: "MediaPlay", + 415: 'MediaPlay', // MediaFastForward (Tizen/WebOS) - 417: "MediaFastForward", + 417: 'MediaFastForward', // Back (WebOS) - 461: "Back", + 461: 'Back', // Back (Tizen) - 10009: "Back", + 10009: 'Back', // MediaTrackPrevious (Tizen) - 10232: "MediaTrackPrevious", + 10232: 'MediaTrackPrevious', // MediaTrackNext (Tizen) - 10233: "MediaTrackNext", + 10233: 'MediaTrackNext', // MediaPlayPause (Tizen) - 10252: "MediaPlayPause" + 10252: 'MediaPlayPause' }; /** * Keys used for keyboard navigation. */ -const NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"]; +const NavigationKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; let hasFieldKey = false; try { - hasFieldKey = "key" in new KeyboardEvent("keydown"); + hasFieldKey = 'key' in new KeyboardEvent('keydown'); } catch (e) { console.error("error checking 'key' field"); } @@ -78,7 +78,7 @@ export function isNavigationKey(key) { } export function enable() { - document.addEventListener("keydown", function (e) { + document.addEventListener('keydown', function (e) { const key = getKeyName(e); // Ignore navigation keys for non-TV @@ -89,54 +89,54 @@ export function enable() { let capture = true; switch (key) { - case "ArrowLeft": - inputManager.handle("left"); + case 'ArrowLeft': + inputManager.handle('left'); break; - case "ArrowUp": - inputManager.handle("up"); + case 'ArrowUp': + inputManager.handle('up'); break; - case "ArrowRight": - inputManager.handle("right"); + case 'ArrowRight': + inputManager.handle('right'); break; - case "ArrowDown": - inputManager.handle("down"); + case 'ArrowDown': + inputManager.handle('down'); break; - case "Back": - inputManager.handle("back"); + case 'Back': + inputManager.handle('back'); break; - case "Escape": + case 'Escape': if (layoutManager.tv) { - inputManager.handle("back"); + inputManager.handle('back'); } else { capture = false; } break; - case "MediaPlay": - inputManager.handle("play"); + case 'MediaPlay': + inputManager.handle('play'); break; - case "Pause": - inputManager.handle("pause"); + case 'Pause': + inputManager.handle('pause'); break; - case "MediaPlayPause": - inputManager.handle("playpause"); + case 'MediaPlayPause': + inputManager.handle('playpause'); break; - case "MediaRewind": - inputManager.handle("rewind"); + case 'MediaRewind': + inputManager.handle('rewind'); break; - case "MediaFastForward": - inputManager.handle("fastforward"); + case 'MediaFastForward': + inputManager.handle('fastforward'); break; - case "MediaStop": - inputManager.handle("stop"); + case 'MediaStop': + inputManager.handle('stop'); break; - case "MediaTrackPrevious": - inputManager.handle("previoustrack"); + case 'MediaTrackPrevious': + inputManager.handle('previoustrack'); break; - case "MediaTrackNext": - inputManager.handle("nexttrack"); + case 'MediaTrackNext': + inputManager.handle('nexttrack'); break; default: @@ -144,7 +144,7 @@ export function enable() { } if (capture) { - console.debug("disabling default event handling"); + console.debug('disabling default event handling'); e.preventDefault(); } }); @@ -153,13 +153,15 @@ export function enable() { // Gamepad initialisation. No script is required if no gamepads are present at init time, saving a bit of resources. // Whenever the gamepad is connected, we hand all the control of the gamepad to gamepadtokey.js by removing the event handler function attachGamepadScript(e) { - console.log("Gamepad connected! Attaching gamepadtokey.js script"); - window.removeEventListener("gamepadconnected", attachGamepadScript); - require(["components/input/gamepadtokey"]); + console.log('Gamepad connected! Attaching gamepadtokey.js script'); + window.removeEventListener('gamepadconnected', attachGamepadScript); + require(['scripts/gamepadtokey']); } // No need to check for gamepads manually at load time, the eventhandler will be fired for that -window.addEventListener("gamepadconnected", attachGamepadScript); +if (navigator.getGamepads) { /* eslint-disable-line compat/compat */ + window.addEventListener('gamepadconnected', attachGamepadScript); +} export default { enable: enable, diff --git a/src/scripts/librarybrowser.js b/src/scripts/libraryBrowser.js similarity index 65% rename from src/scripts/librarybrowser.js rename to src/scripts/libraryBrowser.js index bc8908fe6c..f9e5e23596 100644 --- a/src/scripts/librarybrowser.js +++ b/src/scripts/libraryBrowser.js @@ -1,9 +1,9 @@ -define(["userSettings"], function (userSettings) { - "use strict"; +define(['userSettings', 'globalize'], function (userSettings, globalize) { + 'use strict'; var libraryBrowser = { getSavedQueryKey: function (modifier) { - return window.location.href.split("#")[0] + (modifier || ""); + return window.location.href.split('#')[0] + (modifier || ''); }, loadSavedQueryValues: function (key, query) { var values = userSettings.get(key); @@ -29,34 +29,34 @@ define(["userSettings"], function (userSettings) { userSettings.set(key, JSON.stringify(values)); }, saveViewSetting: function (key, value) { - userSettings.set(key + "-_view", value); + userSettings.set(key + '-_view', value); }, getSavedView: function (key) { - return userSettings.get(key + "-_view"); + return userSettings.get(key + '-_view'); }, showLayoutMenu: function (button, currentLayout, views) { var dispatchEvent = true; if (!views) { dispatchEvent = false; - views = button.getAttribute("data-layouts"); - views = views ? views.split(",") : ["List", "Poster", "PosterCard", "Thumb", "ThumbCard"]; + views = button.getAttribute('data-layouts'); + views = views ? views.split(',') : ['List', 'Poster', 'PosterCard', 'Thumb', 'ThumbCard']; } var menuItems = views.map(function (v) { return { - name: Globalize.translate("Option" + v), + name: globalize.translate('Option' + v), id: v, selected: currentLayout == v }; }); - require(["actionsheet"], function (actionsheet) { + require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, positionTo: button, callback: function (id) { - button.dispatchEvent(new CustomEvent("layoutchange", { + button.dispatchEvent(new CustomEvent('layoutchange', { detail: { viewStyle: id }, @@ -66,7 +66,7 @@ define(["userSettings"], function (userSettings) { if (!dispatchEvent) { if (window.$) { - $(button).trigger("layoutchange", [id]); + $(button).trigger('layoutchange', [id]); } } } @@ -77,49 +77,49 @@ define(["userSettings"], function (userSettings) { var startIndex = options.startIndex; var limit = options.limit; var totalRecordCount = options.totalRecordCount; - var html = ""; + var html = ''; var recordsEnd = Math.min(startIndex + limit, totalRecordCount); var showControls = limit < totalRecordCount; if (html += '
', showControls) { html += ''; - html += Globalize.translate("ListPaging", (totalRecordCount ? startIndex + 1 : 0), recordsEnd, totalRecordCount); - html += ""; + html += globalize.translate('ListPaging', (totalRecordCount ? startIndex + 1 : 0), recordsEnd, totalRecordCount); + html += ''; } if (showControls || options.viewButton || options.filterButton || options.sortButton || options.addLayoutButton) { html += '
'; if (showControls) { - html += ''; - html += ''; + html += ''; + html += ''; } if (options.addLayoutButton) { - html += ''; + html += ''; } if (options.sortButton) { - html += ''; + html += ''; } if (options.filterButton) { - html += ''; + html += ''; } - html += "
"; + html += '
'; } - return html += "
"; + return html += '
'; }, showSortMenu: function (options) { - require(["dialogHelper", "emby-radio"], function (dialogHelper) { + require(['dialogHelper', 'emby-radio'], function (dialogHelper) { function onSortByChange() { var newValue = this.value; if (this.checked) { var changed = options.query.SortBy != newValue; - options.query.SortBy = newValue.replace("_", ","); + options.query.SortBy = newValue.replace('_', ','); options.query.StartIndex = 0; if (options.callback && changed) { @@ -148,48 +148,48 @@ define(["userSettings"], function (userSettings) { entryAnimationDuration: 160, exitAnimationDuration: 200 }); - dlg.classList.add("ui-body-a"); - dlg.classList.add("background-theme-a"); - dlg.classList.add("formDialog"); - var html = ""; + dlg.classList.add('ui-body-a'); + dlg.classList.add('background-theme-a'); + dlg.classList.add('formDialog'); + var html = ''; html += '
'; html += '

'; - html += Globalize.translate("HeaderSortBy"); - html += "

"; + html += globalize.translate('HeaderSortBy'); + html += ''; var i; var length; var isChecked; html += '
'; for (i = 0, length = options.items.length; i < length; i++) { var option = options.items[i]; - var radioValue = option.id.replace(",", "_"); - isChecked = (options.query.SortBy || "").replace(",", "_") == radioValue ? " checked" : ""; - html += '"; + var radioValue = option.id.replace(',', '_'); + isChecked = (options.query.SortBy || '').replace(',', '_') == radioValue ? ' checked' : ''; + html += ''; } - html += "
"; + html += '
'; html += '

'; - html += Globalize.translate("HeaderSortOrder"); - html += "

"; - html += "
"; - isChecked = "Ascending" == options.query.SortOrder ? " checked" : ""; - html += '"; - isChecked = "Descending" == options.query.SortOrder ? " checked" : ""; - html += '"; - html += "
"; - html += "
"; + html += globalize.translate('HeaderSortOrder'); + html += ''; + html += '
'; + isChecked = 'Ascending' == options.query.SortOrder ? ' checked' : ''; + html += ''; + isChecked = 'Descending' == options.query.SortOrder ? ' checked' : ''; + html += ''; + html += '
'; + html += '
'; dlg.innerHTML = html; dialogHelper.open(dlg); - var sortBys = dlg.querySelectorAll(".menuSortBy"); + var sortBys = dlg.querySelectorAll('.menuSortBy'); for (i = 0, length = sortBys.length; i < length; i++) { - sortBys[i].addEventListener("change", onSortByChange); + sortBys[i].addEventListener('change', onSortByChange); } - var sortOrders = dlg.querySelectorAll(".menuSortOrder"); + var sortOrders = dlg.querySelectorAll('.menuSortOrder'); for (i = 0, length = sortOrders.length; i < length; i++) { - sortOrders[i].addEventListener("change", onSortOrderChange); + sortOrders[i].addEventListener('change', onSortOrderChange); } }); } diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js new file mode 100644 index 0000000000..937ba42795 --- /dev/null +++ b/src/scripts/libraryMenu.js @@ -0,0 +1,973 @@ +define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', 'viewManager', 'libraryBrowser', 'appRouter', 'apphost', 'playbackManager', 'syncPlayManager', 'groupSelectionMenu', 'browser', 'globalize', 'scripts/imagehelper', 'paper-icon-button-light', 'material-icons', 'scrollStyles', 'flexStyles'], function (dom, layoutManager, inputManager, connectionManager, events, viewManager, libraryBrowser, appRouter, appHost, playbackManager, syncPlayManager, groupSelectionMenu, browser, globalize, imageHelper) { + 'use strict'; + + function renderHeader() { + var html = ''; + html += '
'; + html += '
'; + html += ''; + html += ''; + html += ''; + html += '

'; + html += '
'; + html += '
'; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += '
'; + html += '
'; + html += '
'; + html += '
'; + + skinHeader.classList.add('skinHeader-withBackground'); + skinHeader.classList.add('skinHeader-blurred'); + skinHeader.innerHTML = html; + + headerHomeButton = skinHeader.querySelector('.headerHomeButton'); + headerUserButton = skinHeader.querySelector('.headerUserButton'); + headerCastButton = skinHeader.querySelector('.headerCastButton'); + headerAudioPlayerButton = skinHeader.querySelector('.headerAudioPlayerButton'); + headerSearchButton = skinHeader.querySelector('.headerSearchButton'); + headerSyncButton = skinHeader.querySelector('.headerSyncButton'); + + lazyLoadViewMenuBarImages(); + bindMenuEvents(); + } + + function getCurrentApiClient() { + if (currentUser && currentUser.localUser) { + return connectionManager.getApiClient(currentUser.localUser.ServerId); + } + + return connectionManager.currentApiClient(); + } + + function lazyLoadViewMenuBarImages() { + require(['imageLoader'], function (imageLoader) { + imageLoader.lazyChildren(skinHeader); + }); + } + + function onBackClick() { + appRouter.back(); + } + + function updateUserInHeader(user) { + var hasImage; + + if (user && user.name) { + if (user.imageUrl) { + var url = user.imageUrl; + updateHeaderUserButton(url); + hasImage = true; + } + + headerUserButton.classList.remove('hide'); + } else { + headerUserButton.classList.add('hide'); + } + + if (!hasImage) { + updateHeaderUserButton(null); + } + + if (user && user.localUser) { + if (headerHomeButton) { + headerHomeButton.classList.remove('hide'); + } + + if (headerSearchButton) { + headerSearchButton.classList.remove('hide'); + } + + if (!layoutManager.tv) { + headerCastButton.classList.remove('hide'); + } + + var policy = user.Policy ? user.Policy : user.localUser.Policy; + + if (headerSyncButton && policy && policy.SyncPlayAccess !== 'None') { + headerSyncButton.classList.remove('hide'); + } + } else { + headerHomeButton.classList.add('hide'); + headerCastButton.classList.add('hide'); + headerSyncButton.classList.add('hide'); + + if (headerSearchButton) { + headerSearchButton.classList.add('hide'); + } + } + + requiresUserRefresh = false; + } + + function updateHeaderUserButton(src) { + if (src) { + headerUserButton.classList.add('headerUserButtonRound'); + headerUserButton.innerHTML = '
"; + } else { + headerUserButton.classList.remove('headerUserButtonRound'); + headerUserButton.innerHTML = ''; + } + } + + function showSearch() { + inputManager.trigger('search'); + } + + function onHeaderUserButtonClick(e) { + Dashboard.navigate('mypreferencesmenu.html'); + } + + function onHeaderHomeButtonClick() { + Dashboard.navigate('home.html'); + } + + function showAudioPlayer() { + return appRouter.showNowPlaying(); + } + + function bindMenuEvents() { + mainDrawerButton = document.querySelector('.mainDrawerButton'); + + if (mainDrawerButton) { + mainDrawerButton.addEventListener('click', toggleMainDrawer); + } + + var headerBackButton = skinHeader.querySelector('.headerBackButton'); + + if (headerBackButton) { + headerBackButton.addEventListener('click', onBackClick); + } + + if (headerSearchButton) { + headerSearchButton.addEventListener('click', showSearch); + } + + headerUserButton.addEventListener('click', onHeaderUserButtonClick); + headerHomeButton.addEventListener('click', onHeaderHomeButtonClick); + + if (!layoutManager.tv) { + headerCastButton.addEventListener('click', onCastButtonClicked); + } + + headerAudioPlayerButton.addEventListener('click', showAudioPlayer); + headerSyncButton.addEventListener('click', onSyncButtonClicked); + + if (layoutManager.mobile) { + initHeadRoom(skinHeader); + } + events.on(playbackManager, 'playbackstart', onPlaybackStart); + events.on(playbackManager, 'playbackstop', onPlaybackStop); + } + + function onPlaybackStart(e) { + if (playbackManager.isPlayingAudio() && layoutManager.tv) { + headerAudioPlayerButton.classList.remove('hide'); + } else { + headerAudioPlayerButton.classList.add('hide'); + } + } + + function onPlaybackStop(e, stopInfo) { + if (stopInfo.nextMediaType != 'Audio') { + headerAudioPlayerButton.classList.add('hide'); + } + } + + function onCastButtonClicked() { + var btn = this; + + require(['playerSelectionMenu'], function (playerSelectionMenu) { + playerSelectionMenu.show(btn); + }); + } + + function onSyncButtonClicked() { + var btn = this; + groupSelectionMenu.show(btn); + } + + function onSyncPlayEnabled(event, enabled) { + var icon = headerSyncButton.querySelector('span'); + icon.classList.remove('sync', 'sync_disabled', 'sync_problem'); + if (enabled) { + icon.classList.add('sync'); + } else { + icon.classList.add('sync_disabled'); + } + } + + function onSyncPlaySyncing(event, is_syncing, syncMethod) { + var icon = headerSyncButton.querySelector('span'); + icon.classList.remove('sync', 'sync_disabled', 'sync_problem'); + if (is_syncing) { + icon.classList.add('sync_problem'); + } else { + icon.classList.add('sync'); + } + } + + function getItemHref(item, context) { + return appRouter.getRouteUrl(item, { + context: context + }); + } + + function toggleMainDrawer() { + if (navDrawerInstance.isVisible) { + closeMainDrawer(); + } else { + openMainDrawer(); + } + } + + function openMainDrawer() { + navDrawerInstance.open(); + lastOpenTime = new Date().getTime(); + } + + function onMainDrawerOpened() { + if (layoutManager.mobile) { + document.body.classList.add('bodyWithPopupOpen'); + } + } + + function closeMainDrawer() { + navDrawerInstance.close(); + } + + function onMainDrawerSelect(e) { + if (navDrawerInstance.isVisible) { + onMainDrawerOpened(); + } else { + document.body.classList.remove('bodyWithPopupOpen'); + } + } + + function refreshLibraryInfoInDrawer(user, drawer) { + var html = ''; + html += '
'; + html += '' + globalize.translate('ButtonHome') + ''; + + // libraries are added here + html += '
'; + html += '
'; + + if (user.localUser && user.localUser.Policy.IsAdministrator) { + html += '
'; + html += '

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

'; + html += '' + globalize.translate('TabDashboard') + ''; + html += '' + globalize.translate('Metadata') + ''; + html += '
'; + } + + if (user.localUser) { + html += '
'; + html += '

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

'; + + if (appHost.supports('multiserver')) { + html += '' + globalize.translate('ButtonSelectServer') + ''; + } + + html += '' + globalize.translate('ButtonSettings') + ''; + html += '' + globalize.translate('ButtonSignOut') + ''; + html += '
'; + } + + // add buttons to navigation drawer + navDrawerScrollContainer.innerHTML = html; + + var btnSettings = navDrawerScrollContainer.querySelector('.btnSettings'); + if (btnSettings) { + btnSettings.addEventListener('click', onSettingsClick); + } + + var btnLogout = navDrawerScrollContainer.querySelector('.btnLogout'); + if (btnLogout) { + btnLogout.addEventListener('click', onLogoutClick); + } + } + + function refreshDashboardInfoInDrawer(apiClient) { + currentDrawerType = 'admin'; + loadNavDrawer(); + + if (navDrawerScrollContainer.querySelector('.adminDrawerLogo')) { + updateDashboardMenuSelectedItem(); + } else { + createDashboardMenu(apiClient); + } + } + + function isUrlInCurrentView(url) { + return -1 !== window.location.href.toString().toLowerCase().indexOf(url.toLowerCase()); + } + + function updateDashboardMenuSelectedItem() { + var links = navDrawerScrollContainer.querySelectorAll('.navMenuOption'); + var currentViewId = viewManager.currentView().id; + + for (var i = 0, length = links.length; i < length; i++) { + var link = links[i]; + var selected = false; + var pageIds = link.getAttribute('data-pageids'); + + if (pageIds) { + pageIds = pageIds.split('|'); + selected = -1 != pageIds.indexOf(currentViewId); + } + + var pageUrls = link.getAttribute('data-pageurls'); + + if (pageUrls) { + pageUrls = pageUrls.split('|'); + selected = pageUrls.filter(isUrlInCurrentView).length > 0; + } + + if (selected) { + link.classList.add('navMenuOption-selected'); + var title = ''; + link = link.querySelector('.navMenuOptionText') || link; + title += (link.innerText || link.textContent).trim(); + LibraryMenu.setTitle(title); + } else { + link.classList.remove('navMenuOption-selected'); + } + } + } + + function createToolsMenuList(pluginItems) { + var links = [{ + name: globalize.translate('TabServer') + }, { + name: globalize.translate('TabDashboard'), + href: 'dashboard.html', + pageIds: ['dashboardPage'], + icon: 'dashboard' + }, { + name: globalize.translate('General'), + href: 'dashboardgeneral.html', + pageIds: ['dashboardGeneralPage'], + icon: 'settings' + }, { + name: globalize.translate('TabUsers'), + href: 'userprofiles.html', + pageIds: ['userProfilesPage', 'newUserPage', 'editUserPage', 'userLibraryAccessPage', 'userParentalControlPage', 'userPasswordPage'], + icon: 'people' + }, { + name: globalize.translate('HeaderLibraries'), + href: 'library.html', + pageIds: ['mediaLibraryPage', 'librarySettingsPage', 'libraryDisplayPage', 'metadataImagesConfigurationPage', 'metadataNfoPage'], + icon: 'folder' + }, { + name: globalize.translate('TabPlayback'), + icon: 'play_arrow', + href: 'encodingsettings.html', + pageIds: ['encodingSettingsPage', 'playbackConfigurationPage', 'streamingSettingsPage'] + }]; + addPluginPagesToMainMenu(links, pluginItems, 'server'); + links.push({ + divider: true, + name: globalize.translate('TabDevices') + }); + links.push({ + name: globalize.translate('TabDevices'), + href: 'devices.html', + pageIds: ['devicesPage', 'devicePage'], + icon: 'devices' + }); + links.push({ + name: globalize.translate('HeaderActivity'), + href: 'serveractivity.html', + pageIds: ['serverActivityPage'], + icon: 'assessment' + }); + links.push({ + name: globalize.translate('DLNA'), + href: 'dlnasettings.html', + pageIds: ['dlnaSettingsPage', 'dlnaProfilesPage', 'dlnaProfilePage'], + icon: 'input' + }); + links.push({ + divider: true, + name: globalize.translate('TabLiveTV') + }); + links.push({ + name: globalize.translate('TabLiveTV'), + href: 'livetvstatus.html', + pageIds: ['liveTvStatusPage', 'liveTvTunerPage'], + icon: 'live_tv' + }); + links.push({ + name: globalize.translate('TabDVR'), + href: 'livetvsettings.html', + pageIds: ['liveTvSettingsPage'], + icon: 'dvr' + }); + links.push({ + divider: true, + name: globalize.translate('TabAdvanced') + }); + links.push({ + name: globalize.translate('TabNetworking'), + icon: 'cloud', + href: 'networking.html', + pageIds: ['networkingPage'] + }); + links.push({ + name: globalize.translate('HeaderApiKeys'), + icon: 'vpn_key', + href: 'apikeys.html', + pageIds: ['apiKeysPage'] + }); + links.push({ + name: globalize.translate('TabLogs'), + href: 'log.html', + pageIds: ['logPage'], + icon: 'bug_report' + }); + links.push({ + name: globalize.translate('TabNotifications'), + icon: 'notifications', + href: 'notificationsettings.html', + pageIds: ['notificationSettingsPage', 'notificationSettingPage'] + }); + links.push({ + name: globalize.translate('TabPlugins'), + icon: 'shopping_cart', + href: 'installedplugins.html', + pageIds: ['pluginsPage', 'pluginCatalogPage'] + }); + links.push({ + name: globalize.translate('TabScheduledTasks'), + href: 'scheduledtasks.html', + pageIds: ['scheduledTasksPage', 'scheduledTaskPage'], + icon: 'schedule' + }); + addPluginPagesToMainMenu(links, pluginItems); + return links; + } + + function addPluginPagesToMainMenu(links, pluginItems, section) { + for (var i = 0, length = pluginItems.length; i < length; i++) { + var pluginItem = pluginItems[i]; + + if (pluginItem.EnableInMainMenu && pluginItem.MenuSection === section) { + links.push({ + name: pluginItem.DisplayName, + icon: pluginItem.MenuIcon || 'folder', + href: Dashboard.getConfigurationPageUrl(pluginItem.Name), + pageUrls: [Dashboard.getConfigurationPageUrl(pluginItem.Name)] + }); + } + } + } + + function getToolsMenuLinks(apiClient) { + return apiClient.getJSON(apiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration&EnableInMainMenu=true').then(createToolsMenuList, function (err) { + return createToolsMenuList([]); + }); + } + + function getToolsLinkHtml(item) { + var menuHtml = ''; + var pageIds = item.pageIds ? item.pageIds.join('|') : ''; + pageIds = pageIds ? ' data-pageids="' + pageIds + '"' : ''; + var pageUrls = item.pageUrls ? item.pageUrls.join('|') : ''; + pageUrls = pageUrls ? ' data-pageurls="' + pageUrls + '"' : ''; + menuHtml += ''; + + if (item.icon) { + menuHtml += ''; + } + + menuHtml += ''; + menuHtml += item.name; + menuHtml += ''; + return menuHtml + ''; + } + + function getToolsMenuHtml(apiClient) { + return getToolsMenuLinks(apiClient).then(function (items) { + var item; + var menuHtml = ''; + menuHtml += '
'; + + for (var i = 0; i < items.length; i++) { + item = items[i]; + + if (item.href) { + menuHtml += getToolsLinkHtml(item); + } else if (item.name) { + menuHtml += '

'; + menuHtml += item.name; + menuHtml += '

'; + } + } + + return menuHtml + '
'; + }); + } + + function createDashboardMenu(apiClient) { + return getToolsMenuHtml(apiClient).then(function (toolsMenuHtml) { + var html = ''; + html += ''; + html += toolsMenuHtml; + navDrawerScrollContainer.innerHTML = html; + updateDashboardMenuSelectedItem(); + }); + } + + function onSidebarLinkClick() { + var section = this.getElementsByClassName('sectionName')[0]; + var text = section ? section.innerHTML : this.innerHTML; + LibraryMenu.setTitle(text); + } + + function getUserViews(apiClient, userId) { + return apiClient.getUserViews({}, userId).then(function (result) { + var items = result.Items; + var list = []; + + for (var i = 0, length = items.length; i < length; i++) { + var view = items[i]; + list.push(view); + + if ('livetv' == view.CollectionType) { + view.ImageTags = {}; + view.icon = 'live_tv'; + var guideView = Object.assign({}, view); + guideView.Name = globalize.translate('ButtonGuide'); + guideView.ImageTags = {}; + guideView.icon = 'dvr'; + guideView.url = 'livetv.html?tab=1'; + list.push(guideView); + } + } + + return list; + }); + } + + function showBySelector(selector, show) { + var elem = document.querySelector(selector); + + if (elem) { + if (show) { + elem.classList.remove('hide'); + } else { + elem.classList.add('hide'); + } + } + } + + function updateLibraryMenu(user) { + // FIXME: Potential equivalent might be + // showBySelector(".lnkSyncToOtherDevices", !!user.Policy.EnableContentDownloading); + if (!user) { + showBySelector('.libraryMenuDownloads', false); + showBySelector('.lnkSyncToOtherDevices', false); + return void showBySelector('.userMenuOptions', false); + } + + // FIXME: Potentially the same as above + if (user.Policy.EnableContentDownloading) { + showBySelector('.lnkSyncToOtherDevices', true); + } else { + showBySelector('.lnkSyncToOtherDevices', false); + } + + if (user.Policy.EnableContentDownloading && appHost.supports('sync')) { + showBySelector('.libraryMenuDownloads', true); + } else { + showBySelector('.libraryMenuDownloads', false); + } + + var userId = Dashboard.getCurrentUserId(); + var apiClient = getCurrentApiClient(); + var libraryMenuOptions = document.querySelector('.libraryMenuOptions'); + + if (libraryMenuOptions) { + getUserViews(apiClient, userId).then(function (result) { + var items = result; + var html = `

${globalize.translate('HeaderMedia')}

`; + html += items.map(function (i) { + var icon = i.icon || imageHelper.getLibraryIcon(i.CollectionType); + var itemId = i.Id; + + const linkHtml = ` + + ${i.Name} + `; + + return linkHtml; + }).join(''); + libraryMenuOptions.innerHTML = html; + var elem = libraryMenuOptions; + var sidebarLinks = elem.querySelectorAll('.navMenuOption'); + + for (const sidebarLink of sidebarLinks) { + sidebarLink.removeEventListener('click', onSidebarLinkClick); + sidebarLink.addEventListener('click', onSidebarLinkClick); + } + }); + } + } + + function getTopParentId() { + return getParameterByName('topParentId') || null; + } + + function onMainDrawerClick(e) { + if (dom.parentWithTag(e.target, 'A')) { + setTimeout(closeMainDrawer, 30); + } + } + + function onSettingsClick() { + Dashboard.navigate('mypreferencesmenu.html'); + } + + function onLogoutClick() { + Dashboard.logout(); + } + + function updateCastIcon() { + var context = document; + var info = playbackManager.getPlayerInfo(); + var icon = headerCastButton.querySelector('.material-icons'); + + icon.classList.remove('cast_connected', 'cast'); + + if (info && !info.isLocalPlayer) { + icon.classList.add('cast_connected'); + headerCastButton.classList.add('castButton-active'); + context.querySelector('.headerSelectedPlayer').innerHTML = info.deviceName || info.name; + } else { + icon.classList.add('cast'); + headerCastButton.classList.remove('castButton-active'); + context.querySelector('.headerSelectedPlayer').innerHTML = ''; + } + } + + function updateLibraryNavLinks(page) { + var i; + var length; + var isLiveTvPage = page.classList.contains('liveTvPage'); + var isChannelsPage = page.classList.contains('channelsPage'); + var isEditorPage = page.classList.contains('metadataEditorPage'); + var isMySyncPage = page.classList.contains('mySyncPage'); + var id = isLiveTvPage || isChannelsPage || isEditorPage || isMySyncPage || page.classList.contains('allLibraryPage') ? '' : getTopParentId() || ''; + var elems = document.getElementsByClassName('lnkMediaFolder'); + + for (var i = 0, length = elems.length; i < length; i++) { + var lnkMediaFolder = elems[i]; + var itemId = lnkMediaFolder.getAttribute('data-itemid'); + + if (isChannelsPage && 'channels' === itemId) { + lnkMediaFolder.classList.add('navMenuOption-selected'); + } else if (isLiveTvPage && 'livetv' === itemId) { + lnkMediaFolder.classList.add('navMenuOption-selected'); + } else if (isEditorPage && 'editor' === itemId) { + lnkMediaFolder.classList.add('navMenuOption-selected'); + } else if (isMySyncPage && 'manageoffline' === itemId && -1 != window.location.href.toString().indexOf('mode=download')) { + lnkMediaFolder.classList.add('navMenuOption-selected'); + } else if (isMySyncPage && 'syncotherdevices' === itemId && -1 == window.location.href.toString().indexOf('mode=download')) { + lnkMediaFolder.classList.add('navMenuOption-selected'); + } else if (id && itemId == id) { + lnkMediaFolder.classList.add('navMenuOption-selected'); + } else { + lnkMediaFolder.classList.remove('navMenuOption-selected'); + } + } + } + + function updateMenuForPageType(isDashboardPage, isLibraryPage) { + var newPageType = isDashboardPage ? 2 : isLibraryPage ? 1 : 3; + + if (currentPageType !== newPageType) { + currentPageType = newPageType; + + if (isDashboardPage && !layoutManager.mobile) { + skinHeader.classList.add('headroomDisabled'); + } else { + skinHeader.classList.remove('headroomDisabled'); + } + + var bodyClassList = document.body.classList; + + if (isLibraryPage) { + bodyClassList.add('libraryDocument'); + bodyClassList.remove('dashboardDocument'); + bodyClassList.remove('hideMainDrawer'); + + if (navDrawerInstance) { + navDrawerInstance.setEdgeSwipeEnabled(true); + } + } else { + if (isDashboardPage) { + bodyClassList.remove('libraryDocument'); + bodyClassList.add('dashboardDocument'); + bodyClassList.remove('hideMainDrawer'); + + if (navDrawerInstance) { + navDrawerInstance.setEdgeSwipeEnabled(true); + } + } else { + bodyClassList.remove('libraryDocument'); + bodyClassList.remove('dashboardDocument'); + bodyClassList.add('hideMainDrawer'); + + if (navDrawerInstance) { + navDrawerInstance.setEdgeSwipeEnabled(false); + } + } + } + } + + if (requiresUserRefresh) { + connectionManager.user(getCurrentApiClient()).then(updateUserInHeader); + } + } + + function updateTitle(page) { + var title = page.getAttribute('data-title'); + + if (title) { + LibraryMenu.setTitle(title); + } else if (page.classList.contains('standalonePage')) { + LibraryMenu.setDefaultTitle(); + } + } + + function updateBackButton(page) { + if (!headerBackButton) { + headerBackButton = document.querySelector('.headerBackButton'); + } + + if (headerBackButton) { + if ('false' !== page.getAttribute('data-backbutton') && appRouter.canGoBack()) { + headerBackButton.classList.remove('hide'); + } else { + headerBackButton.classList.add('hide'); + } + } + } + + function initHeadRoom(elem) { + require(['headroom'], function (Headroom) { + var headroom = new Headroom(elem); + headroom.init(); + }); + } + + function refreshLibraryDrawer(user) { + loadNavDrawer(); + currentDrawerType = 'library'; + + if (user) { + Promise.resolve(user); + } else { + connectionManager.user(getCurrentApiClient()).then(function (user) { + refreshLibraryInfoInDrawer(user); + updateLibraryMenu(user.localUser); + }); + } + } + + function getNavDrawerOptions() { + var drawerWidth = screen.availWidth - 50; + drawerWidth = Math.max(drawerWidth, 240); + drawerWidth = Math.min(drawerWidth, 320); + return { + target: navDrawerElement, + onChange: onMainDrawerSelect, + width: drawerWidth + }; + } + + function loadNavDrawer() { + if (navDrawerInstance) { + return Promise.resolve(navDrawerInstance); + } + + navDrawerElement = document.querySelector('.mainDrawer'); + navDrawerScrollContainer = navDrawerElement.querySelector('.scrollContainer'); + navDrawerScrollContainer.addEventListener('click', onMainDrawerClick); + return new Promise(function (resolve, reject) { + require(['navdrawer'], function (navdrawer) { + navDrawerInstance = new navdrawer(getNavDrawerOptions()); + + if (!layoutManager.tv) { + navDrawerElement.classList.remove('hide'); + } + + resolve(navDrawerInstance); + }); + }); + } + + var navDrawerElement; + var navDrawerScrollContainer; + var navDrawerInstance; + var mainDrawerButton; + var headerHomeButton; + var currentDrawerType; + var pageTitleElement; + var headerBackButton; + var headerUserButton; + var currentUser; + var headerCastButton; + var headerSearchButton; + var headerAudioPlayerButton; + var headerSyncButton; + var enableLibraryNavDrawer = layoutManager.desktop; + var skinHeader = document.querySelector('.skinHeader'); + var requiresUserRefresh = true; + var lastOpenTime = new Date().getTime(); + window.LibraryMenu = { + getTopParentId: getTopParentId, + onHardwareMenuButtonClick: function () { + toggleMainDrawer(); + }, + setTabs: function (type, selectedIndex, builder) { + require(['mainTabsManager'], function (mainTabsManager) { + if (type) { + mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () { + return []; + }); + } else { + mainTabsManager.setTabs(null); + } + }); + }, + setDefaultTitle: function () { + if (!pageTitleElement) { + pageTitleElement = document.querySelector('.pageTitle'); + } + + if (pageTitleElement) { + pageTitleElement.classList.add('pageTitleWithLogo'); + pageTitleElement.classList.add('pageTitleWithDefaultLogo'); + pageTitleElement.style.backgroundImage = null; + pageTitleElement.innerHTML = ''; + } + + document.title = 'Jellyfin'; + }, + setTitle: function (title) { + if (null == title) { + return void LibraryMenu.setDefaultTitle(); + } + + if ('-' === title) { + title = ''; + } + + var html = title; + + if (!pageTitleElement) { + pageTitleElement = document.querySelector('.pageTitle'); + } + + if (pageTitleElement) { + pageTitleElement.classList.remove('pageTitleWithLogo'); + pageTitleElement.classList.remove('pageTitleWithDefaultLogo'); + pageTitleElement.style.backgroundImage = null; + pageTitleElement.innerHTML = html || ''; + } + + document.title = title || 'Jellyfin'; + }, + setTransparentMenu: function (transparent) { + if (transparent) { + skinHeader.classList.add('semiTransparent'); + } else { + skinHeader.classList.remove('semiTransparent'); + } + } + }; + var currentPageType; + pageClassOn('pagebeforeshow', 'page', function (e) { + if (!this.classList.contains('withTabs')) { + LibraryMenu.setTabs(null); + } + }); + pageClassOn('pageshow', 'page', function (e) { + var page = this; + var isDashboardPage = page.classList.contains('type-interior'); + var isHomePage = page.classList.contains('homePage'); + var isLibraryPage = !isDashboardPage && page.classList.contains('libraryPage'); + var apiClient = getCurrentApiClient(); + + if (isDashboardPage) { + if (mainDrawerButton) { + mainDrawerButton.classList.remove('hide'); + } + + refreshDashboardInfoInDrawer(apiClient); + } else { + if (mainDrawerButton) { + if (enableLibraryNavDrawer || isHomePage) { + mainDrawerButton.classList.remove('hide'); + } else { + mainDrawerButton.classList.add('hide'); + } + } + + if ('library' !== currentDrawerType) { + refreshLibraryDrawer(); + } + } + + updateMenuForPageType(isDashboardPage, isLibraryPage); + + // TODO: Seems to do nothing? Check if needed (also in other views). + if (!e.detail.isRestored) { + window.scrollTo(0, 0); + } + + updateTitle(page); + updateBackButton(page); + updateLibraryNavLinks(page); + }); + + renderHeader(); + + events.on(connectionManager, 'localusersignedin', function (e, user) { + var currentApiClient = connectionManager.getApiClient(user.ServerId); + + currentDrawerType = null; + currentUser = { + localUser: user + }; + + loadNavDrawer(); + + connectionManager.user(currentApiClient).then(function (user) { + currentUser = user; + updateUserInHeader(user); + }); + }); + events.on(connectionManager, 'localusersignedout', function () { + currentUser = {}; + updateUserInHeader(); + }); + events.on(playbackManager, 'playerchange', updateCastIcon); + events.on(syncPlayManager, 'enabled', onSyncPlayEnabled); + events.on(syncPlayManager, 'syncing', onSyncPlaySyncing); + loadNavDrawer(); + return LibraryMenu; +}); diff --git a/src/scripts/librarymenu.js b/src/scripts/librarymenu.js deleted file mode 100644 index 11c89b01da..0000000000 --- a/src/scripts/librarymenu.js +++ /dev/null @@ -1,930 +0,0 @@ -define(["dom", "layoutManager", "inputManager", "connectionManager", "events", "viewManager", "libraryBrowser", "appRouter", "apphost", "playbackManager", "browser", "globalize", "scripts/imagehelper", "paper-icon-button-light", "material-icons", "scrollStyles", "flexStyles"], function (dom, layoutManager, inputManager, connectionManager, events, viewManager, libraryBrowser, appRouter, appHost, playbackManager, browser, globalize, imageHelper) { - "use strict"; - - function renderHeader() { - var html = ""; - html += '
'; - html += '
'; - html += ''; - html += ''; - html += ''; - html += '

'; - html += "
"; - html += '
'; - html += ''; - html += ''; - html += ''; - html += ''; - html += ''; - html += "
"; - html += "
"; - html += '
'; - html += "
"; - - skinHeader.classList.add("skinHeader-withBackground"); - skinHeader.classList.add("skinHeader-blurred"); - skinHeader.innerHTML = html; - - headerHomeButton = skinHeader.querySelector(".headerHomeButton"); - headerUserButton = skinHeader.querySelector(".headerUserButton"); - headerCastButton = skinHeader.querySelector(".headerCastButton"); - headerAudioPlayerButton = skinHeader.querySelector(".headerAudioPlayerButton"); - headerSearchButton = skinHeader.querySelector(".headerSearchButton"); - - lazyLoadViewMenuBarImages(); - bindMenuEvents(); - } - - function getCurrentApiClient() { - if (currentUser && currentUser.localUser) { - return connectionManager.getApiClient(currentUser.localUser.ServerId); - } - - return connectionManager.currentApiClient(); - } - - function lazyLoadViewMenuBarImages() { - require(["imageLoader"], function (imageLoader) { - imageLoader.lazyChildren(skinHeader); - }); - } - - function onBackClick() { - appRouter.back(); - } - - function updateUserInHeader(user) { - var hasImage; - - if (user && user.name) { - if (user.imageUrl) { - var url = user.imageUrl; - updateHeaderUserButton(url); - hasImage = true; - } - - headerUserButton.classList.remove("hide"); - } else { - headerUserButton.classList.add("hide"); - } - - if (!hasImage) { - updateHeaderUserButton(null); - } - - if (user && user.localUser) { - if (headerHomeButton && !layoutManager.mobile) { - headerHomeButton.classList.remove("hide"); - } - - if (headerSearchButton) { - headerSearchButton.classList.remove("hide"); - } - - if (!layoutManager.tv) { - headerCastButton.classList.remove("hide"); - } - } else { - headerHomeButton.classList.add("hide"); - headerCastButton.classList.add("hide"); - - if (headerSearchButton) { - headerSearchButton.classList.add("hide"); - } - } - - requiresUserRefresh = false; - } - - function updateHeaderUserButton(src) { - if (src) { - headerUserButton.classList.add("headerUserButtonRound"); - headerUserButton.innerHTML = '
"; - } else { - headerUserButton.classList.remove("headerUserButtonRound"); - headerUserButton.innerHTML = 'person'; - } - } - - function showSearch() { - inputManager.trigger("search"); - } - - function onHeaderUserButtonClick(e) { - Dashboard.navigate("mypreferencesmenu.html"); - } - - function onHeaderHomeButtonClick() { - Dashboard.navigate("home.html"); - } - - function showAudioPlayer() { - return appRouter.showNowPlaying(); - } - - function bindMenuEvents() { - mainDrawerButton = document.querySelector(".mainDrawerButton"); - - if (mainDrawerButton) { - mainDrawerButton.addEventListener("click", toggleMainDrawer); - } - - var headerBackButton = skinHeader.querySelector(".headerBackButton"); - - if (headerBackButton) { - headerBackButton.addEventListener("click", onBackClick); - } - - if (headerSearchButton) { - headerSearchButton.addEventListener("click", showSearch); - } - - headerUserButton.addEventListener("click", onHeaderUserButtonClick); - headerHomeButton.addEventListener("click", onHeaderHomeButtonClick); - - if (!layoutManager.tv) { - headerCastButton.addEventListener("click", onCastButtonClicked); - } - - headerAudioPlayerButton.addEventListener("click", showAudioPlayer); - - if (layoutManager.mobile) { - initHeadRoom(skinHeader); - } - events.on(playbackManager, 'playbackstart', onPlaybackStart); - events.on(playbackManager, 'playbackstop', onPlaybackStop); - } - - function onPlaybackStart(e) { - if (playbackManager.isPlayingAudio() && layoutManager.tv) { - headerAudioPlayerButton.classList.remove("hide"); - } else { - headerAudioPlayerButton.classList.add("hide"); - } - } - - function onPlaybackStop(e, stopInfo) { - if (stopInfo.nextMediaType != 'Audio') { - headerAudioPlayerButton.classList.add("hide"); - } - } - - function onCastButtonClicked() { - var btn = this; - - require(["playerSelectionMenu"], function (playerSelectionMenu) { - playerSelectionMenu.show(btn); - }); - } - - function getItemHref(item, context) { - return appRouter.getRouteUrl(item, { - context: context - }); - } - - function toggleMainDrawer() { - if (navDrawerInstance.isVisible) { - closeMainDrawer(); - } else { - openMainDrawer(); - } - } - - function openMainDrawer() { - navDrawerInstance.open(); - lastOpenTime = new Date().getTime(); - } - - function onMainDrawerOpened() { - if (layoutManager.mobile) { - document.body.classList.add("bodyWithPopupOpen"); - } - } - - function closeMainDrawer() { - navDrawerInstance.close(); - } - - function onMainDrawerSelect(e) { - if (navDrawerInstance.isVisible) { - onMainDrawerOpened(); - } else { - document.body.classList.remove("bodyWithPopupOpen"); - } - } - - function refreshLibraryInfoInDrawer(user, drawer) { - var html = ""; - html += '
'; - html += 'home' + globalize.translate("ButtonHome") + ""; - - // libraries are added here - html += '
'; - html += "
"; - - if (user.localUser && user.localUser.Policy.IsAdministrator) { - html += '
'; - html += '

'; - html += globalize.translate("HeaderAdmin"); - html += "

"; - html += 'dashboard' + globalize.translate("TabDashboard") + ""; - html += '' + globalize.translate("Metadata") + ""; - html += "
"; - } - - if (user.localUser) { - html += '
'; - html += '

'; - html += globalize.translate("HeaderUser"); - html += "

"; - - if (appHost.supports("multiserver")) { - html += 'wifi' + globalize.translate("ButtonSelectServer") + ""; - } - - html += '' + globalize.translate("ButtonSettings") + ""; - html += '' + globalize.translate("ButtonSignOut") + ""; - html += "
"; - } - - // add buttons to navigation drawer - navDrawerScrollContainer.innerHTML = html; - - var btnSettings = navDrawerScrollContainer.querySelector(".btnSettings"); - if (btnSettings) { - btnSettings.addEventListener("click", onSettingsClick); - } - - var btnLogout = navDrawerScrollContainer.querySelector(".btnLogout"); - if (btnLogout) { - btnLogout.addEventListener("click", onLogoutClick); - } - } - - function refreshDashboardInfoInDrawer(apiClient) { - currentDrawerType = "admin"; - loadNavDrawer(); - - if (navDrawerScrollContainer.querySelector(".adminDrawerLogo")) { - updateDashboardMenuSelectedItem(); - } else { - createDashboardMenu(apiClient); - } - } - - function isUrlInCurrentView(url) { - return -1 !== window.location.href.toString().toLowerCase().indexOf(url.toLowerCase()); - } - - function updateDashboardMenuSelectedItem() { - var links = navDrawerScrollContainer.querySelectorAll(".navMenuOption"); - var currentViewId = viewManager.currentView().id; - - for (var i = 0, length = links.length; i < length; i++) { - var link = links[i]; - var selected = false; - var pageIds = link.getAttribute("data-pageids"); - - if (pageIds) { - pageIds = pageIds.split("|"); - selected = -1 != pageIds.indexOf(currentViewId); - } - - var pageUrls = link.getAttribute("data-pageurls"); - - if (pageUrls) { - pageUrls = pageUrls.split("|"); - selected = pageUrls.filter(isUrlInCurrentView).length > 0; - } - - if (selected) { - link.classList.add("navMenuOption-selected"); - var title = ""; - link = link.querySelector("span") || link; - title += (link.innerText || link.textContent).trim(); - LibraryMenu.setTitle(title); - } else { - link.classList.remove("navMenuOption-selected"); - } - } - } - - function createToolsMenuList(pluginItems) { - var links = [{ - name: globalize.translate("TabServer") - }, { - name: globalize.translate("TabDashboard"), - href: "dashboard.html", - pageIds: ["dashboardPage"], - icon: "dashboard" - }, { - name: globalize.translate("General"), - href: "dashboardgeneral.html", - pageIds: ["dashboardGeneralPage"], - icon: "settings" - }, { - name: globalize.translate("TabUsers"), - href: "userprofiles.html", - pageIds: ["userProfilesPage", "newUserPage", "editUserPage", "userLibraryAccessPage", "userParentalControlPage", "userPasswordPage"], - icon: "people" - }, { - name: globalize.translate("HeaderLibraries"), - href: "library.html", - pageIds: ["mediaLibraryPage", "librarySettingsPage", "libraryDisplayPage", "metadataImagesConfigurationPage", "metadataNfoPage"], - icon: "folder" - }, { - name: globalize.translate("TabPlayback"), - icon: "", - href: "encodingsettings.html", - pageIds: ["encodingSettingsPage", "playbackConfigurationPage", "streamingSettingsPage"] - }]; - addPluginPagesToMainMenu(links, pluginItems, "server"); - links.push({ - divider: true, - name: globalize.translate("TabDevices") - }); - links.push({ - name: globalize.translate("TabDevices"), - href: "devices.html", - pageIds: ["devicesPage", "devicePage"], - icon: "devices" - }); - links.push({ - name: globalize.translate("HeaderActivity"), - href: "serveractivity.html", - pageIds: ["serverActivityPage"], - icon: "assessment" - }); - links.push({ - name: globalize.translate("DLNA"), - href: "dlnasettings.html", - pageIds: ["dlnaSettingsPage", "dlnaProfilesPage", "dlnaProfilePage"], - icon: "input" - }); - links.push({ - divider: true, - name: globalize.translate("TabLiveTV") - }); - links.push({ - name: globalize.translate("TabLiveTV"), - href: "livetvstatus.html", - pageIds: ["liveTvStatusPage", "liveTvTunerPage"], - icon: "live_tv" - }); - links.push({ - name: globalize.translate("DVR"), - href: "livetvsettings.html", - pageIds: ["liveTvSettingsPage"], - icon: "dvr" - }); - links.push({ - divider: true, - name: globalize.translate("TabAdvanced") - }); - links.push({ - name: globalize.translate("TabNetworking"), - icon: "cloud", - href: "networking.html", - pageIds: ["networkingPage"] - }); - links.push({ - name: globalize.translate("HeaderApiKeys"), - icon: "vpn_key", - href: "apikeys.html", - pageIds: ["apiKeysPage"] - }); - links.push({ - name: globalize.translate("TabLogs"), - href: "log.html", - pageIds: ["logPage"], - icon: "bug_report" - }); - links.push({ - name: globalize.translate("TabNotifications"), - icon: "notifications", - href: "notificationsettings.html", - pageIds: ["notificationSettingsPage", "notificationSettingPage"] - }); - links.push({ - name: globalize.translate("TabPlugins"), - icon: "shopping_cart", - href: "installedplugins.html", - pageIds: ["pluginsPage", "pluginCatalogPage"] - }); - links.push({ - name: globalize.translate("TabScheduledTasks"), - href: "scheduledtasks.html", - pageIds: ["scheduledTasksPage", "scheduledTaskPage"], - icon: "schedule" - }); - addPluginPagesToMainMenu(links, pluginItems); - return links; - } - - function addPluginPagesToMainMenu(links, pluginItems, section) { - for (var i = 0, length = pluginItems.length; i < length; i++) { - var pluginItem = pluginItems[i]; - - if (pluginItem.EnableInMainMenu && pluginItem.MenuSection === section) { - links.push({ - name: pluginItem.DisplayName, - icon: pluginItem.MenuIcon || "folder", - href: Dashboard.getConfigurationPageUrl(pluginItem.Name), - pageUrls: [Dashboard.getConfigurationPageUrl(pluginItem.Name)] - }); - } - } - } - - function getToolsMenuLinks(apiClient) { - return apiClient.getJSON(apiClient.getUrl("web/configurationpages") + "?pageType=PluginConfiguration&EnableInMainMenu=true").then(createToolsMenuList, function (err) { - return createToolsMenuList([]); - }); - } - - function getToolsLinkHtml(item) { - var menuHtml = ""; - var pageIds = item.pageIds ? item.pageIds.join("|") : ""; - pageIds = pageIds ? ' data-pageids="' + pageIds + '"' : ""; - var pageUrls = item.pageUrls ? item.pageUrls.join("|") : ""; - pageUrls = pageUrls ? ' data-pageurls="' + pageUrls + '"' : ""; - menuHtml += '"; - - if (item.icon) { - menuHtml += '' + item.icon + ""; - } - - menuHtml += ''; - menuHtml += item.name; - menuHtml += ""; - return menuHtml + ""; - } - - function getToolsMenuHtml(apiClient) { - return getToolsMenuLinks(apiClient).then(function (items) { - var item; - var menuHtml = ""; - menuHtml += '
'; - - for (var i = 0; i < items.length; i++) { - item = items[i]; - - if (item.href) { - menuHtml += getToolsLinkHtml(item); - } else if (item.name) { - menuHtml += '

'; - menuHtml += item.name; - menuHtml += "

"; - } - } - - return menuHtml + "
"; - }); - } - - function createDashboardMenu(apiClient) { - return getToolsMenuHtml(apiClient).then(function (toolsMenuHtml) { - var html = ""; - html += '"; - html += toolsMenuHtml; - navDrawerScrollContainer.innerHTML = html; - updateDashboardMenuSelectedItem(); - }); - } - - function onSidebarLinkClick() { - var section = this.getElementsByClassName("sectionName")[0]; - var text = section ? section.innerHTML : this.innerHTML; - LibraryMenu.setTitle(text); - } - - function getUserViews(apiClient, userId) { - return apiClient.getUserViews({}, userId).then(function (result) { - var items = result.Items; - var list = []; - - for (var i = 0, length = items.length; i < length; i++) { - var view = items[i]; - list.push(view); - - if ("livetv" == view.CollectionType) { - view.ImageTags = {}; - view.icon = "live_tv"; - var guideView = Object.assign({}, view); - guideView.Name = globalize.translate("ButtonGuide"); - guideView.ImageTags = {}; - guideView.icon = "dvr"; - guideView.url = "livetv.html?tab=1"; - list.push(guideView); - } - } - - return list; - }); - } - - function showBySelector(selector, show) { - var elem = document.querySelector(selector); - - if (elem) { - if (show) { - elem.classList.remove("hide"); - } else { - elem.classList.add("hide"); - } - } - } - - function updateLibraryMenu(user) { - // FIXME: Potential equivalent might be - // showBySelector(".lnkSyncToOtherDevices", !!user.Policy.EnableContentDownloading); - if (!user) { - showBySelector(".libraryMenuDownloads", false); - showBySelector(".lnkSyncToOtherDevices", false); - return void showBySelector(".userMenuOptions", false); - } - - // FIXME: Potentially the same as above - if (user.Policy.EnableContentDownloading) { - showBySelector(".lnkSyncToOtherDevices", true); - } else { - showBySelector(".lnkSyncToOtherDevices", false); - } - - if (user.Policy.EnableContentDownloading && appHost.supports("sync")) { - showBySelector(".libraryMenuDownloads", true); - } else { - showBySelector(".libraryMenuDownloads", false); - } - - var userId = Dashboard.getCurrentUserId(); - var apiClient = getCurrentApiClient(); - var libraryMenuOptions = document.querySelector(".libraryMenuOptions"); - - if (libraryMenuOptions) { - getUserViews(apiClient, userId).then(function (result) { - var items = result; - var html = ""; - html += '

'; - html += globalize.translate("HeaderMedia"); - html += "

"; - html += items.map(function (i) { - var icon = i.icon || imageHelper.getLibraryIcon(i.CollectionType); - var itemId = i.Id; - - if (i.onclick) { - i.onclick; - } - - return '' + icon + '' + i.Name + ""; - }).join(""); - libraryMenuOptions.innerHTML = html; - var elem = libraryMenuOptions; - var sidebarLinks = elem.querySelectorAll(".navMenuOption"); - - for (var i = 0, length = sidebarLinks.length; i < length; i++) { - sidebarLinks[i].removeEventListener("click", onSidebarLinkClick); - sidebarLinks[i].addEventListener("click", onSidebarLinkClick); - } - }); - } - } - - function getTopParentId() { - return getParameterByName("topParentId") || null; - } - - function onMainDrawerClick(e) { - if (dom.parentWithTag(e.target, "A")) { - setTimeout(closeMainDrawer, 30); - } - } - - function onSettingsClick() { - Dashboard.navigate("mypreferencesmenu.html"); - } - - function onLogoutClick() { - Dashboard.logout(); - } - - function updateCastIcon() { - var context = document; - var info = playbackManager.getPlayerInfo(); - var icon = headerCastButton.querySelector("i"); - - if (info && !info.isLocalPlayer) { - icon.innerHTML = ""; - headerCastButton.classList.add("castButton-active"); - context.querySelector(".headerSelectedPlayer").innerHTML = info.deviceName || info.name; - } else { - icon.innerHTML = "cast"; - headerCastButton.classList.remove("castButton-active"); - context.querySelector(".headerSelectedPlayer").innerHTML = ""; - } - } - - function updateLibraryNavLinks(page) { - var i; - var length; - var isLiveTvPage = page.classList.contains("liveTvPage"); - var isChannelsPage = page.classList.contains("channelsPage"); - var isEditorPage = page.classList.contains("metadataEditorPage"); - var isMySyncPage = page.classList.contains("mySyncPage"); - var id = isLiveTvPage || isChannelsPage || isEditorPage || isMySyncPage || page.classList.contains("allLibraryPage") ? "" : getTopParentId() || ""; - var elems = document.getElementsByClassName("lnkMediaFolder"); - - for (var i = 0, length = elems.length; i < length; i++) { - var lnkMediaFolder = elems[i]; - var itemId = lnkMediaFolder.getAttribute("data-itemid"); - - if (isChannelsPage && "channels" === itemId) { - lnkMediaFolder.classList.add("navMenuOption-selected"); - } else if (isLiveTvPage && "livetv" === itemId) { - lnkMediaFolder.classList.add("navMenuOption-selected"); - } else if (isEditorPage && "editor" === itemId) { - lnkMediaFolder.classList.add("navMenuOption-selected"); - } else if (isMySyncPage && "manageoffline" === itemId && -1 != window.location.href.toString().indexOf("mode=download")) { - lnkMediaFolder.classList.add("navMenuOption-selected"); - } else if (isMySyncPage && "syncotherdevices" === itemId && -1 == window.location.href.toString().indexOf("mode=download")) { - lnkMediaFolder.classList.add("navMenuOption-selected"); - } else if (id && itemId == id) { - lnkMediaFolder.classList.add("navMenuOption-selected"); - } else { - lnkMediaFolder.classList.remove("navMenuOption-selected"); - } - } - } - - function updateMenuForPageType(isDashboardPage, isLibraryPage) { - var newPageType = isDashboardPage ? 2 : isLibraryPage ? 1 : 3; - - if (currentPageType !== newPageType) { - currentPageType = newPageType; - - if (isDashboardPage && !layoutManager.mobile) { - skinHeader.classList.add("headroomDisabled"); - } else { - skinHeader.classList.remove("headroomDisabled"); - } - - var bodyClassList = document.body.classList; - - if (isLibraryPage) { - bodyClassList.add("libraryDocument"); - bodyClassList.remove("dashboardDocument"); - bodyClassList.remove("hideMainDrawer"); - - if (navDrawerInstance) { - navDrawerInstance.setEdgeSwipeEnabled(true); - } - } else { - if (isDashboardPage) { - bodyClassList.remove("libraryDocument"); - bodyClassList.add("dashboardDocument"); - bodyClassList.remove("hideMainDrawer"); - - if (navDrawerInstance) { - navDrawerInstance.setEdgeSwipeEnabled(true); - } - } else { - bodyClassList.remove("libraryDocument"); - bodyClassList.remove("dashboardDocument"); - bodyClassList.add("hideMainDrawer"); - - if (navDrawerInstance) { - navDrawerInstance.setEdgeSwipeEnabled(false); - } - } - } - } - - if (requiresUserRefresh) { - connectionManager.user(getCurrentApiClient()).then(updateUserInHeader); - } - } - - function updateTitle(page) { - var title = page.getAttribute("data-title"); - - if (title) { - LibraryMenu.setTitle(title); - } else if (page.classList.contains("standalonePage")) { - LibraryMenu.setDefaultTitle(); - } - } - - function updateBackButton(page) { - if (!headerBackButton) { - headerBackButton = document.querySelector(".headerBackButton"); - } - - if (headerBackButton) { - if ("false" !== page.getAttribute("data-backbutton") && appRouter.canGoBack()) { - headerBackButton.classList.remove("hide"); - } else { - headerBackButton.classList.add("hide"); - } - } - } - - function initHeadRoom(elem) { - require(["headroom"], function (Headroom) { - var headroom = new Headroom([], {}); - headroom.add(elem); - }); - } - - function refreshLibraryDrawer(user) { - loadNavDrawer(); - currentDrawerType = "library"; - - if (user) { - Promise.resolve(user); - } else { - connectionManager.user(getCurrentApiClient()).then(function (user) { - refreshLibraryInfoInDrawer(user); - updateLibraryMenu(user.localUser); - }); - } - } - - function getNavDrawerOptions() { - var drawerWidth = screen.availWidth - 50; - drawerWidth = Math.max(drawerWidth, 240); - drawerWidth = Math.min(drawerWidth, 320); - return { - target: navDrawerElement, - onChange: onMainDrawerSelect, - width: drawerWidth - }; - } - - function loadNavDrawer() { - if (navDrawerInstance) { - return Promise.resolve(navDrawerInstance); - } - - navDrawerElement = document.querySelector(".mainDrawer"); - navDrawerScrollContainer = navDrawerElement.querySelector(".scrollContainer"); - navDrawerScrollContainer.addEventListener("click", onMainDrawerClick); - return new Promise(function (resolve, reject) { - require(["navdrawer"], function (navdrawer) { - navDrawerInstance = new navdrawer(getNavDrawerOptions()); - - if (!layoutManager.tv) { - navDrawerElement.classList.remove("hide"); - } - - resolve(navDrawerInstance); - }); - }); - } - - var navDrawerElement; - var navDrawerScrollContainer; - var navDrawerInstance; - var mainDrawerButton; - var headerHomeButton; - var currentDrawerType; - var pageTitleElement; - var headerBackButton; - var headerUserButton; - var currentUser; - var headerCastButton; - var headerSearchButton; - var headerAudioPlayerButton; - var enableLibraryNavDrawer = layoutManager.desktop; - var skinHeader = document.querySelector(".skinHeader"); - var requiresUserRefresh = true; - var lastOpenTime = new Date().getTime(); - window.LibraryMenu = { - getTopParentId: getTopParentId, - onHardwareMenuButtonClick: function () { - toggleMainDrawer(); - }, - setTabs: function (type, selectedIndex, builder) { - require(["mainTabsManager"], function (mainTabsManager) { - if (type) { - mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () { - return []; - }); - } else { - mainTabsManager.setTabs(null); - } - }); - }, - setDefaultTitle: function () { - if (!pageTitleElement) { - pageTitleElement = document.querySelector(".pageTitle"); - } - - if (pageTitleElement) { - pageTitleElement.classList.add("pageTitleWithLogo"); - pageTitleElement.classList.add("pageTitleWithDefaultLogo"); - pageTitleElement.style.backgroundImage = null; - pageTitleElement.innerHTML = ""; - } - - document.title = "Jellyfin"; - }, - setTitle: function (title) { - if (null == title) { - return void LibraryMenu.setDefaultTitle(); - } - - if ("-" === title) { - title = ""; - } - - var html = title; - - if (!pageTitleElement) { - pageTitleElement = document.querySelector(".pageTitle"); - } - - if (pageTitleElement) { - pageTitleElement.classList.remove("pageTitleWithLogo"); - pageTitleElement.classList.remove("pageTitleWithDefaultLogo"); - pageTitleElement.style.backgroundImage = null; - pageTitleElement.innerHTML = html || ""; - } - - document.title = title || "Jellyfin"; - }, - setTransparentMenu: function (transparent) { - if (transparent) { - skinHeader.classList.add("semiTransparent"); - } else { - skinHeader.classList.remove("semiTransparent"); - } - } - }; - var currentPageType; - pageClassOn("pagebeforeshow", "page", function (e) { - if (!this.classList.contains("withTabs")) { - LibraryMenu.setTabs(null); - } - }); - pageClassOn("pageshow", "page", function (e) { - var page = this; - var isDashboardPage = page.classList.contains("type-interior"); - var isHomePage = page.classList.contains("homePage"); - var isLibraryPage = !isDashboardPage && page.classList.contains("libraryPage"); - var apiClient = getCurrentApiClient(); - - if (isDashboardPage) { - if (mainDrawerButton) { - mainDrawerButton.classList.remove("hide"); - } - - refreshDashboardInfoInDrawer(apiClient); - } else { - if (mainDrawerButton) { - if (enableLibraryNavDrawer || isHomePage) { - mainDrawerButton.classList.remove("hide"); - } else { - mainDrawerButton.classList.add("hide"); - } - } - - if ("library" !== currentDrawerType) { - refreshLibraryDrawer(); - } - } - - updateMenuForPageType(isDashboardPage, isLibraryPage); - - if (!e.detail.isRestored) { - window.scrollTo(0, 0); - } - - updateTitle(page); - updateBackButton(page); - updateLibraryNavLinks(page); - }); - - renderHeader(); - - events.on(connectionManager, "localusersignedin", function (e, user) { - currentDrawerType = null; - currentUser = { - localUser: user - }; - loadNavDrawer(); - connectionManager.user(connectionManager.getApiClient(user.ServerId)).then(function (user) { - currentUser = user; - updateUserInHeader(user); - }); - }); - events.on(connectionManager, "localusersignedout", function () { - currentUser = {}; - updateUserInHeader(); - }); - events.on(playbackManager, "playerchange", updateCastIcon); - loadNavDrawer(); - return LibraryMenu; -}); diff --git a/src/scripts/livetvcomponents.js b/src/scripts/livetvcomponents.js index 61ffc66fb4..fd4a46c0f1 100644 --- a/src/scripts/livetvcomponents.js +++ b/src/scripts/livetvcomponents.js @@ -1,12 +1,12 @@ -define(["layoutManager", "datetime", "cardBuilder", "apphost"], function (layoutManager, datetime, cardBuilder, appHost) { - "use strict"; +define(['layoutManager', 'datetime', 'cardBuilder', 'apphost'], function (layoutManager, datetime, cardBuilder, appHost) { + 'use strict'; function enableScrollX() { return !layoutManager.desktop; } function getBackdropShape() { - return enableScrollX() ? "overflowBackdrop" : "backdrop"; + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; } function getTimersHtml(timers, options) { @@ -14,27 +14,27 @@ define(["layoutManager", "datetime", "cardBuilder", "apphost"], function (layout var i; var length; var items = timers.map(function (t) { - t.Type = "Timer"; + t.Type = 'Timer'; return t; }); var groups = []; - var currentGroupName = ""; + var currentGroupName = ''; var currentGroup = []; for (i = 0, length = items.length; i < length; i++) { var item = items[i]; - var dateText = ""; + var dateText = ''; if (options.indexByDate !== false && item.StartDate) { try { var premiereDate = datetime.parseISO8601Date(item.StartDate, true); dateText = datetime.toLocaleDateString(premiereDate, { - weekday: "long", - month: "short", - day: "numeric" + weekday: 'long', + month: 'short', + day: 'numeric' }); } catch (err) { - console.error("error parsing premiereDate:" + item.StartDate + "; error: " + err); + console.error('error parsing premiereDate:' + item.StartDate + '; error: ' + err); } } @@ -60,23 +60,23 @@ define(["layoutManager", "datetime", "cardBuilder", "apphost"], function (layout }); } - var html = ""; + var html = ''; for (i = 0, length = groups.length; i < length; i++) { var group = groups[i]; - var supportsImageAnalysis = appHost.supports("imageanalysis"); + var supportsImageAnalysis = appHost.supports('imageanalysis'); var cardLayout = appHost.preferVisualCards || supportsImageAnalysis; cardLayout = true; if (group.name) { html += '
'; - html += '

' + group.name + "

"; + html += '

' + group.name + '

'; } if (enableScrollX()) { - var scrollXClass = "scrollX hiddenScrollX"; + var scrollXClass = 'scrollX hiddenScrollX'; if (layoutManager.tv) { - scrollXClass += " smoothScrollX"; + scrollXClass += ' smoothScrollX'; } html += '
'; @@ -86,26 +86,26 @@ define(["layoutManager", "datetime", "cardBuilder", "apphost"], function (layout html += cardBuilder.getCardsHtml({ items: group.items, - shape: cardLayout ? getBackdropShape() : enableScrollX() ? "autoOverflow" : "autoVertical", + shape: cardLayout ? getBackdropShape() : enableScrollX() ? 'autoOverflow' : 'autoVertical', showParentTitleOrTitle: true, showAirTime: true, showAirEndTime: true, showChannelName: !cardLayout, cardLayout: cardLayout, centerText: !cardLayout, - action: "edit", - cardFooterAside: "none", - preferThumb: !!cardLayout || "auto", - defaultShape: cardLayout ? null : "portrait", + action: 'edit', + cardFooterAside: 'none', + preferThumb: !!cardLayout || 'auto', + defaultShape: cardLayout ? null : 'portrait', coverImage: true, allowBottomPadding: false, overlayText: false, showChannelLogo: cardLayout }); - html += "
"; + html += '
'; if (group.name) { - html += "
"; + html += '
'; } } diff --git a/src/components/input/mouseManager.js b/src/scripts/mouseManager.js similarity index 98% rename from src/components/input/mouseManager.js rename to src/scripts/mouseManager.js index 78057c0bfe..e6117fa851 100644 --- a/src/components/input/mouseManager.js +++ b/src/scripts/mouseManager.js @@ -41,7 +41,7 @@ define(['inputManager', 'focusManager', 'browser', 'layoutManager', 'events', 'd var eventY = e.screenY; // if coord don't exist how could it move - if (typeof eventX === "undefined" && typeof eventY === "undefined") { + if (typeof eventX === 'undefined' && typeof eventY === 'undefined') { return; } diff --git a/src/components/multidownload.js b/src/scripts/multiDownload.js similarity index 100% rename from src/components/multidownload.js rename to src/scripts/multiDownload.js diff --git a/src/scripts/playlistedit.js b/src/scripts/playlistedit.js index 32a3a960a4..636a7ef056 100644 --- a/src/scripts/playlistedit.js +++ b/src/scripts/playlistedit.js @@ -1,14 +1,14 @@ -define(["listView"], function (listView) { - "use strict"; +define(['listView'], function (listView) { + 'use strict'; function getFetchPlaylistItemsFn(itemId) { return function () { var query = { - Fields: "PrimaryImageAspectRatio,UserData", - EnableImageTypes: "Primary,Backdrop,Banner,Thumb", + Fields: 'PrimaryImageAspectRatio,UserData', + EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', UserId: ApiClient.getCurrentUserId() }; - return ApiClient.getJSON(ApiClient.getUrl("Playlists/" + itemId + "/Items", query)); + return ApiClient.getJSON(ApiClient.getUrl(`Playlists/${itemId}/Items`, query)); }; } @@ -19,7 +19,7 @@ define(["listView"], function (listView) { showIndex: false, showRemoveFromPlaylist: true, playFromHere: true, - action: "playallfromhere", + action: 'playallfromhere', smallIcon: true, dragHandle: true, playlistId: itemId @@ -28,9 +28,9 @@ define(["listView"], function (listView) { } function init(page, item) { - var elem = page.querySelector("#childrenContent .itemsContainer"); - elem.classList.add("vertical-list"); - elem.classList.remove("vertical-wrap"); + var elem = page.querySelector('#childrenContent .itemsContainer'); + elem.classList.add('vertical-list'); + elem.classList.remove('vertical-wrap'); elem.enableDragReordering(true); elem.fetchData = getFetchPlaylistItemsFn(item.Id); elem.getItemsHtml = getItemsHtmlFn(item.Id); @@ -43,8 +43,8 @@ define(["listView"], function (listView) { init(page, item); } - page.querySelector("#childrenContent").classList.add("verticalSection-extrabottompadding"); - page.querySelector("#childrenContent .itemsContainer").refreshItems(); + page.querySelector('#childrenContent').classList.add('verticalSection-extrabottompadding'); + page.querySelector('#childrenContent .itemsContainer').refreshItems(); } }; }); diff --git a/src/scripts/playlists.js b/src/scripts/playlists.js index 91c49017da..974d00f8e6 100644 --- a/src/scripts/playlists.js +++ b/src/scripts/playlists.js @@ -1,5 +1,5 @@ -define(["loading", "listView", "cardBuilder", "libraryMenu", "libraryBrowser", "apphost", "imageLoader", "emby-itemscontainer"], function (loading, listView, cardBuilder, libraryMenu, libraryBrowser, appHost, imageLoader) { - "use strict"; +define(['loading', 'listView', 'cardBuilder', 'libraryMenu', 'libraryBrowser', 'apphost', 'imageLoader', 'userSettings', 'emby-itemscontainer'], function (loading, listView, cardBuilder, libraryMenu, libraryBrowser, appHost, imageLoader, userSettings) { + 'use strict'; return function (view, params) { function getPageData(context) { @@ -9,16 +9,20 @@ define(["loading", "listView", "cardBuilder", "libraryMenu", "libraryBrowser", " if (!pageData) { pageData = data[key] = { query: { - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Playlist", + SortBy: 'SortName', + SortOrder: 'Ascending', + IncludeItemTypes: 'Playlist', Recursive: true, - Fields: "PrimaryImageAspectRatio,SortName,CumulativeRunTimeTicks,CanDelete", - StartIndex: 0, - Limit: 100 + Fields: 'PrimaryImageAspectRatio,SortName,CumulativeRunTimeTicks,CanDelete', + StartIndex: 0 }, - view: libraryBrowser.getSavedView(key) || "Poster" + view: libraryBrowser.getSavedView(key) || 'Poster' }; + + if (userSettings.libraryPageSize() > 0) { + pageData.query['Limit'] = userSettings.libraryPageSize(); + } + pageData.query.ParentId = libraryMenu.getTopParentId(); libraryBrowser.loadSavedQueryValues(key, pageData.query); } @@ -48,31 +52,32 @@ define(["loading", "listView", "cardBuilder", "libraryMenu", "libraryBrowser", " function onViewStyleChange() { var viewStyle = getPageData(view).view; - var itemsContainer = view.querySelector(".itemsContainer"); + var itemsContainer = view.querySelector('.itemsContainer'); - if ("List" == viewStyle) { - itemsContainer.classList.add("vertical-list"); - itemsContainer.classList.remove("vertical-wrap"); + if ('List' == viewStyle) { + itemsContainer.classList.add('vertical-list'); + itemsContainer.classList.remove('vertical-wrap'); } else { - itemsContainer.classList.remove("vertical-list"); - itemsContainer.classList.add("vertical-wrap"); + itemsContainer.classList.remove('vertical-list'); + itemsContainer.classList.add('vertical-wrap'); } - itemsContainer.innerHTML = ""; + itemsContainer.innerHTML = ''; } function reloadItems() { showLoadingMessage(); var query = getQuery(view); var promise1 = ApiClient.getItems(Dashboard.getCurrentUserId(), query); + // TODO: promise2 is unused, check if necessary. var promise2 = Dashboard.getCurrentUser(); Promise.all([promise1, promise2]).then(function (responses) { var result = responses[0]; - responses[1]; + // TODO: Is the scroll necessary? window.scrollTo(0, 0); - var html = ""; + var html = ''; var viewStyle = getPageData(view).view; - view.querySelector(".listTopPaging").innerHTML = libraryBrowser.getQueryPagingHtml({ + view.querySelector('.listTopPaging').innerHTML = libraryBrowser.getQueryPagingHtml({ startIndex: query.StartIndex, limit: query.Limit, totalRecordCount: result.TotalRecordCount, @@ -80,37 +85,37 @@ define(["loading", "listView", "cardBuilder", "libraryMenu", "libraryBrowser", " showLimit: false, updatePageSizeSetting: false, addLayoutButton: true, - layouts: "List,Poster,PosterCard,Thumb,ThumbCard", + layouts: 'List,Poster,PosterCard,Thumb,ThumbCard', currentLayout: viewStyle }); if (result.TotalRecordCount) { - if (viewStyle == "List") { + if (viewStyle == 'List') { html = listView.getListViewHtml({ items: result.Items, sortBy: query.SortBy }); - } else if (viewStyle == "PosterCard") { + } else if (viewStyle == 'PosterCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "square", + shape: 'square', coverImage: true, showTitle: true, cardLayout: true }); - } else if (viewStyle == "Thumb") { + } else if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', showTitle: true, centerText: true, preferThumb: true, overlayPlayButton: true }); - } else if (viewStyle == "ThumbCard") { + } else if (viewStyle == 'ThumbCard') { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "backdrop", + shape: 'backdrop', showTitle: true, preferThumb: true, cardLayout: true @@ -118,43 +123,47 @@ define(["loading", "listView", "cardBuilder", "libraryMenu", "libraryBrowser", " } else { html = cardBuilder.getCardsHtml({ items: result.Items, - shape: "square", + shape: 'square', showTitle: true, coverImage: true, centerText: true, overlayPlayButton: true }); } - view.querySelector(".noItemsMessage").classList.add("hide"); + view.querySelector('.noItemsMessage').classList.add('hide'); } else { - view.querySelector(".noItemsMessage").classList.remove("hide"); + view.querySelector('.noItemsMessage').classList.remove('hide'); } - var elem = view.querySelector(".itemsContainer"); + var elem = view.querySelector('.itemsContainer'); elem.innerHTML = html; imageLoader.lazyChildren(elem); - var btnNextPage = view.querySelector(".btnNextPage"); + var btnNextPage = view.querySelector('.btnNextPage'); if (btnNextPage) { - btnNextPage.addEventListener("click", function () { - query.StartIndex += query.Limit; + btnNextPage.addEventListener('click', function () { + if (userSettings.libraryPageSize() > 0) { + query.StartIndex += query.Limit; + } reloadItems(); }); } - var btnPreviousPage = view.querySelector(".btnPreviousPage"); + var btnPreviousPage = view.querySelector('.btnPreviousPage'); if (btnPreviousPage) { - btnPreviousPage.addEventListener("click", function () { - query.StartIndex -= query.Limit; + btnPreviousPage.addEventListener('click', function () { + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); + } reloadItems(); }); } - var btnChangeLayout = view.querySelector(".btnChangeLayout"); + var btnChangeLayout = view.querySelector('.btnChangeLayout'); if (btnChangeLayout) { - btnChangeLayout.addEventListener("layoutchange", function (e) { + btnChangeLayout.addEventListener('layoutchange', function (e) { var layout = e.detail.viewStyle; getPageData(view).view = layout; libraryBrowser.saveViewSetting(getSavedQueryKey(view), layout); @@ -169,11 +178,11 @@ define(["loading", "listView", "cardBuilder", "libraryMenu", "libraryBrowser", " } var data = {}; - view.addEventListener("viewbeforeshow", function () { + view.addEventListener('viewbeforeshow', function () { reloadItems(); }); - view.querySelector(".btnNewPlaylist").addEventListener("click", function () { - require(["playlistEditor"], function (playlistEditor) { + view.querySelector('.btnNewPlaylist').addEventListener('click', function () { + require(['playlistEditor'], function (playlistEditor) { var serverId = ApiClient.serverInfo().Id; new playlistEditor().show({ items: [], diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 9c3db58a7f..6f13711b66 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -1,439 +1,436 @@ define([ - "jQuery", - "emby-button", - "emby-input", - "scripts/livetvcomponents", - "paper-icon-button-light", - "emby-itemscontainer", - "emby-collapse", - "emby-select", - "livetvcss", - "emby-checkbox", - "emby-slider", - "listViewStyle", - "dashboardcss", - "detailtablecss"], function () { + 'jQuery', + 'emby-button', + 'emby-input', + 'scripts/livetvcomponents', + 'paper-icon-button-light', + 'emby-itemscontainer', + 'emby-collapse', + 'emby-select', + 'livetvcss', + 'emby-checkbox', + 'emby-slider', + 'listViewStyle', + 'dashboardcss', + 'detailtablecss'], function () { function defineRoute(newRoute) { var path = newRoute.path; - console.debug("defining route: " + path); - newRoute.dictionary = "core"; + console.debug('defining route: ' + path); + newRoute.dictionary = 'core'; Emby.Page.addRoute(path, newRoute); } - console.debug("defining core routes"); + console.debug('defining core routes'); defineRoute({ - path: "/addplugin.html", - autoFocus: false, - roles: "admin", - controller: "dashboard/plugins/add" - }); - defineRoute({ - path: "/mypreferencesmenu.html", - autoFocus: false, - transition: "fade", - controller: "user/menu" - }); - defineRoute({ - path: "/myprofile.html", - autoFocus: false, - transition: "fade", - controller: "user/profile" - }); - defineRoute({ - path: "/addserver.html", + path: '/addserver.html', autoFocus: false, anonymous: true, startup: true, - controller: "auth/addserver" + controller: 'auth/addserver' }); defineRoute({ - path: "/mypreferencesdisplay.html", + path: '/selectserver.html', autoFocus: false, - transition: "fade", - controller: "user/display" + anonymous: true, + startup: true, + controller: 'auth/selectserver', + type: 'selectserver' }); defineRoute({ - path: "/mypreferenceshome.html", + path: '/login.html', autoFocus: false, - transition: "fade", - controller: "user/home" + anonymous: true, + startup: true, + controller: 'auth/login', + type: 'login' }); defineRoute({ - path: "/mypreferencesplayback.html", - autoFocus: false, - transition: "fade", - controller: "user/playback" + path: '/forgotpassword.html', + anonymous: true, + startup: true, + controller: 'auth/forgotpassword' }); defineRoute({ - path: "/mypreferencessubtitles.html", + path: '/forgotpasswordpin.html', autoFocus: false, - transition: "fade", - controller: "user/subtitles" + anonymous: true, + startup: true, + controller: 'auth/forgotpasswordpin' }); defineRoute({ - path: "/dashboard.html", + path: '/mypreferencesmenu.html', autoFocus: false, - roles: "admin", - controller: "dashboard/dashboard" + transition: 'fade', + controller: 'user/menu' }); defineRoute({ - path: "/dashboardgeneral.html", - controller: "dashboard/general", + path: '/myprofile.html', autoFocus: false, - roles: "admin" + transition: 'fade', + controller: 'user/profile' }); defineRoute({ - path: "/networking.html", + path: '/mypreferencesdisplay.html', autoFocus: false, - roles: "admin", - controller: "dashboard/networking" + transition: 'fade', + controller: 'user/display' }); defineRoute({ - path: "/devices.html", + path: '/mypreferenceshome.html', autoFocus: false, - roles: "admin", - controller: "devices" + transition: 'fade', + controller: 'user/home' }); defineRoute({ - path: "/device.html", + path: '/mypreferencesplayback.html', autoFocus: false, - roles: "admin", - controller: "device" + transition: 'fade', + controller: 'user/playback' }); defineRoute({ - path: "/dlnaprofile.html", + path: '/mypreferencessubtitles.html', autoFocus: false, - roles: "admin", - controller: "dlnaprofile" + transition: 'fade', + controller: 'user/subtitles' + }); + + defineRoute({ + path: '/dashboard.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/dashboard' }); defineRoute({ - path: "/dlnaprofiles.html", + path: '/dashboardgeneral.html', + controller: 'dashboard/general', autoFocus: false, - roles: "admin", - controller: "dlnaprofiles" + roles: 'admin' }); defineRoute({ - path: "/dlnasettings.html", + path: '/networking.html', autoFocus: false, - roles: "admin", - controller: "dlnasettings" + roles: 'admin', + controller: 'dashboard/networking' }); defineRoute({ - path: "/edititemmetadata.html", - controller: "edititemmetadata", + path: '/devices.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/devices/devices' + }); + defineRoute({ + path: '/device.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/devices/device' + }); + defineRoute({ + path: '/dlnaprofile.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/dlna/profile' + }); + defineRoute({ + path: '/dlnaprofiles.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/dlna/profiles' + }); + defineRoute({ + path: '/addplugin.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/plugins/add' + }); + defineRoute({ + path: '/library.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/mediaLibrary' + }); + defineRoute({ + path: '/librarydisplay.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/librarydisplay' + }); + defineRoute({ + path: '/dlnasettings.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/dlna/settings' + }); + defineRoute({ + path: '/edititemmetadata.html', + controller: 'edititemmetadata', autoFocus: false }); defineRoute({ - path: "/encodingsettings.html", + path: '/encodingsettings.html', autoFocus: false, - roles: "admin", - controller: "encodingsettings" + roles: 'admin', + controller: 'dashboard/encodingsettings' }); defineRoute({ - path: "/forgotpassword.html", - anonymous: true, - startup: true, - controller: "auth/forgotpassword" + path: '/log.html', + roles: 'admin', + controller: 'dashboard/logs' }); defineRoute({ - path: "/forgotpasswordpin.html", + path: '/metadataimages.html', autoFocus: false, - anonymous: true, - startup: true, - controller: "auth/forgotpasswordpin" + roles: 'admin', + controller: 'dashboard/metadataImages' }); defineRoute({ - path: "/home.html", + path: '/metadatanfo.html', autoFocus: false, - controller: "home", - transition: "fade", - type: "home" + roles: 'admin', + controller: 'dashboard/metadatanfo' }); defineRoute({ - path: "/list.html", + path: '/notificationsetting.html', autoFocus: false, - controller: "list", - transition: "fade" + roles: 'admin', + controller: 'dashboard/notifications/notification' }); defineRoute({ - path: "/index.html", + path: '/notificationsettings.html', + controller: 'dashboard/notifications/notifications', autoFocus: false, - isDefaultRoute: true + roles: 'admin' }); defineRoute({ - path: "/itemdetails.html", - controller: "itemdetailpage", + path: '/playbackconfiguration.html', autoFocus: false, - transition: "fade" + roles: 'admin', + controller: 'dashboard/playback' }); defineRoute({ - path: "/library.html", + path: '/availableplugins.html', autoFocus: false, - roles: "admin", - controller: "medialibrarypage" - }); - defineRoute({ - path: "/librarydisplay.html", - autoFocus: false, - roles: "admin", - controller: "librarydisplay" - }); - defineRoute({ - path: "/librarysettings.html", - autoFocus: false, - roles: "admin", - controller: "librarysettings" - }); - defineRoute({ - path: "/livetv.html", - controller: "livetv/livetvsuggested", - autoFocus: false, - transition: "fade" - }); - defineRoute({ - path: "/livetvguideprovider.html", - autoFocus: false, - roles: "admin", - controller: "livetvguideprovider" - }); - defineRoute({ - path: "/livetvsettings.html", - autoFocus: false, - controller: "livetvsettings" - }); - defineRoute({ - path: "/livetvstatus.html", - autoFocus: false, - roles: "admin", - controller: "livetvstatus" - }); - defineRoute({ - path: "/livetvtuner.html", - autoFocus: false, - roles: "admin", - controller: "livetvtuner" - }); - defineRoute({ - path: "/log.html", - roles: "admin", - controller: "dashboard/logs" - }); - defineRoute({ - path: "/login.html", - autoFocus: false, - anonymous: true, - startup: true, - controller: "auth/login", - type: "login" - }); - defineRoute({ - path: "/metadataimages.html", - autoFocus: false, - roles: "admin", - controller: "metadataimagespage" - }); - defineRoute({ - path: "/metadatanfo.html", - autoFocus: false, - roles: "admin", - controller: "metadatanfo" - }); - defineRoute({ - path: "/movies.html", - autoFocus: false, - controller: "movies/moviesrecommended", - transition: "fade" - }); - defineRoute({ - path: "/music.html", - controller: "music/musicrecommended", - autoFocus: false, - transition: "fade" - }); - defineRoute({ - path: "/notificationsetting.html", - autoFocus: false, - roles: "admin", - controller: "dashboard/notifications/notification" - }); - defineRoute({ - path: "/notificationsettings.html", - controller: "dashboard/notifications/notifications", - autoFocus: false, - roles: "admin" - }); - defineRoute({ - path: "/nowplaying.html", - controller: "playback/nowplaying", - autoFocus: false, - transition: "fade", - fullscreen: true, - supportsThemeMedia: true, - enableMediaControl: false - }); - defineRoute({ - path: "/playbackconfiguration.html", - autoFocus: false, - roles: "admin", - controller: "playbackconfiguration" - }); - defineRoute({ - path: "/availableplugins.html", - autoFocus: false, - roles: "admin", - controller: "dashboard/plugins/available" - }); - defineRoute({ - path: "/installedplugins.html", - autoFocus: false, - roles: "admin", - controller: "dashboard/plugins/installed" - }); - defineRoute({ - path: "/scheduledtask.html", - autoFocus: false, - roles: "admin", - controller: "dashboard/scheduledtasks/scheduledtask" - }); - defineRoute({ - path: "/scheduledtasks.html", - autoFocus: false, - roles: "admin", - controller: "dashboard/scheduledtasks/scheduledtasks" - }); - defineRoute({ - path: "/search.html", - controller: "searchpage" - }); - defineRoute({ - path: "/selectserver.html", - autoFocus: false, - anonymous: true, - startup: true, - controller: "auth/selectserver", - type: "selectserver" - }); - defineRoute({ - path: "/serveractivity.html", - autoFocus: false, - roles: "admin", - controller: "serveractivity" - }); - defineRoute({ - path: "/apikeys.html", - autoFocus: false, - roles: "admin", - controller: "apikeys" - }); - defineRoute({ - path: "/streamingsettings.html", - autoFocus: false, - roles: "admin", - controller: "streamingsettings" - }); - defineRoute({ - path: "/tv.html", - autoFocus: false, - controller: "shows/tvrecommended", - transition: "fade" - }); - defineRoute({ - path: "/useredit.html", - autoFocus: false, - roles: "admin", - controller: "useredit" - }); - defineRoute({ - path: "/userlibraryaccess.html", - autoFocus: false, - roles: "admin", - controller: "userlibraryaccess" - }); - defineRoute({ - path: "/usernew.html", - autoFocus: false, - roles: "admin", - controller: "usernew" - }); - defineRoute({ - path: "/userparentalcontrol.html", - autoFocus: false, - roles: "admin", - controller: "userparentalcontrol" - }); - defineRoute({ - path: "/userpassword.html", - autoFocus: false, - controller: "userpasswordpage" - }); - defineRoute({ - path: "/userprofiles.html", - autoFocus: false, - roles: "admin", - controller: "userprofilespage" + roles: 'admin', + controller: 'dashboard/plugins/available' }); defineRoute({ - path: "/wizardremoteaccess.html", + path: '/home.html', autoFocus: false, - anonymous: true, - controller: "wizard/remoteaccess" + controller: 'home', + transition: 'fade', + type: 'home' }); defineRoute({ - path: "/wizardfinish.html", - autoFocus: false, - anonymous: true, - controller: "wizard/finish" + path: '/search.html', + controller: 'searchpage' }); defineRoute({ - path: "/wizardlibrary.html", + path: '/list.html', autoFocus: false, - anonymous: true, - controller: "medialibrarypage" + controller: 'list', + transition: 'fade' }); defineRoute({ - path: "/wizardsettings.html", + path: '/itemdetails.html', + controller: 'itemDetails', autoFocus: false, - anonymous: true, - controller: "wizard/settings" + transition: 'fade' }); defineRoute({ - path: "/wizardstart.html", + path: '/livetv.html', + controller: 'livetv/livetvsuggested', autoFocus: false, - anonymous: true, - controller: "wizard/start" + transition: 'fade' }); defineRoute({ - path: "/wizarduser.html", - controller: "wizard/user", + path: '/livetvguideprovider.html', + autoFocus: false, + roles: 'admin', + controller: 'livetvguideprovider' + }); + defineRoute({ + path: '/livetvsettings.html', + autoFocus: false, + controller: 'livetvsettings' + }); + defineRoute({ + path: '/livetvstatus.html', + autoFocus: false, + roles: 'admin', + controller: 'livetvstatus' + }); + defineRoute({ + path: '/livetvtuner.html', + autoFocus: false, + roles: 'admin', + controller: 'livetvtuner' + }); + defineRoute({ + path: '/movies.html', + autoFocus: false, + controller: 'movies/moviesrecommended', + transition: 'fade' + }); + defineRoute({ + path: '/music.html', + controller: 'music/musicrecommended', + autoFocus: false, + transition: 'fade' + }); + defineRoute({ + path: '/installedplugins.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/plugins/installed' + }); + defineRoute({ + path: '/scheduledtask.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/scheduledtasks/scheduledtask' + }); + defineRoute({ + path: '/scheduledtasks.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/scheduledtasks/scheduledtasks' + }); + defineRoute({ + path: '/serveractivity.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/serveractivity' + }); + defineRoute({ + path: '/apikeys.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/apikeys' + }); + defineRoute({ + path: '/streamingsettings.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/streaming' + }); + defineRoute({ + path: '/tv.html', + autoFocus: false, + controller: 'shows/tvrecommended', + transition: 'fade' + }); + + defineRoute({ + path: '/useredit.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/users/useredit' + }); + defineRoute({ + path: '/userlibraryaccess.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/users/userlibraryaccess' + }); + defineRoute({ + path: '/usernew.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/users/usernew' + }); + defineRoute({ + path: '/userparentalcontrol.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/users/userparentalcontrol' + }); + defineRoute({ + path: '/userpassword.html', + autoFocus: false, + controller: 'dashboard/users/userpasswordpage' + }); + defineRoute({ + path: '/userprofiles.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/users/userprofilespage' + }); + + defineRoute({ + path: '/wizardremoteaccess.html', + autoFocus: false, + anonymous: true, + controller: 'wizard/remoteaccess' + }); + defineRoute({ + path: '/wizardfinish.html', + autoFocus: false, + anonymous: true, + controller: 'wizard/finish' + }); + defineRoute({ + path: '/wizardlibrary.html', + autoFocus: false, + anonymous: true, + controller: 'dashboard/mediaLibrary' + }); + defineRoute({ + path: '/wizardsettings.html', + autoFocus: false, + anonymous: true, + controller: 'wizard/settings' + }); + defineRoute({ + path: '/wizardstart.html', + autoFocus: false, + anonymous: true, + controller: 'wizard/start' + }); + defineRoute({ + path: '/wizarduser.html', + controller: 'wizard/user', autoFocus: false, anonymous: true }); defineRoute({ - path: "/videoosd.html", - transition: "fade", - controller: "playback/videoosd", + path: '/videoosd.html', + transition: 'fade', + controller: 'playback/videoosd', autoFocus: false, - type: "video-osd", + type: 'video-osd', supportsThemeMedia: true, fullscreen: true, enableMediaControl: false }); defineRoute({ - path: "/configurationpage", + path: '/nowplaying.html', + controller: 'playback/nowplaying', + autoFocus: false, + transition: 'fade', + fullscreen: true, + supportsThemeMedia: true, + enableMediaControl: false + }); + defineRoute({ + path: '/configurationpage', autoFocus: false, enableCache: false, enableContentQueryString: true, - roles: "admin" + roles: 'admin' }); defineRoute({ - path: "/", + path: '/', isDefaultRoute: true, autoFocus: false }); + defineRoute({ + path: '/index.html', + autoFocus: false, + isDefaultRoute: true + }); }); diff --git a/src/components/scrollhelper.js b/src/scripts/scrollHelper.js similarity index 100% rename from src/components/scrollhelper.js rename to src/scripts/scrollHelper.js diff --git a/src/scripts/searchtab.js b/src/scripts/searchtab.js index c0852bfc77..95b1e5a7c3 100644 --- a/src/scripts/searchtab.js +++ b/src/scripts/searchtab.js @@ -1,18 +1,18 @@ -define(["searchFields", "searchResults", "events"], function (SearchFields, SearchResults, events) { - "use strict"; +define(['searchFields', 'searchResults', 'events'], function (SearchFields, SearchResults, events) { + 'use strict'; function init(instance, tabContent, options) { tabContent.innerHTML = '
'; instance.searchFields = new SearchFields({ - element: tabContent.querySelector(".searchFields") + element: tabContent.querySelector('.searchFields') }); instance.searchResults = new SearchResults({ - element: tabContent.querySelector(".searchResults"), + element: tabContent.querySelector('.searchResults'), serverId: ApiClient.serverId(), parentId: options.parentId, collectionType: options.collectionType }); - events.on(instance.searchFields, "search", function (e, value) { + events.on(instance.searchFields, 'search', function (e, value) { instance.searchResults.search(value); }); } diff --git a/src/components/serverNotifications.js b/src/scripts/serverNotifications.js similarity index 88% rename from src/components/serverNotifications.js rename to src/scripts/serverNotifications.js index 5554925272..2553c284f0 100644 --- a/src/components/serverNotifications.js +++ b/src/scripts/serverNotifications.js @@ -1,4 +1,4 @@ -define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focusManager', 'appRouter'], function (connectionManager, playbackManager, events, inputManager, focusManager, appRouter) { +define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'inputManager', 'focusManager', 'appRouter'], function (connectionManager, playbackManager, syncPlayManager, events, inputManager, focusManager, appRouter) { 'use strict'; var serverNotifications = {}; @@ -142,12 +142,12 @@ define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focus function onMessageReceived(e, msg) { var apiClient = this; - if (msg.MessageType === "Play") { + if (msg.MessageType === 'Play') { notifyApp(); var serverId = apiClient.serverInfo().Id; - if (msg.Data.PlayCommand === "PlayNext") { + if (msg.Data.PlayCommand === 'PlayNext') { playbackManager.queueNext({ ids: msg.Data.ItemIds, serverId: serverId }); - } else if (msg.Data.PlayCommand === "PlayLast") { + } else if (msg.Data.PlayCommand === 'PlayLast') { playbackManager.queue({ ids: msg.Data.ItemIds, serverId: serverId }); } else { playbackManager.play({ @@ -160,7 +160,7 @@ define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focus serverId: serverId }); } - } else if (msg.MessageType === "Playstate") { + } else if (msg.MessageType === 'Playstate') { if (msg.Data.Command === 'Stop') { inputManager.trigger('stop'); } else if (msg.Data.Command === 'Pause') { @@ -178,22 +178,26 @@ define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focus } else { notifyApp(); } - } else if (msg.MessageType === "GeneralCommand") { + } else if (msg.MessageType === 'GeneralCommand') { var cmd = msg.Data; processGeneralCommand(cmd, apiClient); - } else if (msg.MessageType === "UserDataChanged") { + } else if (msg.MessageType === 'UserDataChanged') { if (msg.Data.UserId === apiClient.getCurrentUserId()) { for (var i = 0, length = msg.Data.UserDataList.length; i < length; i++) { events.trigger(serverNotifications, 'UserDataChanged', [apiClient, msg.Data.UserDataList[i]]); } } + } else if (msg.MessageType === 'SyncPlayCommand') { + syncPlayManager.processCommand(msg.Data, apiClient); + } else if (msg.MessageType === 'SyncPlayGroupUpdate') { + syncPlayManager.processGroupUpdate(msg.Data, apiClient); } else { events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]); } } function bindEvents(apiClient) { - events.off(apiClient, "message", onMessageReceived); - events.on(apiClient, "message", onMessageReceived); + events.off(apiClient, 'message', onMessageReceived); + events.on(apiClient, 'message', onMessageReceived); } connectionManager.getApiClients().forEach(bindEvents); diff --git a/src/scripts/settings/appSettings.js b/src/scripts/settings/appSettings.js index 03ceb346a2..6f0975e98c 100644 --- a/src/scripts/settings/appSettings.js +++ b/src/scripts/settings/appSettings.js @@ -12,7 +12,7 @@ import events from 'events'; } export function enableAutoLogin(val) { - if (val != null) { + if (val !== undefined) { this.set('enableAutoLogin', val.toString()); } @@ -20,7 +20,7 @@ import events from 'events'; } export function enableSystemExternalPlayers(val) { - if (val !== null) { + if (val !== undefined) { this.set('enableSystemExternalPlayers', val.toString()); } @@ -29,7 +29,7 @@ import events from 'events'; export function enableAutomaticBitrateDetection(isInNetwork, mediaType, val) { var key = 'enableautobitratebitrate-' + mediaType + '-' + isInNetwork; - if (val != null) { + if (val !== undefined) { if (isInNetwork && mediaType === 'Audio') { val = true; } @@ -46,7 +46,7 @@ import events from 'events'; export function maxStreamingBitrate(isInNetwork, mediaType, val) { var key = 'maxbitrate-' + mediaType + '-' + isInNetwork; - if (val != null) { + if (val !== undefined) { if (isInNetwork && mediaType === 'Audio') { // nothing to do, this is always a max value } else { @@ -72,7 +72,7 @@ import events from 'events'; } export function maxChromecastBitrate(val) { - if (val != null) { + if (val !== undefined) { this.set('chromecastBitrate1', val); } @@ -81,7 +81,7 @@ import events from 'events'; } export function syncOnlyOnWifi(val) { - if (val != null) { + if (val !== undefined) { this.set('syncOnlyOnWifi', val.toString()); } @@ -89,7 +89,7 @@ import events from 'events'; } export function syncPath(val) { - if (val != null) { + if (val !== undefined) { this.set('syncPath', val); } @@ -97,7 +97,7 @@ import events from 'events'; } export function cameraUploadServers(val) { - if (val != null) { + if (val !== undefined) { this.set('cameraUploadServers', val.join(',')); } @@ -110,7 +110,7 @@ import events from 'events'; } export function runAtStartup(val) { - if (val != null) { + if (val !== undefined) { this.set('runatstartup', val.toString()); } diff --git a/src/scripts/settings/userSettings.js b/src/scripts/settings/userSettings.js index 9f9606dfb8..581d0a295a 100644 --- a/src/scripts/settings/userSettings.js +++ b/src/scripts/settings/userSettings.js @@ -84,7 +84,7 @@ import events from 'events'; } export function enableCinemaMode(val) { - if (val != null) { + if (val !== undefined) { return this.set('enableCinemaMode', val.toString(), false); } @@ -93,7 +93,7 @@ import events from 'events'; } export function enableNextVideoInfoOverlay(val) { - if (val != null) { + if (val !== undefined) { return this.set('enableNextVideoInfoOverlay', val.toString()); } @@ -102,25 +102,25 @@ import events from 'events'; } export function enableThemeSongs(val) { - if (val != null) { + if (val !== undefined) { return this.set('enableThemeSongs', val.toString(), false); } val = this.get('enableThemeSongs', false); - return val !== 'false'; + return val === 'true'; } export function enableThemeVideos(val) { - if (val != null) { + if (val !== undefined) { return this.set('enableThemeVideos', val.toString(), false); } val = this.get('enableThemeVideos', false); - return val !== 'false'; + return val === 'true'; } export function enableFastFadein(val) { - if (val != null) { + if (val !== undefined) { return this.set('fastFadein', val.toString(), false); } @@ -128,8 +128,17 @@ import events from 'events'; return val !== 'false'; } + export function enableBlurhash(val) { + if (val !== undefined) { + return this.set('blurhash', val.toString(), false); + } + + val = this.get('blurhash', false); + return val !== 'false'; + } + export function enableBackdrops(val) { - if (val != null) { + if (val !== undefined) { return this.set('enableBackdrops', val.toString(), false); } @@ -137,8 +146,17 @@ import events from 'events'; return val !== 'false'; } + export function detailsBanner(val) { + if (val !== undefined) { + return this.set('detailsBanner', val.toString(), false); + } + + val = this.get('detailsBanner', false); + return val !== 'false'; + } + export function language(val) { - if (val != null) { + if (val !== undefined) { return this.set('language', val.toString(), false); } @@ -146,15 +164,23 @@ import events from 'events'; } export function dateTimeLocale(val) { - if (val != null) { + if (val !== undefined) { return this.set('datetimelocale', val.toString(), false); } return this.get('datetimelocale', false); } + export function chromecastVersion(val) { + if (val !== undefined) { + return this.set('chromecastVersion', val.toString()); + } + + return this.get('chromecastVersion') || 'stable'; + } + export function skipBackLength(val) { - if (val != null) { + if (val !== undefined) { return this.set('skipBackLength', val.toString()); } @@ -162,7 +188,7 @@ import events from 'events'; } export function skipForwardLength(val) { - if (val != null) { + if (val !== undefined) { return this.set('skipForwardLength', val.toString()); } @@ -170,7 +196,7 @@ import events from 'events'; } export function dashboardTheme(val) { - if (val != null) { + if (val !== undefined) { return this.set('dashboardTheme', val); } @@ -178,7 +204,7 @@ import events from 'events'; } export function skin(val) { - if (val != null) { + if (val !== undefined) { return this.set('skin', val, false); } @@ -186,7 +212,7 @@ import events from 'events'; } export function theme(val) { - if (val != null) { + if (val !== undefined) { return this.set('appTheme', val, false); } @@ -194,15 +220,29 @@ import events from 'events'; } export function screensaver(val) { - if (val != null) { + if (val !== undefined) { return this.set('screensaver', val, false); } return this.get('screensaver', false); } + export function libraryPageSize(val) { + if (val !== undefined) { + return this.set('libraryPageSize', parseInt(val, 10), false); + } + + var libraryPageSize = parseInt(this.get('libraryPageSize', false), 10); + if (libraryPageSize === 0) { + // Explicitly return 0 to avoid returning 100 because 0 is falsy. + return 0; + } else { + return libraryPageSize || 100; + } + } + export function soundEffects(val) { - if (val != null) { + if (val !== undefined) { return this.set('soundeffects', val, false); } @@ -251,3 +291,34 @@ import events from 'events'; } /* eslint-enable indent */ +export default { + setUserInfo: setUserInfo, + getData: getData, + importFrom: importFrom, + set: set, + get: get, + serverConfig: serverConfig, + enableCinemaMode: enableCinemaMode, + enableNextVideoInfoOverlay: enableNextVideoInfoOverlay, + enableThemeSongs: enableThemeSongs, + enableThemeVideos: enableThemeVideos, + enableFastFadein: enableFastFadein, + enableBlurhash: enableBlurhash, + enableBackdrops: enableBackdrops, + language: language, + dateTimeLocale: dateTimeLocale, + skipBackLength: skipBackLength, + skipForwardLength: skipForwardLength, + dashboardTheme: dashboardTheme, + skin: skin, + theme: theme, + screensaver: screensaver, + libraryPageSize: libraryPageSize, + soundEffects: soundEffects, + loadQuerySettings: loadQuerySettings, + saveQuerySettings: saveQuerySettings, + getSubtitleAppearanceSettings: getSubtitleAppearanceSettings, + setSubtitleAppearanceSettings: setSubtitleAppearanceSettings, + setFilter: setFilter, + getFilter: getFilter +}; diff --git a/src/scripts/settings/webSettings.js b/src/scripts/settings/webSettings.js index 4b1b658e9b..64989b4fc2 100644 --- a/src/scripts/settings/webSettings.js +++ b/src/scripts/settings/webSettings.js @@ -2,7 +2,17 @@ let data; function getConfig() { if (data) return Promise.resolve(data); - return fetch("/config.json?nocache=" + new Date().getUTCMilliseconds()).then(function (response) { + return fetch('config.json?nocache=' + new Date().getUTCMilliseconds()).then(response => { + data = response.json(); + return data; + }).catch(error => { + console.warn('web config file is missing so the template will be used'); + return getDefaultConfig(); + }); +} + +function getDefaultConfig() { + return fetch('config.template.json').then(function (response) { data = response.json(); return data; }); @@ -11,5 +21,8 @@ function getConfig() { export function enableMultiServer() { return getConfig().then(config => { return config.multiserver; + }).catch(error => { + console.log('cannot get web config:', error); + return false; }); } diff --git a/src/components/shell.js b/src/scripts/shell.js similarity index 100% rename from src/components/shell.js rename to src/scripts/shell.js diff --git a/src/scripts/site.js b/src/scripts/site.js index 3b992eaf2b..e60efc757d 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -1,36 +1,36 @@ function getWindowLocationSearch(win) { - "use strict"; + 'use strict'; var search = (win || window).location.search; if (!search) { - var index = window.location.href.indexOf("?"); + var index = window.location.href.indexOf('?'); if (-1 != index) { search = window.location.href.substring(index); } } - return search || ""; + return search || ''; } function getParameterByName(name, url) { - "use strict"; + 'use strict'; - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regexS = "[\\?&]" + name + "=([^&#]*)"; - var regex = new RegExp(regexS, "i"); + name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'); + var regexS = '[\\?&]' + name + '=([^&#]*)'; + var regex = new RegExp(regexS, 'i'); var results = regex.exec(url || getWindowLocationSearch()); if (null == results) { - return ""; + return ''; } - return decodeURIComponent(results[1].replace(/\+/g, " ")); + return decodeURIComponent(results[1].replace(/\+/g, ' ')); } function pageClassOn(eventName, className, fn) { - "use strict"; + 'use strict'; document.addEventListener(eventName, function (event) { var target = event.target; @@ -42,7 +42,7 @@ function pageClassOn(eventName, className, fn) { } function pageIdOn(eventName, id, fn) { - "use strict"; + 'use strict'; document.addEventListener(eventName, function (event) { var target = event.target; @@ -71,17 +71,17 @@ var Dashboard = { } var urlLower = window.location.href.toLowerCase(); - var index = urlLower.lastIndexOf("/web"); + var index = urlLower.lastIndexOf('/web'); if (-1 != index) { return urlLower.substring(0, index); } var loc = window.location; - var address = loc.protocol + "//" + loc.hostname; + var address = loc.protocol + '//' + loc.hostname; if (loc.port) { - address += ":" + loc.port; + address += ':' + loc.port; } return address; @@ -104,21 +104,21 @@ var Dashboard = { var loginPage; if (AppInfo.isNativeApp) { - loginPage = "selectserver.html"; + loginPage = 'selectserver.html'; window.ApiClient = null; } else { - loginPage = "login.html"; + loginPage = 'login.html'; } Dashboard.navigate(loginPage); }); }, getConfigurationPageUrl: function (name) { - return "configurationpage?name=" + encodeURIComponent(name); + return 'configurationpage?name=' + encodeURIComponent(name); }, getConfigurationResourceUrl: function (name) { if (AppInfo.isNativeApp) { - return ApiClient.getUrl("web/ConfigurationPage", { + return ApiClient.getUrl('web/ConfigurationPage', { name: name }); } @@ -127,7 +127,7 @@ var Dashboard = { }, navigate: function (url, preserveQueryString) { if (!url) { - throw new Error("url cannot be null or empty"); + throw new Error('url cannot be null or empty'); } var queryString = getWindowLocationSearch(); @@ -137,36 +137,36 @@ var Dashboard = { } return new Promise(function (resolve, reject) { - require(["appRouter"], function (appRouter) { + require(['appRouter'], function (appRouter) { return appRouter.show(url).then(resolve, reject); }); }); }, navigate_direct: function (path) { return new Promise(function (resolve, reject) { - require(["appRouter"], function (appRouter) { + require(['appRouter'], function (appRouter) { return appRouter.showDirect(path).then(resolve, reject); }); }); }, processPluginConfigurationUpdateResult: function () { - require(["loading", "toast"], function (loading, toast) { + require(['loading', 'toast'], function (loading, toast) { loading.hide(); - toast(Globalize.translate("MessageSettingsSaved")); + toast(Globalize.translate('MessageSettingsSaved')); }); }, processServerConfigurationUpdateResult: function (result) { - require(["loading", "toast"], function (loading, toast) { + require(['loading', 'toast'], function (loading, toast) { loading.hide(); - toast(Globalize.translate("MessageSettingsSaved")); + toast(Globalize.translate('MessageSettingsSaved')); }); }, processErrorResponse: function (response) { - require(["loading"], function (loading) { + require(['loading'], function (loading) { loading.hide(); }); - var status = "" + response.status; + var status = '' + response.status; if (response.statusText) { status = response.statusText; @@ -174,21 +174,21 @@ var Dashboard = { Dashboard.alert({ title: status, - message: response.headers ? response.headers.get("X-Application-Error-Code") : null + message: response.headers ? response.headers.get('X-Application-Error-Code') : null }); }, alert: function (options) { - if ("string" == typeof options) { - return void require(["toast"], function (toast) { + if ('string' == typeof options) { + return void require(['toast'], function (toast) { toast({ text: options }); }); } - require(["alert"], function (alert) { + require(['alert'], function (alert) { alert({ - title: options.title || Globalize.translate("HeaderAlert"), + title: options.title || Globalize.translate('HeaderAlert'), text: options.message }).then(options.callback || function () {}); }); @@ -197,11 +197,11 @@ var Dashboard = { var apiClient = window.ApiClient; if (apiClient) { - require(["serverRestartDialog", "events"], function (ServerRestartDialog, events) { + require(['serverRestartDialog', 'events'], function (ServerRestartDialog, events) { var dialog = new ServerRestartDialog({ apiClient: apiClient }); - events.on(dialog, "restarted", function () { + events.on(dialog, 'restarted', function () { if (AppInfo.isNativeApp) { apiClient.ensureWebSocket(); } else { @@ -214,19 +214,19 @@ var Dashboard = { }, capabilities: function (appHost) { var capabilities = { - PlayableMediaTypes: ["Audio", "Video"], - SupportedCommands: ["MoveUp", "MoveDown", "MoveLeft", "MoveRight", "PageUp", "PageDown", "PreviousLetter", "NextLetter", "ToggleOsd", "ToggleContextMenu", "Select", "Back", "SendKey", "SendString", "GoHome", "GoToSettings", "VolumeUp", "VolumeDown", "Mute", "Unmute", "ToggleMute", "SetVolume", "SetAudioStreamIndex", "SetSubtitleStreamIndex", "DisplayContent", "GoToSearch", "DisplayMessage", "SetRepeatMode", "ChannelUp", "ChannelDown", "PlayMediaSource", "PlayTrailers"], - SupportsPersistentIdentifier: "cordova" === self.appMode || "android" === self.appMode, + PlayableMediaTypes: ['Audio', 'Video'], + SupportedCommands: ['MoveUp', 'MoveDown', 'MoveLeft', 'MoveRight', 'PageUp', 'PageDown', 'PreviousLetter', 'NextLetter', 'ToggleOsd', 'ToggleContextMenu', 'Select', 'Back', 'SendKey', 'SendString', 'GoHome', 'GoToSettings', 'VolumeUp', 'VolumeDown', 'Mute', 'Unmute', 'ToggleMute', 'SetVolume', 'SetAudioStreamIndex', 'SetSubtitleStreamIndex', 'DisplayContent', 'GoToSearch', 'DisplayMessage', 'SetRepeatMode', 'ChannelUp', 'ChannelDown', 'PlayMediaSource', 'PlayTrailers'], + SupportsPersistentIdentifier: 'cordova' === self.appMode || 'android' === self.appMode, SupportsMediaControl: true }; appHost.getPushTokenInfo(); return capabilities = Object.assign(capabilities, appHost.getPushTokenInfo()); }, selectServer: function () { - if (window.NativeShell && typeof window.NativeShell.selectServer === "function") { + if (window.NativeShell && typeof window.NativeShell.selectServer === 'function') { window.NativeShell.selectServer(); } else { - Dashboard.navigate("selectserver.html"); + Dashboard.navigate('selectserver.html'); } } }; @@ -234,11 +234,11 @@ var Dashboard = { var AppInfo = {}; !function () { - "use strict"; + 'use strict'; function defineConnectionManager(connectionManager) { window.ConnectionManager = connectionManager; - define("connectionManager", [], function () { + define('connectionManager', [], function () { return connectionManager; }); } @@ -264,13 +264,13 @@ var AppInfo = {}; return userSettings.setUserInfo(user.Id, localApiClient); }; - events.on(connectionManager, "localusersignedout", function () { + events.on(connectionManager, 'localusersignedout', function () { userSettings.setUserInfo(null, null); }); } function createConnectionManager() { - return require(["connectionManagerFactory", "apphost", "credentialprovider", "events", "userSettings"], function (ConnectionManager, apphost, credentialProvider, events, userSettings) { + return require(['connectionManagerFactory', 'apphost', 'credentialprovider', 'events', 'userSettings'], function (ConnectionManager, apphost, credentialProvider, events, userSettings) { var credentialProviderInstance = new credentialProvider(); var promises = [apphost.getSyncProfile(), apphost.init()]; @@ -286,10 +286,10 @@ var AppInfo = {}; bindConnectionManagerEvents(connectionManager, events, userSettings); if (!AppInfo.isNativeApp) { - console.debug("loading ApiClient singleton"); + console.debug('loading ApiClient singleton'); - return require(["apiclient"], function (apiClientFactory) { - console.debug("creating ApiClient singleton"); + return require(['apiclient'], function (apiClientFactory) { + console.debug('creating ApiClient singleton'); var apiClient = new apiClientFactory(Dashboard.serverAddress(), apphost.appName(), apphost.appVersion(), apphost.deviceName(), apphost.deviceId()); @@ -301,7 +301,7 @@ var AppInfo = {}; window.ApiClient = apiClient; localApiClient = apiClient; - console.debug("loaded ApiClient singleton"); + console.debug('loaded ApiClient singleton'); }); } @@ -314,28 +314,35 @@ var AppInfo = {}; return obj; } + function returnDefault(obj) { + if (obj.default === null) { + throw new Error('Object has no default!'); + } + return obj.default; + } + function getBowerPath() { - return "libraries"; + return 'libraries'; } function getComponentsPath() { - return "components"; + return 'components'; } function getElementsPath() { - return "elements"; + return 'elements'; } function getScriptsPath() { - return "scripts"; + return 'scripts'; } function getPlaybackManager(playbackManager) { - window.addEventListener("beforeunload", function () { + window.addEventListener('beforeunload', function () { try { playbackManager.onAppClose(); } catch (err) { - console.error("error in onAppClose: " + err); + console.error('error in onAppClose: ' + err); } }); return playbackManager; @@ -350,106 +357,77 @@ var AppInfo = {}; return layoutManager; } - function createWindowHeadroom(Headroom) { - var headroom = new Headroom([], {}); - return headroom; - } - function createSharedAppFooter(appFooter) { return new appFooter({}); } function onRequireJsError(requireType, requireModules) { - console.error("RequireJS error: " + (requireType || "unknown") + ". Failed modules: " + (requireModules || []).join(",")); + console.error('RequireJS error: ' + (requireType || 'unknown') + '. Failed modules: ' + (requireModules || []).join(',')); } function defineResizeObserver() { if (self.ResizeObserver) { - define("ResizeObserver", [], function () { + define('ResizeObserver', [], function () { return self.ResizeObserver; }); } else { - define("ResizeObserver", ["resize-observer-polyfill"], returnFirstDependency); + define('ResizeObserver', ['resize-observer-polyfill'], returnFirstDependency); } } - function initRequireWithBrowser(browser) { - var bowerPath = getBowerPath(); + function initRequireWithBrowser() { var componentsPath = getComponentsPath(); + var scriptsPath = getScriptsPath(); - define("filesystem", [componentsPath + "/filesystem"], returnFirstDependency); + define('filesystem', [scriptsPath + '/filesystem'], returnFirstDependency); - if (window.IntersectionObserver && !browser.edge) { - define("lazyLoader", [componentsPath + "/lazyloader/lazyloader-intersectionobserver"], returnFirstDependency); - } else { - define("lazyLoader", [componentsPath + "/lazyloader/lazyloader-scroll"], returnFirstDependency); - } + define('lazyLoader', [componentsPath + '/lazyLoader/lazyLoaderIntersectionObserver'], returnFirstDependency); + define('shell', [scriptsPath + '/shell'], returnFirstDependency); - define("shell", [componentsPath + "/shell"], returnFirstDependency); + define('registerElement', ['document-register-element'], returnFirstDependency); - define("apiclient", [bowerPath + "/apiclient/apiclient"], returnFirstDependency); - - if ("registerElement" in document) { - define("registerElement", []); - } else if (browser.msie) { - define("registerElement", ["webcomponents"], returnFirstDependency); - } else { - define("registerElement", ["document-register-element"], returnFirstDependency); - } - - define("imageFetcher", [componentsPath + "/images/imageFetcher"], returnFirstDependency); - - var preferNativeAlerts = browser.tv; - - define("alert", [componentsPath + "/alert"], returnFirstDependency); + define('alert', [componentsPath + '/alert'], returnFirstDependency); defineResizeObserver(); - define("dialog", [componentsPath + "/dialog/dialog"], returnFirstDependency); - define("confirm", [componentsPath + "/confirm/confirm"], returnFirstDependency); + define('dialog', [componentsPath + '/dialog/dialog'], returnFirstDependency); - define("prompt", [componentsPath + "/prompt/prompt"], returnFirstDependency); + define('confirm', [componentsPath + '/confirm/confirm'], returnFirstDependency); - define("loading", [componentsPath + "/loading/loading"], returnFirstDependency); - define("multi-download", [componentsPath + "/multidownload"], returnFirstDependency); - define("fileDownloader", [componentsPath + "/filedownloader"], returnFirstDependency); - define("localassetmanager", [bowerPath + "/apiclient/localassetmanager"], returnFirstDependency); + define('prompt', [componentsPath + '/prompt/prompt'], returnFirstDependency); - define("castSenderApiLoader", [componentsPath + "/castSenderApi"], returnFirstDependency); + define('loading', [componentsPath + '/loading/loading'], returnFirstDependency); + define('multi-download', [scriptsPath + '/multiDownload'], returnFirstDependency); + define('fileDownloader', [scriptsPath + '/fileDownloader'], returnFirstDependency); - define("transfermanager", [bowerPath + "/apiclient/sync/transfermanager"], returnFirstDependency); - define("filerepository", [bowerPath + "/apiclient/sync/filerepository"], returnFirstDependency); - define("localsync", [bowerPath + "/apiclient/sync/localsync"], returnFirstDependency); + define('castSenderApiLoader', [componentsPath + '/castSenderApi'], returnFirstDependency); } function init() { - define("livetvcss", ["css!assets/css/livetv.css"], returnFirstDependency); - define("detailtablecss", ["css!assets/css/detailtable.css"], returnFirstDependency); + define('livetvcss', ['css!assets/css/livetv.css'], returnFirstDependency); + define('detailtablecss', ['css!assets/css/detailtable.css'], returnFirstDependency); var promises = []; if (!window.fetch) { - promises.push(require(["fetch"])); - } - if ("function" != typeof Object.assign) { - promises.push(require(["objectassign"])); + promises.push(require(['fetch'])); } Promise.all(promises).then(function () { createConnectionManager().then(function () { - console.debug("initAfterDependencies promises resolved"); + console.debug('initAfterDependencies promises resolved'); - require(["globalize", "browser"], function (globalize, browser) { + require(['globalize', 'browser'], function (globalize, browser) { window.Globalize = globalize; loadCoreDictionary(globalize).then(function () { onGlobalizeInit(browser); }); }); - require(["keyboardnavigation"], function(keyboardnavigation) { + require(['keyboardnavigation'], function(keyboardnavigation) { keyboardnavigation.enable(); }); - require(["mouseManager"]); - require(["focusPreventScroll"]); - require(["autoFocuser"], function(autoFocuser) { + require(['mouseManager']); + require(['focusPreventScroll']); + require(['autoFocuser'], function(autoFocuser) { autoFocuser.enable(); }); require(['globalize', 'connectionManager', 'events'], function (globalize, connectionManager, events) { @@ -460,38 +438,38 @@ var AppInfo = {}; } function loadCoreDictionary(globalize) { - var languages = ["ar", "be-by", "bg-bg", "ca", "cs", "da", "de", "el", "en-gb", "en-us", "es", "es-ar", "es-mx", "fa", "fi", "fr", "fr-ca", "gsw", "he", "hi-in", "hr", "hu", "id", "it", "kk", "ko", "lt-lt", "ms", "nb", "nl", "pl", "pt-br", "pt-pt", "ro", "ru", "sk", "sl-si", "sv", "tr", "uk", "vi", "zh-cn", "zh-hk", "zh-tw"]; + var languages = ['ar', 'be-by', 'bg-bg', 'ca', 'cs', 'da', 'de', 'el', 'en-gb', 'en-us', 'es', 'es-ar', 'es-mx', 'fa', 'fi', 'fr', 'fr-ca', 'gsw', 'he', 'hi-in', 'hr', 'hu', 'id', 'it', 'kk', 'ko', 'lt-lt', 'ms', 'nb', 'nl', 'pl', 'pt-br', 'pt-pt', 'ro', 'ru', 'sk', 'sl-si', 'sv', 'tr', 'uk', 'vi', 'zh-cn', 'zh-hk', 'zh-tw']; var translations = languages.map(function (language) { return { lang: language, - path: "strings/" + language + ".json" + path: 'strings/' + language + '.json' }; }); - globalize.defaultModule("core"); + globalize.defaultModule('core'); return globalize.loadStrings({ - name: "core", + name: 'core', translations: translations }); } function onGlobalizeInit(browser) { - if ("android" === self.appMode) { - if (-1 !== self.location.href.toString().toLowerCase().indexOf("start=backgroundsync")) { + if ('android' === self.appMode) { + if (-1 !== self.location.href.toString().toLowerCase().indexOf('start=backgroundsync')) { return onAppReady(browser); } } - document.title = Globalize.translateDocument(document.title, "core"); + document.title = Globalize.translateDocument(document.title, 'core'); if (browser.tv && !browser.android) { - console.debug("using system fonts with explicit sizes"); - require(["systemFontsSizedCss"]); + console.debug('using system fonts with explicit sizes'); + require(['systemFontsSizedCss']); } else { - console.debug("using default fonts"); - require(["systemFontsCss"]); + console.debug('using default fonts'); + require(['systemFontsCss']); } - require(["apphost", "css!assets/css/librarybrowser"], function (appHost) { + require(['apphost', 'css!assets/css/librarybrowser'], function (appHost) { loadPlugins(appHost, browser).then(function () { onAppReady(browser); }); @@ -499,23 +477,24 @@ var AppInfo = {}; } function loadPlugins(appHost, browser, shell) { - console.debug("loading installed plugins"); + console.debug('loading installed plugins'); var list = [ - "components/playback/playaccessvalidation", - "components/playback/experimentalwarnings", - "components/htmlaudioplayer/plugin", - "components/htmlvideoplayer/plugin", - "components/photoplayer/plugin", - "components/youtubeplayer/plugin", - "components/backdropscreensaver/plugin", - "components/logoscreensaver/plugin" + 'plugins/playAccessValidation/plugin', + 'plugins/experimentalWarnings/plugin', + 'plugins/htmlAudioPlayer/plugin', + 'plugins/htmlVideoPlayer/plugin', + 'plugins/photoPlayer/plugin', + 'plugins/bookPlayer/plugin', + 'plugins/youtubePlayer/plugin', + 'plugins/backdropScreensaver/plugin', + 'plugins/logoScreensaver/plugin' ]; - if (appHost.supports("remotecontrol")) { - list.push("components/sessionplayer"); + if (appHost.supports('remotecontrol')) { + list.push('plugins/sessionPlayer/plugin'); if (browser.chrome || browser.opera) { - list.push("components/chromecast/chromecastplayer"); + list.push('plugins/chromecastPlayer/plugin'); } } @@ -525,7 +504,7 @@ var AppInfo = {}; return new Promise(function (resolve, reject) { Promise.all(list.map(loadPlugin)).then(function () { - require(["packageManager"], function (packageManager) { + require(['packageManager'], function (packageManager) { packageManager.init().then(resolve, reject); }); }, reject); @@ -534,65 +513,69 @@ var AppInfo = {}; function loadPlugin(url) { return new Promise(function (resolve, reject) { - require(["pluginManager"], function (pluginManager) { + require(['pluginManager'], function (pluginManager) { pluginManager.loadPlugin(url).then(resolve, reject); }); }); } function onAppReady(browser) { - console.debug("begin onAppReady"); + console.debug('begin onAppReady'); // ensure that appHost is loaded in this point require(['apphost', 'appRouter'], function (appHost, appRouter) { window.Emby = {}; - console.debug("onAppReady: loading dependencies"); + console.debug('onAppReady: loading dependencies'); if (browser.iOS) { require(['css!assets/css/ios.css']); } window.Emby.Page = appRouter; - require(['emby-button', 'scripts/themeloader', 'libraryMenu', 'scripts/routes'], function () { + require(['emby-button', 'scripts/themeLoader', 'libraryMenu', 'scripts/routes'], function () { Emby.Page.start({ click: false, hashbang: true }); - require(["components/thememediaplayer", "scripts/autobackdrops"]); + require(['components/themeMediaPlayer', 'scripts/autoBackdrops']); if (!browser.tv && !browser.xboxOne && !browser.ps4) { - require(["components/nowplayingbar/nowplayingbar"]); + require(['components/nowPlayingBar/nowPlayingBar']); } - if (appHost.supports("remotecontrol")) { - require(["playerSelectionMenu", "components/playback/remotecontrolautoplay"]); + if (appHost.supports('remotecontrol')) { + require(['playerSelectionMenu', 'components/playback/remotecontrolautoplay']); } - require(["components/screensavermanager"]); + require(['libraries/screensavermanager']); - if (!appHost.supports("physicalvolumecontrol") || browser.touch) { - require(["components/playback/volumeosd"]); + if (!appHost.supports('physicalvolumecontrol') || browser.touch) { + require(['components/playback/volumeosd']); } - require(["mediaSession", "serverNotifications"]); - require(["date-fns", "date-fns/locale"]); + /* eslint-disable-next-line compat/compat */ + if (navigator.mediaSession || window.NativeShell) { + require(['mediaSession']); + } + require(['serverNotifications']); + require(['date-fns', 'date-fns/locale']); if (!browser.tv && !browser.xboxOne) { - require(["components/playback/playbackorientation"]); + require(['components/playback/playbackorientation']); registerServiceWorker(); if (window.Notification) { - require(["components/notifications/notifications"]); + require(['components/notifications/notifications']); } } - require(["playerSelectionMenu", "fullscreenManager"]); + require(['playerSelectionMenu']); var apiClient = window.ConnectionManager && window.ConnectionManager.currentApiClient(); if (apiClient) { - fetch(apiClient.getUrl("Branding/Css")) + fetch(apiClient.getUrl('Branding/Css')) .then(function(response) { if (!response.ok) { throw new Error(response.status + ' ' + response.statusText); @@ -615,17 +598,21 @@ var AppInfo = {}; } function registerServiceWorker() { - if (navigator.serviceWorker && "cordova" !== self.appMode && "android" !== self.appMode) { + /* eslint-disable compat/compat */ + if (navigator.serviceWorker && self.appMode !== 'cordova' && self.appMode !== 'android') { try { - navigator.serviceWorker.register("serviceworker.js"); + navigator.serviceWorker.register('serviceworker.js'); } catch (err) { - console.error("error registering serviceWorker: " + err); + console.error('error registering serviceWorker: ' + err); } + } else { + console.warn('serviceWorker unsupported'); } + /* eslint-enable compat/compat */ } - function onWebComponentsReady(browser) { - initRequireWithBrowser(browser); + function onWebComponentsReady() { + initRequireWithBrowser(); if (self.appMode === 'cordova' || self.appMode === 'android' || self.appMode === 'standalone') { AppInfo.isNativeApp = true; @@ -637,7 +624,7 @@ var AppInfo = {}; var localApiClient; (function () { - var urlArgs = "v=" + (window.dashboardVersion || new Date().getDate()); + var urlArgs = 'v=' + (window.dashboardVersion || new Date().getDate()); var bowerPath = getBowerPath(); var componentsPath = getComponentsPath(); @@ -645,62 +632,73 @@ var AppInfo = {}; var scriptsPath = getScriptsPath(); var paths = { - browserdeviceprofile: "scripts/browserdeviceprofile", - browser: "scripts/browser", - libraryBrowser: "scripts/librarybrowser", - inputManager: "scripts/inputManager", - datetime: "scripts/datetime", - globalize: "scripts/globalize", - dfnshelper: "scripts/dfnshelper", - libraryMenu: "scripts/librarymenu", - playlisteditor: componentsPath + "/playlisteditor/playlisteditor", - medialibrarycreator: componentsPath + "/medialibrarycreator/medialibrarycreator", - medialibraryeditor: componentsPath + "/medialibraryeditor/medialibraryeditor", - imageoptionseditor: componentsPath + "/imageoptionseditor/imageoptionseditor", - apphost: componentsPath + "/apphost", - visibleinviewport: componentsPath + "/visibleinviewport", - qualityoptions: componentsPath + "/qualityoptions", - focusManager: componentsPath + "/focusManager", - itemHelper: componentsPath + "/itemhelper", - itemShortcuts: componentsPath + "/shortcuts", - playQueueManager: componentsPath + "/playback/playqueuemanager", - nowPlayingHelper: componentsPath + "/playback/nowplayinghelper", - pluginManager: componentsPath + "/pluginManager", - packageManager: componentsPath + "/packagemanager", - screensaverManager: componentsPath + "/screensavermanager" + browserdeviceprofile: 'scripts/browserDeviceProfile', + browser: 'scripts/browser', + libraryBrowser: 'scripts/libraryBrowser', + inputManager: 'scripts/inputManager', + datetime: 'scripts/datetime', + globalize: 'scripts/globalize', + dfnshelper: 'scripts/dfnshelper', + libraryMenu: 'scripts/libraryMenu', + playlisteditor: componentsPath + '/playlisteditor/playlisteditor', + medialibrarycreator: componentsPath + '/mediaLibraryCreator/mediaLibraryCreator', + medialibraryeditor: componentsPath + '/mediaLibraryEditor/mediaLibraryEditor', + imageoptionseditor: componentsPath + '/imageOptionsEditor/imageOptionsEditor', + apphost: componentsPath + '/apphost', + visibleinviewport: bowerPath + '/visibleinviewport', + qualityoptions: componentsPath + '/qualityOptions', + focusManager: componentsPath + '/focusManager', + itemHelper: componentsPath + '/itemHelper', + itemShortcuts: componentsPath + '/shortcuts', + playQueueManager: componentsPath + '/playback/playqueuemanager', + nowPlayingHelper: componentsPath + '/playback/nowplayinghelper', + pluginManager: componentsPath + '/pluginManager', + packageManager: componentsPath + '/packagemanager', + screensaverManager: componentsPath + '/screensavermanager', + chromecastHelper: 'plugins/chromecastPlayer/chromecastHelpers' }; requirejs.onError = onRequireJsError; requirejs.config({ waitSeconds: 0, map: { - "*": { - css: "components/require/requirecss", - text: "components/require/requiretext" + '*': { + css: 'components/require/requirecss', + text: 'components/require/requiretext' } }, bundles: { bundle: [ - "document-register-element", - "fetch", - "flvjs", - "jstree", - "jQuery", - "hlsjs", - "howler", - "native-promise-only", - "resize-observer-polyfill", - "shaka", - "swiper", - "queryString", - "sortable", - "webcomponents", - "material-icons", - "jellyfin-noto", - "date-fns", - "page", - "polyfill", - "classlist-polyfill" + 'document-register-element', + 'fetch', + 'flvjs', + 'jstree', + 'epubjs', + 'jQuery', + 'hlsjs', + 'howler', + 'native-promise-only', + 'resize-observer-polyfill', + 'shaka', + 'swiper', + 'queryString', + 'sortable', + 'webcomponents', + 'material-icons', + 'jellyfin-noto', + 'date-fns', + 'page', + 'polyfill', + 'fast-text-encoding', + 'intersection-observer', + 'classlist-polyfill', + 'screenfull', + 'headroom', + 'apiclient', + 'events', + 'credentialprovider', + 'connectionManagerFactory', + 'appStorage' ] }, urlArgs: urlArgs, @@ -708,184 +706,176 @@ var AppInfo = {}; onError: onRequireJsError }); - require(["polyfill"]); - require(["classlist-polyfill"]); + require(['fetch']); + require(['polyfill']); + require(['fast-text-encoding']); + require(['intersection-observer']); + require(['classlist-polyfill']); // Expose jQuery globally - require(["jQuery"], function(jQuery) { + require(['jQuery'], function(jQuery) { window.$ = jQuery; window.jQuery = jQuery; }); - require(["css!assets/css/site"]); - require(["jellyfin-noto"]); + require(['css!assets/css/site']); + require(['jellyfin-noto']); // define styles // TODO determine which of these files can be moved to the components themselves - define("systemFontsCss", ["css!assets/css/fonts"], returnFirstDependency); - define("systemFontsSizedCss", ["css!assets/css/fonts.sized"], returnFirstDependency); - define("scrollStyles", ["css!assets/css/scrollstyles"], returnFirstDependency); - define("dashboardcss", ["css!assets/css/dashboard"], returnFirstDependency); - define("programStyles", ["css!" + componentsPath + "/guide/programs"], returnFirstDependency); - define("listViewStyle", ["css!" + componentsPath + "/listview/listview"], returnFirstDependency); - define("formDialogStyle", ["css!" + componentsPath + "/formdialog"], returnFirstDependency); - define("clearButtonStyle", ["css!assets/css/clearbutton"], returnFirstDependency); - define("cardStyle", ["css!" + componentsPath + "/cardbuilder/card"], returnFirstDependency); - define("flexStyles", ["css!assets/css/flexstyles"], returnFirstDependency); + define('systemFontsCss', ['css!assets/css/fonts'], returnFirstDependency); + define('systemFontsSizedCss', ['css!assets/css/fonts.sized'], returnFirstDependency); + define('scrollStyles', ['css!assets/css/scrollstyles'], returnFirstDependency); + define('dashboardcss', ['css!assets/css/dashboard'], returnFirstDependency); + define('programStyles', ['css!' + componentsPath + '/guide/programs'], returnFirstDependency); + define('listViewStyle', ['css!' + componentsPath + '/listview/listview'], returnFirstDependency); + define('formDialogStyle', ['css!' + componentsPath + '/formdialog'], returnFirstDependency); + define('clearButtonStyle', ['css!assets/css/clearbutton'], returnFirstDependency); + define('cardStyle', ['css!' + componentsPath + '/cardbuilder/card'], returnFirstDependency); + define('flexStyles', ['css!assets/css/flexstyles'], returnFirstDependency); // define legacy features // TODO delete the rest of these - define("fnchecked", ["legacy/fnchecked"], returnFirstDependency); - define("legacyDashboard", ["legacy/dashboard"], returnFirstDependency); - define("legacySelectMenu", ["legacy/selectmenu"], returnFirstDependency); + define('fnchecked', ['legacy/fnchecked'], returnFirstDependency); + define('legacyDashboard', ['legacy/dashboard'], returnFirstDependency); + define('legacySelectMenu', ['legacy/selectmenu'], returnFirstDependency); // there are several objects that need to be instantiated // TODO find a better way to do this - define("appFooter", [componentsPath + "/appfooter/appfooter"], returnFirstDependency); - define("appFooter-shared", ["appFooter"], createSharedAppFooter); - - // TODO pull apiclient out of this repository - define('events', [bowerPath + "/apiclient/events"], returnFirstDependency); - define('credentialprovider', [bowerPath + "/apiclient/credentialprovider"], returnFirstDependency); - define('connectionManagerFactory', [bowerPath + "/apiclient/connectionmanager"], returnFirstDependency); - define('appStorage', [bowerPath + "/apiclient/appStorage"], returnFirstDependency); - define("serversync", [bowerPath + "/apiclient/sync/serversync"], returnFirstDependency); - define("multiserversync", [bowerPath + "/apiclient/sync/multiserversync"], returnFirstDependency); - define("mediasync", [bowerPath + "/apiclient/sync/mediasync"], returnFirstDependency); - define("itemrepository", [bowerPath + "/apiclient/sync/itemrepository"], returnFirstDependency); - define("useractionrepository", [bowerPath + "/apiclient/sync/useractionrepository"], returnFirstDependency); + define('appFooter', [componentsPath + '/appFooter/appFooter'], returnFirstDependency); + define('appFooter-shared', ['appFooter'], createSharedAppFooter); // TODO remove these libraries // all of these have been modified so we need to fix that first - define("headroom", [componentsPath + "/headroom/headroom"], returnFirstDependency); - define("scroller", [componentsPath + "/scroller"], returnFirstDependency); - define("navdrawer", [componentsPath + "/navdrawer/navdrawer"], returnFirstDependency); + define('scroller', [bowerPath + '/scroller'], returnFirstDependency); + define('navdrawer', [bowerPath + '/navdrawer/navdrawer'], returnFirstDependency); - define("emby-button", [elementsPath + "/emby-button/emby-button"], returnFirstDependency); - define("paper-icon-button-light", [elementsPath + "/emby-button/paper-icon-button-light"], returnFirstDependency); - define("emby-checkbox", [elementsPath + "/emby-checkbox/emby-checkbox"], returnFirstDependency); - define("emby-collapse", [elementsPath + "/emby-collapse/emby-collapse"], returnFirstDependency); - define("emby-input", [elementsPath + "/emby-input/emby-input"], returnFirstDependency); - define("emby-progressring", [elementsPath + "/emby-progressring/emby-progressring"], returnFirstDependency); - define("emby-radio", [elementsPath + "/emby-radio/emby-radio"], returnFirstDependency); - define("emby-select", [elementsPath + "/emby-select/emby-select"], returnFirstDependency); - define("emby-slider", [elementsPath + "/emby-slider/emby-slider"], returnFirstDependency); - define("emby-textarea", [elementsPath + "/emby-textarea/emby-textarea"], returnFirstDependency); - define("emby-toggle", [elementsPath + "/emby-toggle/emby-toggle"], returnFirstDependency); + define('emby-button', [elementsPath + '/emby-button/emby-button'], returnFirstDependency); + define('paper-icon-button-light', [elementsPath + '/emby-button/paper-icon-button-light'], returnFirstDependency); + define('emby-checkbox', [elementsPath + '/emby-checkbox/emby-checkbox'], returnFirstDependency); + define('emby-collapse', [elementsPath + '/emby-collapse/emby-collapse'], returnFirstDependency); + define('emby-input', [elementsPath + '/emby-input/emby-input'], returnFirstDependency); + define('emby-progressring', [elementsPath + '/emby-progressring/emby-progressring'], returnFirstDependency); + define('emby-radio', [elementsPath + '/emby-radio/emby-radio'], returnFirstDependency); + define('emby-select', [elementsPath + '/emby-select/emby-select'], returnFirstDependency); + define('emby-slider', [elementsPath + '/emby-slider/emby-slider'], returnFirstDependency); + define('emby-textarea', [elementsPath + '/emby-textarea/emby-textarea'], returnFirstDependency); + define('emby-toggle', [elementsPath + '/emby-toggle/emby-toggle'], returnFirstDependency); + define('emby-scroller', [elementsPath + '/emby-scroller/emby-scroller'], returnFirstDependency); + define('emby-tabs', [elementsPath + '/emby-tabs/emby-tabs'], returnFirstDependency); + define('emby-scrollbuttons', [elementsPath + '/emby-scrollbuttons/emby-scrollbuttons'], returnFirstDependency); + define('emby-itemrefreshindicator', [elementsPath + '/emby-itemrefreshindicator/emby-itemrefreshindicator'], returnFirstDependency); + define('emby-itemscontainer', [elementsPath + '/emby-itemscontainer/emby-itemscontainer'], returnFirstDependency); + define('emby-playstatebutton', [elementsPath + '/emby-playstatebutton/emby-playstatebutton'], returnFirstDependency); + define('emby-ratingbutton', [elementsPath + '/emby-ratingbutton/emby-ratingbutton'], returnFirstDependency); + define('emby-progressbar', [elementsPath + '/emby-progressbar/emby-progressbar'], returnFirstDependency); + define('emby-programcell', [elementsPath + '/emby-programcell/emby-programcell'], returnFirstDependency); - define("webSettings", [scriptsPath + "/settings/webSettings"], returnFirstDependency); - define("appSettings", [scriptsPath + "/settings/appSettings"], returnFirstDependency); - define("userSettings", [scriptsPath + "/settings/userSettings"], returnFirstDependency); + define('webSettings', [scriptsPath + '/settings/webSettings'], returnFirstDependency); + define('appSettings', [scriptsPath + '/settings/appSettings'], returnFirstDependency); + define('userSettings', [scriptsPath + '/settings/userSettings'], returnFirstDependency); - define("chromecastHelper", [componentsPath + "/chromecast/chromecasthelpers"], returnFirstDependency); - define("mediaSession", [componentsPath + "/playback/mediasession"], returnFirstDependency); - define("actionsheet", [componentsPath + "/actionsheet/actionsheet"], returnFirstDependency); - define("tunerPicker", [componentsPath + "/tunerpicker"], returnFirstDependency); - define("mainTabsManager", [componentsPath + "/maintabsmanager"], returnFirstDependency); - define("imageLoader", [componentsPath + "/images/imageLoader"], returnFirstDependency); - define("directorybrowser", [componentsPath + "/directorybrowser/directorybrowser"], returnFirstDependency); - define("metadataEditor", [componentsPath + "/metadataeditor/metadataeditor"], returnFirstDependency); - define("personEditor", [componentsPath + "/metadataeditor/personeditor"], returnFirstDependency); - define("playerSelectionMenu", [componentsPath + "/playback/playerSelectionMenu"], returnFirstDependency); - define("playerSettingsMenu", [componentsPath + "/playback/playersettingsmenu"], returnFirstDependency); - define("playMethodHelper", [componentsPath + "/playback/playmethodhelper"], returnFirstDependency); - define("brightnessOsd", [componentsPath + "/playback/brightnessosd"], returnFirstDependency); - define("emby-itemscontainer", [componentsPath + "/emby-itemscontainer/emby-itemscontainer"], returnFirstDependency); - define("alphaNumericShortcuts", [componentsPath + "/alphanumericshortcuts/alphanumericshortcuts"], returnFirstDependency); - define("emby-scroller", [componentsPath + "/emby-scroller/emby-scroller"], returnFirstDependency); - define("emby-tabs", [componentsPath + "/emby-tabs/emby-tabs"], returnFirstDependency); - define("emby-scrollbuttons", [componentsPath + "/emby-scrollbuttons/emby-scrollbuttons"], returnFirstDependency); - define("emby-itemrefreshindicator", [componentsPath + "/emby-itemrefreshindicator/emby-itemrefreshindicator"], returnFirstDependency); - define("multiSelect", [componentsPath + "/multiselect/multiselect"], returnFirstDependency); - define("alphaPicker", [componentsPath + "/alphapicker/alphapicker"], returnFirstDependency); - define("tabbedView", [componentsPath + "/tabbedview/tabbedview"], returnFirstDependency); - define("itemsTab", [componentsPath + "/tabbedview/itemstab"], returnFirstDependency); - define("collectionEditor", [componentsPath + "/collectioneditor/collectioneditor"], returnFirstDependency); - define("serverRestartDialog", [componentsPath + "/serverRestartDialog"], returnFirstDependency); - define("playlistEditor", [componentsPath + "/playlisteditor/playlisteditor"], returnFirstDependency); - define("recordingCreator", [componentsPath + "/recordingcreator/recordingcreator"], returnFirstDependency); - define("recordingEditor", [componentsPath + "/recordingcreator/recordingeditor"], returnFirstDependency); - define("seriesRecordingEditor", [componentsPath + "/recordingcreator/seriesrecordingeditor"], returnFirstDependency); - define("recordingFields", [componentsPath + "/recordingcreator/recordingfields"], returnFirstDependency); - define("recordingButton", [componentsPath + "/recordingcreator/recordingbutton"], returnFirstDependency); - define("recordingHelper", [componentsPath + "/recordingcreator/recordinghelper"], returnFirstDependency); - define("subtitleEditor", [componentsPath + "/subtitleeditor/subtitleeditor"], returnFirstDependency); - define("subtitleSync", [componentsPath + "/subtitlesync/subtitlesync"], returnFirstDependency); - define("itemIdentifier", [componentsPath + "/itemidentifier/itemidentifier"], returnFirstDependency); - define("itemMediaInfo", [componentsPath + "/itemMediaInfo/itemMediaInfo"], returnFirstDependency); - define("mediaInfo", [componentsPath + "/mediainfo/mediainfo"], returnFirstDependency); - define("itemContextMenu", [componentsPath + "/itemcontextmenu"], returnFirstDependency); - define("imageEditor", [componentsPath + "/imageeditor/imageeditor"], returnFirstDependency); - define("imageDownloader", [componentsPath + "/imagedownloader/imagedownloader"], returnFirstDependency); - define("dom", [componentsPath + "/dom"], returnFirstDependency); - define("playerStats", [componentsPath + "/playerstats/playerstats"], returnFirstDependency); - define("searchFields", [componentsPath + "/search/searchfields"], returnFirstDependency); - define("searchResults", [componentsPath + "/search/searchresults"], returnFirstDependency); - define("upNextDialog", [componentsPath + "/upnextdialog/upnextdialog"], returnFirstDependency); - define("fullscreen-doubleclick", [componentsPath + "/fullscreen/fullscreen-dc"], returnFirstDependency); - define("fullscreenManager", [componentsPath + "/fullscreenManager", "events"], returnFirstDependency); - define("subtitleAppearanceHelper", [componentsPath + "/subtitlesettings/subtitleappearancehelper"], returnFirstDependency); - define("subtitleSettings", [componentsPath + "/subtitlesettings/subtitlesettings"], returnFirstDependency); - define("displaySettings", [componentsPath + "/displaysettings/displaysettings"], returnFirstDependency); - define("playbackSettings", [componentsPath + "/playbacksettings/playbacksettings"], returnFirstDependency); - define("homescreenSettings", [componentsPath + "/homescreensettings/homescreensettings"], returnFirstDependency); - define("playbackManager", [componentsPath + "/playback/playbackmanager"], getPlaybackManager); - define("layoutManager", [componentsPath + "/layoutManager", "apphost"], getLayoutManager); - define("homeSections", [componentsPath + "/homesections/homesections"], returnFirstDependency); - define("playMenu", [componentsPath + "/playmenu"], returnFirstDependency); - define("refreshDialog", [componentsPath + "/refreshdialog/refreshdialog"], returnFirstDependency); - define("backdrop", [componentsPath + "/backdrop/backdrop"], returnFirstDependency); - define("fetchHelper", [componentsPath + "/fetchhelper"], returnFirstDependency); - define("cardBuilder", [componentsPath + "/cardbuilder/cardBuilder"], returnFirstDependency); - define("peoplecardbuilder", [componentsPath + "/cardbuilder/peoplecardbuilder"], returnFirstDependency); - define("chaptercardbuilder", [componentsPath + "/cardbuilder/chaptercardbuilder"], returnFirstDependency); - define("deleteHelper", [componentsPath + "/deletehelper"], returnFirstDependency); - define("tvguide", [componentsPath + "/guide/guide"], returnFirstDependency); - define("guide-settings-dialog", [componentsPath + "/guide/guide-settings"], returnFirstDependency); - define("loadingDialog", [componentsPath + "/loadingdialog/loadingdialog"], returnFirstDependency); - define("viewManager", [componentsPath + "/viewManager/viewManager"], function (viewManager) { + define('mediaSession', [componentsPath + '/playback/mediasession'], returnFirstDependency); + define('actionsheet', [componentsPath + '/actionSheet/actionSheet'], returnFirstDependency); + define('tunerPicker', [componentsPath + '/tunerPicker'], returnFirstDependency); + define('mainTabsManager', [componentsPath + '/maintabsmanager'], returnFirstDependency); + define('imageLoader', [componentsPath + '/images/imageLoader'], returnFirstDependency); + define('directorybrowser', [componentsPath + '/directorybrowser/directorybrowser'], returnFirstDependency); + define('metadataEditor', [componentsPath + '/metadataEditor/metadataEditor'], returnFirstDependency); + define('personEditor', [componentsPath + '/metadataEditor/personEditor'], returnFirstDependency); + define('playerSelectionMenu', [componentsPath + '/playback/playerSelectionMenu'], returnFirstDependency); + define('playerSettingsMenu', [componentsPath + '/playback/playersettingsmenu'], returnFirstDependency); + define('playMethodHelper', [componentsPath + '/playback/playmethodhelper'], returnFirstDependency); + define('brightnessOsd', [componentsPath + '/playback/brightnessosd'], returnFirstDependency); + define('alphaNumericShortcuts', [scriptsPath + '/alphanumericshortcuts'], returnFirstDependency); + define('multiSelect', [componentsPath + '/multiSelect/multiSelect'], returnFirstDependency); + define('alphaPicker', [componentsPath + '/alphaPicker/alphaPicker'], returnFirstDependency); + define('tabbedView', [componentsPath + '/tabbedview/tabbedview'], returnFirstDependency); + define('itemsTab', [componentsPath + '/tabbedview/itemstab'], returnFirstDependency); + define('collectionEditor', [componentsPath + '/collectionEditor/collectionEditor'], returnFirstDependency); + define('serverRestartDialog', [componentsPath + '/serverRestartDialog'], returnFirstDependency); + define('playlistEditor', [componentsPath + '/playlisteditor/playlisteditor'], returnFirstDependency); + define('recordingCreator', [componentsPath + '/recordingcreator/recordingcreator'], returnFirstDependency); + define('recordingEditor', [componentsPath + '/recordingcreator/recordingeditor'], returnFirstDependency); + define('seriesRecordingEditor', [componentsPath + '/recordingcreator/seriesrecordingeditor'], returnFirstDependency); + define('recordingFields', [componentsPath + '/recordingcreator/recordingfields'], returnFirstDependency); + define('recordingButton', [componentsPath + '/recordingcreator/recordingbutton'], returnFirstDependency); + define('recordingHelper', [componentsPath + '/recordingcreator/recordinghelper'], returnFirstDependency); + define('subtitleEditor', [componentsPath + '/subtitleeditor/subtitleeditor'], returnFirstDependency); + define('subtitleSync', [componentsPath + '/subtitlesync/subtitlesync'], returnFirstDependency); + define('itemIdentifier', [componentsPath + '/itemidentifier/itemidentifier'], returnFirstDependency); + define('itemMediaInfo', [componentsPath + '/itemMediaInfo/itemMediaInfo'], returnFirstDependency); + define('mediaInfo', [componentsPath + '/mediainfo/mediainfo'], returnFirstDependency); + define('itemContextMenu', [componentsPath + '/itemContextMenu'], returnFirstDependency); + define('imageEditor', [componentsPath + '/imageeditor/imageeditor'], returnFirstDependency); + define('imageDownloader', [componentsPath + '/imageDownloader/imageDownloader'], returnFirstDependency); + define('dom', [scriptsPath + '/dom'], returnFirstDependency); + define('playerStats', [componentsPath + '/playerstats/playerstats'], returnFirstDependency); + define('searchFields', [componentsPath + '/search/searchfields'], returnFirstDependency); + define('searchResults', [componentsPath + '/search/searchresults'], returnFirstDependency); + define('upNextDialog', [componentsPath + '/upnextdialog/upnextdialog'], returnFirstDependency); + define('subtitleAppearanceHelper', [componentsPath + '/subtitlesettings/subtitleappearancehelper'], returnFirstDependency); + define('subtitleSettings', [componentsPath + '/subtitlesettings/subtitlesettings'], returnFirstDependency); + define('displaySettings', [componentsPath + '/displaySettings/displaySettings'], returnFirstDependency); + define('playbackSettings', [componentsPath + '/playbackSettings/playbackSettings'], returnFirstDependency); + define('homescreenSettings', [componentsPath + '/homeScreenSettings/homeScreenSettings'], returnFirstDependency); + define('playbackManager', [componentsPath + '/playback/playbackmanager'], getPlaybackManager); + define('timeSyncManager', [componentsPath + '/syncplay/timeSyncManager'], returnDefault); + define('groupSelectionMenu', [componentsPath + '/syncplay/groupSelectionMenu'], returnFirstDependency); + define('syncPlayManager', [componentsPath + '/syncplay/syncPlayManager'], returnDefault); + define('playbackPermissionManager', [componentsPath + '/syncplay/playbackPermissionManager'], returnDefault); + define('layoutManager', [componentsPath + '/layoutManager', 'apphost'], getLayoutManager); + define('homeSections', [componentsPath + '/homesections/homesections'], returnFirstDependency); + define('playMenu', [componentsPath + '/playmenu'], returnFirstDependency); + define('refreshDialog', [componentsPath + '/refreshdialog/refreshdialog'], returnFirstDependency); + define('backdrop', [componentsPath + '/backdrop/backdrop'], returnFirstDependency); + define('fetchHelper', [componentsPath + '/fetchhelper'], returnFirstDependency); + define('cardBuilder', [componentsPath + '/cardbuilder/cardBuilder'], returnFirstDependency); + define('peoplecardbuilder', [componentsPath + '/cardbuilder/peoplecardbuilder'], returnFirstDependency); + define('chaptercardbuilder', [componentsPath + '/cardbuilder/chaptercardbuilder'], returnFirstDependency); + define('deleteHelper', [scriptsPath + '/deleteHelper'], returnFirstDependency); + define('tvguide', [componentsPath + '/guide/guide'], returnFirstDependency); + define('guide-settings-dialog', [componentsPath + '/guide/guide-settings'], returnFirstDependency); + define('loadingDialog', [componentsPath + '/loadingDialog/loadingDialog'], returnFirstDependency); + define('viewManager', [componentsPath + '/viewManager/viewManager'], function (viewManager) { window.ViewManager = viewManager; viewManager.dispatchPageEvents(true); return viewManager; }); - define("slideshow", [componentsPath + "/slideshow/slideshow"], returnFirstDependency); - define("objectassign", [componentsPath + "/polyfills/objectassign"], returnFirstDependency); - define("focusPreventScroll", [componentsPath + "/polyfills/focusPreventScroll"], returnFirstDependency); - define("userdataButtons", [componentsPath + "/userdatabuttons/userdatabuttons"], returnFirstDependency); - define("emby-playstatebutton", [componentsPath + "/userdatabuttons/emby-playstatebutton"], returnFirstDependency); - define("emby-ratingbutton", [componentsPath + "/userdatabuttons/emby-ratingbutton"], returnFirstDependency); - define("listView", [componentsPath + "/listview/listview"], returnFirstDependency); - define("indicators", [componentsPath + "/indicators/indicators"], returnFirstDependency); - define("viewSettings", [componentsPath + "/viewsettings/viewsettings"], returnFirstDependency); - define("filterMenu", [componentsPath + "/filtermenu/filtermenu"], returnFirstDependency); - define("sortMenu", [componentsPath + "/sortmenu/sortmenu"], returnFirstDependency); - define("idb", [componentsPath + "/idb"], returnFirstDependency); - define("sanitizefilename", [componentsPath + "/sanitizefilename"], returnFirstDependency); - define("toast", [componentsPath + "/toast/toast"], returnFirstDependency); - define("scrollHelper", [componentsPath + "/scrollhelper"], returnFirstDependency); - define("touchHelper", [componentsPath + "/touchhelper"], returnFirstDependency); - define("imageUploader", [componentsPath + "/imageuploader/imageuploader"], returnFirstDependency); - define("htmlMediaHelper", [componentsPath + "/htmlMediaHelper"], returnFirstDependency); - define("viewContainer", [componentsPath + "/viewContainer"], returnFirstDependency); - define("dialogHelper", [componentsPath + "/dialogHelper/dialogHelper"], returnFirstDependency); - define("serverNotifications", [componentsPath + "/serverNotifications"], returnFirstDependency); - define("skinManager", [componentsPath + "/skinManager"], returnFirstDependency); - define("keyboardnavigation", [componentsPath + "/input/keyboardnavigation"], returnFirstDependency); - define("mouseManager", [componentsPath + "/input/mouseManager"], returnFirstDependency); - define("scrollManager", [componentsPath + "/scrollManager"], returnFirstDependency); - define("autoFocuser", [componentsPath + "/autoFocuser"], returnFirstDependency); - define("connectionManager", [], function () { + define('slideshow', [componentsPath + '/slideshow/slideshow'], returnFirstDependency); + define('focusPreventScroll', ['legacy/focusPreventScroll'], returnFirstDependency); + define('userdataButtons', [componentsPath + '/userdatabuttons/userdatabuttons'], returnFirstDependency); + define('listView', [componentsPath + '/listview/listview'], returnFirstDependency); + define('indicators', [componentsPath + '/indicators/indicators'], returnFirstDependency); + define('viewSettings', [componentsPath + '/viewsettings/viewsettings'], returnFirstDependency); + define('filterMenu', [componentsPath + '/filtermenu/filtermenu'], returnFirstDependency); + define('sortMenu', [componentsPath + '/sortmenu/sortmenu'], returnFirstDependency); + define('sanitizefilename', [componentsPath + '/sanitizeFilename'], returnFirstDependency); + define('toast', [componentsPath + '/toast/toast'], returnFirstDependency); + define('scrollHelper', [scriptsPath + '/scrollHelper'], returnFirstDependency); + define('touchHelper', [scriptsPath + '/touchHelper'], returnFirstDependency); + define('imageUploader', [componentsPath + '/imageUploader/imageUploader'], returnFirstDependency); + define('htmlMediaHelper', [componentsPath + '/htmlMediaHelper'], returnFirstDependency); + define('viewContainer', [componentsPath + '/viewContainer'], returnFirstDependency); + define('dialogHelper', [componentsPath + '/dialogHelper/dialogHelper'], returnFirstDependency); + define('serverNotifications', [scriptsPath + '/serverNotifications'], returnFirstDependency); + define('skinManager', [componentsPath + '/skinManager'], returnFirstDependency); + define('keyboardnavigation', [scriptsPath + '/keyboardNavigation'], returnFirstDependency); + define('mouseManager', [scriptsPath + '/mouseManager'], returnFirstDependency); + define('scrollManager', [componentsPath + '/scrollManager'], returnFirstDependency); + define('autoFocuser', [componentsPath + '/autoFocuser'], returnFirstDependency); + define('connectionManager', [], function () { return ConnectionManager; }); - define("apiClientResolver", [], function () { + define('apiClientResolver', [], function () { return function () { return window.ApiClient; }; }); - define("appRouter", [componentsPath + "/appRouter", "itemHelper"], function (appRouter, itemHelper) { + define('appRouter', [componentsPath + '/appRouter', 'itemHelper'], function (appRouter, itemHelper) { function showItem(item, serverId, options) { - if ("string" == typeof item) { - require(["connectionManager"], function (connectionManager) { + if ('string' == typeof item) { + require(['connectionManager'], function (connectionManager) { var apiClient = connectionManager.currentApiClient(); apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (item) { appRouter.showItem(item, options); @@ -896,58 +886,58 @@ var AppInfo = {}; options = arguments[1]; } - appRouter.show("/" + appRouter.getRouteUrl(item, options), { + appRouter.show('/' + appRouter.getRouteUrl(item, options), { item: item }); } } appRouter.showLocalLogin = function (serverId, manualLogin) { - Dashboard.navigate("login.html?serverid=" + serverId); + Dashboard.navigate('login.html?serverid=' + serverId); }; appRouter.showVideoOsd = function () { - return Dashboard.navigate("videoosd.html"); + return Dashboard.navigate('videoosd.html'); }; appRouter.showSelectServer = function () { - Dashboard.navigate(AppInfo.isNativeApp ? "selectserver.html" : "login.html"); + Dashboard.navigate(AppInfo.isNativeApp ? 'selectserver.html' : 'login.html'); }; appRouter.showWelcome = function () { - Dashboard.navigate(AppInfo.isNativeApp ? "selectserver.html" : "login.html"); + Dashboard.navigate(AppInfo.isNativeApp ? 'selectserver.html' : 'login.html'); }; appRouter.showSettings = function () { - Dashboard.navigate("mypreferencesmenu.html"); + Dashboard.navigate('mypreferencesmenu.html'); }; appRouter.showGuide = function () { - Dashboard.navigate("livetv.html?tab=1"); + Dashboard.navigate('livetv.html?tab=1'); }; appRouter.goHome = function () { - Dashboard.navigate("home.html"); + Dashboard.navigate('home.html'); }; appRouter.showSearch = function () { - Dashboard.navigate("search.html"); + Dashboard.navigate('search.html'); }; appRouter.showLiveTV = function () { - Dashboard.navigate("livetv.html"); + Dashboard.navigate('livetv.html'); }; appRouter.showRecordedTV = function () { - Dashboard.navigate("livetv.html?tab=3"); + Dashboard.navigate('livetv.html?tab=3'); }; appRouter.showFavorites = function () { - Dashboard.navigate("home.html?tab=1"); + Dashboard.navigate('home.html?tab=1'); }; appRouter.showSettings = function () { - Dashboard.navigate("mypreferencesmenu.html"); + Dashboard.navigate('mypreferencesmenu.html'); }; appRouter.setTitle = function (title) { @@ -956,7 +946,7 @@ var AppInfo = {}; appRouter.getRouteUrl = function (item, options) { if (!item) { - throw new Error("item cannot be null"); + throw new Error('item cannot be null'); } if (item.url) { @@ -974,168 +964,168 @@ var AppInfo = {}; var itemType = item.Type || (options ? options.itemType : null); var serverId = item.ServerId || options.serverId; - if ("settings" === item) { - return "mypreferencesmenu.html"; + if ('settings' === item) { + return 'mypreferencesmenu.html'; } - if ("wizard" === item) { - return "wizardstart.html"; + if ('wizard' === item) { + return 'wizardstart.html'; } - if ("manageserver" === item) { - return "dashboard.html"; + if ('manageserver' === item) { + return 'dashboard.html'; } - if ("recordedtv" === item) { - return "livetv.html?tab=3&serverId=" + options.serverId; + if ('recordedtv' === item) { + return 'livetv.html?tab=3&serverId=' + options.serverId; } - if ("nextup" === item) { - return "list.html?type=nextup&serverId=" + options.serverId; + if ('nextup' === item) { + return 'list.html?type=nextup&serverId=' + options.serverId; } - if ("list" === item) { - var url = "list.html?serverId=" + options.serverId + "&type=" + options.itemTypes; + if ('list' === item) { + var url = 'list.html?serverId=' + options.serverId + '&type=' + options.itemTypes; if (options.isFavorite) { - url += "&IsFavorite=true"; + url += '&IsFavorite=true'; } return url; } - if ("livetv" === item) { - if ("programs" === options.section) { - return "livetv.html?tab=0&serverId=" + options.serverId; + if ('livetv' === item) { + if ('programs' === options.section) { + return 'livetv.html?tab=0&serverId=' + options.serverId; } - if ("guide" === options.section) { - return "livetv.html?tab=1&serverId=" + options.serverId; + if ('guide' === options.section) { + return 'livetv.html?tab=1&serverId=' + options.serverId; } - if ("movies" === options.section) { - return "list.html?type=Programs&IsMovie=true&serverId=" + options.serverId; + if ('movies' === options.section) { + return 'list.html?type=Programs&IsMovie=true&serverId=' + options.serverId; } - if ("shows" === options.section) { - return "list.html?type=Programs&IsSeries=true&IsMovie=false&IsNews=false&serverId=" + options.serverId; + if ('shows' === options.section) { + return 'list.html?type=Programs&IsSeries=true&IsMovie=false&IsNews=false&serverId=' + options.serverId; } - if ("sports" === options.section) { - return "list.html?type=Programs&IsSports=true&serverId=" + options.serverId; + if ('sports' === options.section) { + return 'list.html?type=Programs&IsSports=true&serverId=' + options.serverId; } - if ("kids" === options.section) { - return "list.html?type=Programs&IsKids=true&serverId=" + options.serverId; + if ('kids' === options.section) { + return 'list.html?type=Programs&IsKids=true&serverId=' + options.serverId; } - if ("news" === options.section) { - return "list.html?type=Programs&IsNews=true&serverId=" + options.serverId; + if ('news' === options.section) { + return 'list.html?type=Programs&IsNews=true&serverId=' + options.serverId; } - if ("onnow" === options.section) { - return "list.html?type=Programs&IsAiring=true&serverId=" + options.serverId; + if ('onnow' === options.section) { + return 'list.html?type=Programs&IsAiring=true&serverId=' + options.serverId; } - if ("dvrschedule" === options.section) { - return "livetv.html?tab=4&serverId=" + options.serverId; + if ('dvrschedule' === options.section) { + return 'livetv.html?tab=4&serverId=' + options.serverId; } - if ("seriesrecording" === options.section) { - return "livetv.html?tab=5&serverId=" + options.serverId; + if ('seriesrecording' === options.section) { + return 'livetv.html?tab=5&serverId=' + options.serverId; } - return "livetv.html?serverId=" + options.serverId; + return 'livetv.html?serverId=' + options.serverId; } - if ("SeriesTimer" == itemType) { - return "itemdetails.html?seriesTimerId=" + id + "&serverId=" + serverId; + if ('SeriesTimer' == itemType) { + return 'itemdetails.html?seriesTimerId=' + id + '&serverId=' + serverId; } - if ("livetv" == item.CollectionType) { - return "livetv.html"; + if ('livetv' == item.CollectionType) { + return 'livetv.html'; } - if ("Genre" === item.Type) { - url = "list.html?genreId=" + item.Id + "&serverId=" + serverId; + if ('Genre' === item.Type) { + url = 'list.html?genreId=' + item.Id + '&serverId=' + serverId; - if ("livetv" === context) { - url += "&type=Programs"; + if ('livetv' === context) { + url += '&type=Programs'; } if (options.parentId) { - url += "&parentId=" + options.parentId; + url += '&parentId=' + options.parentId; } return url; } - if ("MusicGenre" === item.Type) { - url = "list.html?musicGenreId=" + item.Id + "&serverId=" + serverId; + if ('MusicGenre' === item.Type) { + url = 'list.html?musicGenreId=' + item.Id + '&serverId=' + serverId; if (options.parentId) { - url += "&parentId=" + options.parentId; + url += '&parentId=' + options.parentId; } return url; } - if ("Studio" === item.Type) { - url = "list.html?studioId=" + item.Id + "&serverId=" + serverId; + if ('Studio' === item.Type) { + url = 'list.html?studioId=' + item.Id + '&serverId=' + serverId; if (options.parentId) { - url += "&parentId=" + options.parentId; + url += '&parentId=' + options.parentId; } return url; } - if ("folders" !== context && !itemHelper.isLocalItem(item)) { - if ("movies" == item.CollectionType) { - url = "movies.html?topParentId=" + item.Id; + if ('folders' !== context && !itemHelper.isLocalItem(item)) { + if ('movies' == item.CollectionType) { + url = 'movies.html?topParentId=' + item.Id; - if (options && "latest" === options.section) { - url += "&tab=1"; + if (options && 'latest' === options.section) { + url += '&tab=1'; } return url; } - if ("tvshows" == item.CollectionType) { - url = "tv.html?topParentId=" + item.Id; + if ('tvshows' == item.CollectionType) { + url = 'tv.html?topParentId=' + item.Id; - if (options && "latest" === options.section) { - url += "&tab=2"; + if (options && 'latest' === options.section) { + url += '&tab=2'; } return url; } - if ("music" == item.CollectionType) { - return "music.html?topParentId=" + item.Id; + if ('music' == item.CollectionType) { + return 'music.html?topParentId=' + item.Id; } } - var itemTypes = ["Playlist", "TvChannel", "Program", "BoxSet", "MusicAlbum", "MusicGenre", "Person", "Recording", "MusicArtist"]; + var itemTypes = ['Playlist', 'TvChannel', 'Program', 'BoxSet', 'MusicAlbum', 'MusicGenre', 'Person', 'Recording', 'MusicArtist']; if (itemTypes.indexOf(itemType) >= 0) { - return "itemdetails.html?id=" + id + "&serverId=" + serverId; + return 'itemdetails.html?id=' + id + '&serverId=' + serverId; } - var contextSuffix = context ? "&context=" + context : ""; + var contextSuffix = context ? '&context=' + context : ''; - if ("Series" == itemType || "Season" == itemType || "Episode" == itemType) { - return "itemdetails.html?id=" + id + contextSuffix + "&serverId=" + serverId; + if ('Series' == itemType || 'Season' == itemType || 'Episode' == itemType) { + return 'itemdetails.html?id=' + id + contextSuffix + '&serverId=' + serverId; } if (item.IsFolder) { if (id) { - return "list.html?parentId=" + id + "&serverId=" + serverId; + return 'list.html?parentId=' + id + '&serverId=' + serverId; } - return "#"; + return '#'; } - return "itemdetails.html?id=" + id + "&serverId=" + serverId; + return 'itemdetails.html?id=' + id + '&serverId=' + serverId; }; appRouter.showItem = showItem; @@ -1143,13 +1133,13 @@ var AppInfo = {}; }); })(); - return require(["browser"], onWebComponentsReady); + return onWebComponentsReady(); }(); -pageClassOn("viewshow", "standalonePage", function () { - document.querySelector(".skinHeader").classList.add("noHeaderRight"); +pageClassOn('viewshow', 'standalonePage', function () { + document.querySelector('.skinHeader').classList.add('noHeaderRight'); }); -pageClassOn("viewhide", "standalonePage", function () { - document.querySelector(".skinHeader").classList.remove("noHeaderRight"); +pageClassOn('viewhide', 'standalonePage', function () { + document.querySelector('.skinHeader').classList.remove('noHeaderRight'); }); diff --git a/src/scripts/taskbutton.js b/src/scripts/taskbutton.js index f9774167c0..8facaf8900 100644 --- a/src/scripts/taskbutton.js +++ b/src/scripts/taskbutton.js @@ -1,5 +1,5 @@ -define(["events", "userSettings", "serverNotifications", "connectionManager", "emby-button"], function (events, userSettings, serverNotifications, connectionManager) { - "use strict"; +define(['events', 'userSettings', 'serverNotifications', 'connectionManager', 'globalize', 'emby-button'], function (events, userSettings, serverNotifications, connectionManager, globalize) { + 'use strict'; return function (options) { function pollTasks() { @@ -26,12 +26,12 @@ define(["events", "userSettings", "serverNotifications", "connectionManager", "e } if (task.State == 'Idle') { - button.removeAttribute("disabled"); + button.removeAttribute('disabled'); } else { - button.setAttribute("disabled", "disabled"); + button.setAttribute('disabled', 'disabled'); } - button.setAttribute("data-taskid", task.Id); + button.setAttribute('data-taskid', task.Id); var progress = (task.CurrentProgressPercentage || 0).toFixed(1); if (options.progressElem) { @@ -47,12 +47,12 @@ define(["events", "userSettings", "serverNotifications", "connectionManager", "e if (options.lastResultElem) { var lastResult = task.LastExecutionResult ? task.LastExecutionResult.Status : ''; - if (lastResult == "Failed") { - options.lastResultElem.html('(' + Globalize.translate('LabelFailed') + ')'); - } else if (lastResult == "Cancelled") { - options.lastResultElem.html('(' + Globalize.translate('LabelCancelled') + ')'); - } else if (lastResult == "Aborted") { - options.lastResultElem.html('' + Globalize.translate('LabelAbortedByServerShutdown') + ''); + if (lastResult == 'Failed') { + options.lastResultElem.html('(' + globalize.translate('LabelFailed') + ')'); + } else if (lastResult == 'Cancelled') { + options.lastResultElem.html('(' + globalize.translate('LabelCancelled') + ')'); + } else if (lastResult == 'Aborted') { + options.lastResultElem.html('' + globalize.translate('LabelAbortedByServerShutdown') + ''); } else { options.lastResultElem.html(lastResult); } @@ -64,7 +64,7 @@ define(["events", "userSettings", "serverNotifications", "connectionManager", "e } function onButtonClick() { - onScheduledTaskMessageConfirmed(this.getAttribute("data-taskid")); + onScheduledTaskMessageConfirmed(this.getAttribute('data-taskid')); } function onScheduledTasksUpdate(e, apiClient, info) { @@ -89,12 +89,12 @@ define(["events", "userSettings", "serverNotifications", "connectionManager", "e if (pollInterval) { clearInterval(pollInterval); } - apiClient.sendMessage("ScheduledTasksInfoStart", "1000,1000"); + apiClient.sendMessage('ScheduledTasksInfoStart', '1000,1000'); pollInterval = setInterval(onPollIntervalFired, 5000); } function stopInterval() { - connectionManager.getApiClient(serverId).sendMessage("ScheduledTasksInfoStop"); + connectionManager.getApiClient(serverId).sendMessage('ScheduledTasksInfoStop'); if (pollInterval) { clearInterval(pollInterval); @@ -102,18 +102,18 @@ define(["events", "userSettings", "serverNotifications", "connectionManager", "e } if (options.panel) { - options.panel.classList.add("hide"); + options.panel.classList.add('hide'); } if (options.mode == 'off') { - button.removeEventListener("click", onButtonClick); - events.off(serverNotifications, "ScheduledTasksInfo", onScheduledTasksUpdate); + button.removeEventListener('click', onButtonClick); + events.off(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); stopInterval(); } else { - button.addEventListener("click", onButtonClick); + button.addEventListener('click', onButtonClick); pollTasks(); startInterval(); - events.on(serverNotifications, "ScheduledTasksInfo", onScheduledTasksUpdate); + events.on(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); } }; }); diff --git a/src/scripts/themeloader.js b/src/scripts/themeLoader.js similarity index 50% rename from src/scripts/themeloader.js rename to src/scripts/themeLoader.js index ea7ff57f9a..f75d6d0e29 100644 --- a/src/scripts/themeloader.js +++ b/src/scripts/themeLoader.js @@ -1,19 +1,19 @@ -define(["userSettings", "skinManager", "connectionManager", "events"], function (userSettings, skinManager, connectionManager, events) { - "use strict"; +define(['userSettings', 'skinManager', 'connectionManager', 'events'], function (userSettings, skinManager, connectionManager, events) { + 'use strict'; var currentViewType; - pageClassOn("viewbeforeshow", "page", function () { + pageClassOn('viewbeforeshow', 'page', function () { var classList = this.classList; - var viewType = classList.contains("type-interior") || classList.contains("wizardPage") ? "a" : "b"; + var viewType = classList.contains('type-interior') || classList.contains('wizardPage') ? 'a' : 'b'; if (viewType !== currentViewType) { currentViewType = viewType; var theme; var context; - if ("a" === viewType) { + if ('a' === viewType) { theme = userSettings.dashboardTheme(); - context = "serverdashboard"; + context = 'serverdashboard'; } else { theme = userSettings.theme(); } @@ -21,7 +21,7 @@ define(["userSettings", "skinManager", "connectionManager", "events"], function skinManager.setTheme(theme, context); } }); - events.on(connectionManager, "localusersignedin", function (e, user) { + events.on(connectionManager, 'localusersignedin', function (e, user) { currentViewType = null; }); }); diff --git a/src/components/touchhelper.js b/src/scripts/touchHelper.js similarity index 100% rename from src/components/touchhelper.js rename to src/scripts/touchHelper.js diff --git a/src/serviceworker.js b/src/serviceworker.js index c43d1f4b4e..2210183148 100644 --- a/src/serviceworker.js +++ b/src/serviceworker.js @@ -1,2 +1,2 @@ /* eslint-env serviceworker */ -importScripts("components/serviceworker/notifications.js"); +importScripts('components/serviceworker/notifications.js'); diff --git a/src/standalone.js b/src/standalone.js index 04d40d6b11..237872703a 100644 --- a/src/standalone.js +++ b/src/standalone.js @@ -1,4 +1,4 @@ (function() { - "use strict"; + 'use strict'; window.appMode = 'standalone'; })(); diff --git a/src/strings/af.json b/src/strings/af.json index 6ac71790f6..5c4e13dc7c 100644 --- a/src/strings/af.json +++ b/src/strings/af.json @@ -40,5 +40,129 @@ "Add": "Voeg by", "Actor": "Akteur", "AccessRestrictedTryAgainLater": "Toegang is beperk. Probeer weer later .", - "Absolute": "Absoluut" + "Absolute": "Absoluut", + "AlbumArtist": "Album Kunstenaar", + "TabLatest": "Nuutste", + "TabInfo": "Inligting", + "TabGuide": "Gids", + "TabGenres": "Genres", + "TabFavorites": "Gunstellinge", + "TabEpisodes": "Episodes", + "TabDisplay": "Vertoon", + "TabDirectPlay": "Speel Direk", + "TabDevices": "Toestelle", + "TabDashboard": "Paneelbord", + "TabContainers": "Houers", + "Collections": "Versamelings", + "TabCollections": "Versamelings", + "UnsupportedPlayback": "Jellyfin kan nie inhoud wat beskerm word deur DRM ontsuifer nie, maar daar sal 'n poging aangwend word inelkgeval, insluitend beskermde titels. Sommige leêrs mag geheel en al swart verksyn weens enkripsie of ander on-ondersteunde funksies, byvoorbeeld interaktiewe titels.", + "OnApplicationStartup": "Op applikasie begin", + "EveryXHours": "Elke {0} ure", + "EveryHour": "Elke uur", + "EveryXMinutes": "Elke {0} minute", + "OnWakeFromSleep": "Op wakker word van slaap", + "WeeklyAt": "{0}s teen {1}", + "DailyAt": "Daagliks teen {0}", + "LastSeen": "Laas gekyk {0}", + "PersonRole": "soos {0}", + "ListPaging": "{0}-{1} van {2}", + "WriteAccessRequired": "Jellyfin Bediener benodig skryf toegang tot die leêr. Maak asseblief seker dat dit skryf toegang het en probeer weer.", + "PathNotFound": "Die pad kon nie gevind word nie. Maak asseblief seker dat die pad geldig is en probeer weer.", + "Yesterday": "Gister", + "Yes": "Ja", + "XmlTvSportsCategoriesHelp": "Programme met die kategorieë sal vertoon word as sport programme. Verdeel met veelvuldige '|'.", + "XmlTvPathHelp": "'n Pad tot 'n XMLTV lêer. Jellyfin sal die lêer lees en van tyd tot tyd soek vir opdaterings. Jy is verantwoordelik vir die maak en opdatering van die lêer.", + "XmlTvNewsCategoriesHelp": "Programme met die kategorieë sal vertoon word as nuus programme. Verdeel met veelvuldige '|'.", + "XmlTvMovieCategoriesHelp": "Programme met die kategorieë sal vertoon word as replprente. Verdeel met veelvuldige '|'.", + "XmlTvKidsCategoriesHelp": "Programme met die kategorieë sal vertoon word as programme vir kinders. Verdeel met veelvuldige '|'.", + "XmlDocumentAttributeListHelp": "Hierdie kenmerke word toegepas tot die wortel element van elke XML reaksie.", + "Writer": "Skrywer", + "WizardCompleted": "Dit is al wat ons benodig vir nou. Jellyfin het begin om inligting van jou media biblioteek te versamel. Kyk na van ons apps, en dan klik Finaliseer om die Paneelbord.", + "Whitelist": "Witlys", + "WelcomeToProject": "Welkom tot Jellyfin!", + "Wednesday": "Woensdag", + "Watched": "Gekyk", + "ViewPlaybackInfo": "Beskou terugspeel inligting", + "ViewArtist": "Beskou kunstenaar", + "ViewAlbum": "Beskou album", + "VideoRange": "Video reekse", + "Vertical": "Vertikaal", + "ValueVideoCodec": "Video Kodek: {0}", + "ValueTimeLimitSingleHour": "Tyd limiet: 1 uur", + "ValueTimeLimitMultiHour": "Tyd limiet: {0} ure", + "ValueSpecialEpisodeName": "Spesiale - {0}", + "ValueSongCount": "{0} liedjies", + "ValueSeriesCount": "{0} reekse", + "ValueSeconds": "{0} sekondes", + "ValueOneSong": "1 liedjie", + "ValueOneSeries": "1 reeks", + "ValueOneMusicVideo": "1 musiek video", + "ValueOneMovie": "1 rolprent", + "ValueMusicVideoCount": "{0} musiek videos", + "ValueMovieCount": "{0} rolprente", + "ValueMinutes": "{0} minute", + "ValueDiscNumber": "Skyf {0}", + "ValueContainer": "Houers: {0}", + "ValueConditions": "Kondisies: {0}", + "ValueCodec": "Kodek: {0}", + "ValueAudioCodec": "Audio Kodec: {0}", + "ValueAlbumCount": "{0} albums", + "UserProfilesIntro": "Jellyfin verskaf ondersteuning vir gebruiker profiele met pas instellings vir vertoon, speel staat, en ouer-beheer.", + "UserAgentHelp": "Verskaf 'n pas gebruiker-agent HTTP opskrif.", + "Upload": "Oplaai", + "Up": "Op", + "Unplayed": "Ongespeel", + "Unmute": "Ontstom", + "UninstallPluginHeader": "Oninstalleer Plugin", + "UninstallPluginConfirmation": "Is jy seker jy wil voortgaan met die oninstallasie {0}?", + "Uniform": "Uniform", + "TvLibraryHelp": "Hersien die {0}TV benamings gids{1}.", + "Tuesday": "Dinsdag", + "Transcoding": "Trankodering", + "Trailers": "Voorprente", + "TrackCount": "{0} nommers", + "Track": "Nommer", + "TitlePlayback": "Terugspeel", + "TitleHostingSettings": "Hosting Instellings", + "TitleHardwareAcceleration": "Hardeware Versnelling", + "Thursday": "Donderdag", + "Thumb": "Duim", + "ThisWizardWillGuideYou": "Hierdie gids sal jou deur die opstel proses help. Om te begin, kies asseblief jou taal van voorkeur.", + "TheseSettingsAffectSubtitlesOnThisDevice": "Hierdie instellings affekteer die sub-titels vie hierdie toestel", + "ThemeVideos": "Tema Videos", + "ThemeSongs": "Tema Liedjies", + "TellUsAboutYourself": "Vertel ons van jouself", + "TabUsers": "Gebruikers", + "TabUpcoming": "Komende", + "TabTranscoding": "Transkodering", + "TabTrailers": "Voorprente", + "TabSuggestions": "Voorstelle", + "TabStreaming": "Stroom", + "TabSongs": "Liedjies", + "TabShows": "Programme", + "TabSettings": "Instellings", + "TabServer": "Bediener", + "TabSeries": "Reekse", + "TabScheduledTasks": "Geskeduleerde Take", + "TabResumeSettings": "Hervat", + "TabResponses": "Reaksies", + "TabRecordings": "Opnames", + "TabProfiles": "Profiele", + "TabProfile": "Profiel", + "TabPlaylists": "Speel lyste", + "TabPlaylist": "Speel lys", + "TabPlayback": "Terugspeel", + "TabPassword": "Wagwoord", + "TabParentalControl": "Ouer Beheer", + "TabOther": "Ander", + "TabNotifications": "Kennisgewings", + "TabNfoSettings": "NFO Instellings", + "TabNetworking": "Netwerking", + "TabNetworks": "Netwerke", + "TabMusicVideos": "Musiek Videos", + "TabMusic": "Musiek", + "TabMovies": "Rolprente", + "TabMetadata": "Meta Inligting", + "TabLogs": "Logs", + "TabLiveTV": "Lewendige TV" } diff --git a/src/strings/ar.json b/src/strings/ar.json index 50b93a830a..f93146dc01 100644 --- a/src/strings/ar.json +++ b/src/strings/ar.json @@ -17,7 +17,7 @@ "ButtonAddScheduledTaskTrigger": "إضافة زناد", "ButtonAddServer": "إضافة خادم", "ButtonAddUser": "اضافة مستخدم", - "ButtonArrowDown": "أدنى", + "ButtonArrowDown": "أسفل", "ButtonArrowLeft": "يسار", "ButtonArrowRight": "يمين", "ButtonArrowUp": "أعلى", @@ -234,7 +234,7 @@ "HeaderPlayback": "تشغيل الوسائط", "HeaderPleaseSignIn": "الرجاء تسجيل الدخول", "HeaderPluginInstallation": "تثبيت الملحفات", - "HeaderPreferredMetadataLanguage": "اللغة المفضلة لواصفات البيانات:", + "HeaderPreferredMetadataLanguage": "اللغة المفضلة لواصفات البيانات", "HeaderProfile": "الحساب", "HeaderProfileInformation": "معلومات العريضة", "HeaderProfileServerSettingsHelp": "هذه القيم ستتحكم في كيفية تقديم شكل خادم أمبي في الجهاز", @@ -962,7 +962,7 @@ "AllowMediaConversion": "السماح بتحويل الوسائظ", "AllLanguages": "كل اللغات", "AllEpisodes": "كل الحلقات", - "AllComplexFormats": "جميع التنسيقات المعقدة (ASS ، SSA ، VOBSUB ، PGS ، SUB / IDX ، إلخ.)", + "AllComplexFormats": "جميع التنسيقات المعقدة (ASS ، SSA ، VOBSUB ، PGS ، SUB / IDX ، ...)", "AllChannels": "كل القنوات", "Albums": "ألبومات", "Aired": "عرضت", @@ -1045,5 +1045,112 @@ "Artist": "الفنان", "AllowFfmpegThrottling": "إبطاء الترميزات", "AlbumArtist": "المؤدي", - "Album": "الألبوم" + "Album": "الألبوم", + "Disconnect": "قطع الاتصال", + "Disc": "القرص", + "Disabled": "تعطيل", + "Directors": "المخرجون", + "Director": "المخرج", + "DirectPlaying": "بث بدون تحويل الصيغة", + "DirectStreaming": "البث المباشر", + "DirectStreamHelp2": "البث المباشر للملف يستخدم طاقة معالجة قليلة جدًا دون أي خسارة في جودة الفيديو.", + "DirectStreamHelp1": "الوسائط متوافقة مع الجهاز فيما يتعلق بالدقة ونوع الوسائط (H.264 ، AC3 ، إلخ) ، ولكنها في حاوية ملفات غير متوافقة (mkv ، avi ، wmv ، إلخ). سيتم إعادة حزم الفيديو في الوقت الحقيقي قبل بثه إلى الجهاز.", + "DetectingDevices": "يتم الكشف عن الأجهزة", + "Desktop": "سطح المكتب", + "Descending": "تنازلي", + "Depressed": "منخفض", + "DeinterlaceMethodHelp": "حدد طريقة فك التشابك لاستخدامها عند تحويل محتوى متشابك.", + "DefaultSubtitlesHelp": "يتم تحميل الترجمات استنادًا إلى العلامات الافتراضية والقسرية في البيانات الوصفية المضمنة. سيتم اعتبار تفضيلات اللغة عند توفر خيارات متعددة.", + "DefaultMetadataLangaugeDescription": "هذه هي إعداداتك الافتراضية ويمكن تخصيصها على أساس كل مكتبة.", + "Default": "افتراضي", + "CopyStreamURLError": "توجد مشكله في نسخ الرابط", + "CopyStreamURL": "نسخ عنوان الرابط", + "Continuing": "مستمر", + "CopyStreamURLSuccess": "URL copied successfully.", + "Connect": "اتصال", + "ConfirmEndPlayerSession": "هل تريد اطفاء النظام؟", + "ColorPrimaries": "الألوان", + "ClientSettings": "إعدادات التطبيق", + "ButtonTogglePlaylist": "قائمة التشغيل", + "BoxSet": "طقم", + "ButtonToggleContextMenu": "المزيد", + "ButtonSplit": "تقسيم", + "AllowFfmpegThrottlingHelp": "عندما يتقدم رمز تحويل أو إعادة تحويل بعيدًا بما فيه الكفاية عن موضع التشغيل الحالي ، أوقف العملية مؤقتًا حتى تستهلك موارد أقل. هذا مفيد للغاية عند المشاهدة دون البحث كثيرًا. أوقف هذا إذا واجهت مشاكل في التشغيل.", + "InstallingPackage": "تثبيت {0} (الإصدار {1})", + "Images": "الصور", + "Identify": "التعرف على الوسائط", + "HttpsRequiresCert": "لتمكين الاتصالات الآمنة ، ستحتاج إلى توفير شهادة SSL موثوقة ، مثل Letsencrypt. يرجى إما تقديم شهادة أو تعطيل الاتصالات الآمنة.", + "HeaderServerAddressSettings": "إعدادات عنوان السيرفر", + "HeaderRemoteAccessSettings": "إعدادات الوصول عن بعد", + "HeaderKeepSeries": "حافظ على السلسلة", + "HeaderKeepRecording": "استمر في التسجيل", + "HeaderIdentifyItemHelp": "أدخل معيار بحث واحد أو أكثر. إزالة المعايير لزيادة نتائج البحث.", + "HeaderHttpsSettings": "إعدادات HTTPS", + "HeaderHome": "الصفحة الرئيسية", + "HeaderFetcherSettings": "إعدادات الجلب", + "HeaderFavoritePlaylists": "قوائم التشغيل المفضلة", + "HeaderFavoriteVideos": "مقاطع الفيديو المفضلة", + "HeaderFavoritePeople": "أناس مفضلين", + "HeaderFavoriteMovies": "الأفلام المفضلة", + "HeaderFavoriteBooks": "الكتب المفضلة", + "HeaderExternalIds": "المعرفات الخارجية:", + "HeaderEnabledFieldsHelp": "قم بإلغاء تحديد حقل لقفله ومنع تغيير بياناته.", + "HeaderEnabledFields": "الحقول الممكّنة", + "HeaderEditImages": "تحرير الصور", + "HeaderDVR": "DVR", + "HeaderDownloadSync": "تنزيل ومزامنة", + "HeaderDetectMyDevices": "كشف أجهزتي", + "HeaderDeleteItems": "حذف العناصر", + "HeaderContinueListening": "استمر في الاستماع", + "HeaderConfigureRemoteAccess": "إعدادات الوصول عن بعد", + "HeaderChapterImages": "صور الفصل", + "HeaderCancelSeries": "إلغاء السلسلة", + "HeaderCancelRecording": "إلغاء التسجيل", + "HeaderBlockItemsWithNoRating": "حظر العناصر التي لا تحتوي على معلومات تصنيف أو لم يتم التعرف عليها:", + "HeaderAudioBooks": "الكتب الصوتية", + "HeaderAppearsOn": "يظهر على", + "ApiKeysCaption": "قائمة مفاتيح API الممكّنة حاليًا", + "HeaderAddToPlaylist": "أضف إلى قائمة التشغيل", + "HeaderAddToCollection": "أضف إلى المجموعة", + "HDPrograms": "برامج HD", + "Guide": "الدليل", + "GuestStar": "النجم الضيف", + "GroupVersions": "إصدارات المجموعة", + "GroupBySeries": "تجميع حسب السلسلة", + "Genre": "نوع أدبي", + "General": "الاعدادات العامة", + "FormatValue": "صيغة الملف: {0}", + "Filters": "مرشحات", + "File": "ملف", + "FetchingData": "يتم تنزيل بيانات إضافية", + "Features": "الميزات", + "Favorite": "المفضلة", + "Extras": "مواسم إضافية", + "ErrorDeletingItem": "حدث خطأ في حذف العنصر من سيرفر Jellyfin. يرجى التحقق من أن سيرفر Jellyfin لديه حق الوصول للكتابة إلى مجلد الوسائط وحاول مرة أخرى.", + "Episode": "حلقة", + "EnableThemeVideosHelp": "قم بتشغيل الفيديوهات الرئيسية في الخلفية أثناء تصفح المكتبة.", + "EnableThemeVideos": "الفيديوهات الرئيسية", + "EnableThemeSongsHelp": "قم بتشغيل اللحن الرئيسي في الخلفية أثناء تصفح المكتبة.", + "EnableThemeSongs": "اللحن الرئيسي", + "EnableStreamLoopingHelp": "قم بتمكين هذا إذا كانت عمليات البث المباشر تحتوي فقط على بضع ثوان من البيانات وتحتاج إلى إعادة طلب مستمر. قد يؤدي تمكين هذا عندما لا تكون هناك حاجة إلى مشاكل.", + "EnableStreamLooping": "تكرار البث المباشر", + "EnableHardwareEncoding": "تمكين تشفير الأجهزة", + "EnableExternalVideoPlayersHelp": "سيتم عرض قائمة مشغل خارجي عند بدء تشغيل الفيديو.", + "EnableExternalVideoPlayers": "مشغلات الفيديو الخارجية", + "EnableDisplayMirroring": "اعرض شاشتك على شاشة أخرى", + "EnableColorCodedBackgrounds": "تصنيف الخلفيات حسب اللون", + "EnableCinemaMode": "وضع السينما", + "EnableBackdropsHelp": "اعرض الخلفيات في خلفية بعض الصفحات أثناء تصفح المكتبة.", + "EnableBackdrops": "الخلفيات", + "DownloadsValue": "عدد التنزيلات {0}", + "Download": "تحميل", + "Down": "أسفل", + "DoNotRecord": "لا تسجل", + "DisplayModeHelp": "حدد نمط العرض الذي تريده للواجهة.", + "DisplayMissingEpisodesWithinSeasonsHelp": "يجب تمكين هذا أيضًا لمكتبات التلفزيون في إعدادات السيرفر.", + "DisplayMissingEpisodesWithinSeasons": "عرض الحلقات المفقودة خلال المواسم", + "DisplayInOtherHomeScreenSections": "عرض في أقسام الشاشة الرئيسية مثل أحدث الوسائط واستمر في المشاهدة", + "DisplayInMyMedia": "عرض على الشاشة الرئيسية", + "Display": "عرض", + "Dislike": "لم يعجبنى" } diff --git a/src/strings/bg-bg.json b/src/strings/bg-bg.json index e21176f6ec..8357a9c8dd 100644 --- a/src/strings/bg-bg.json +++ b/src/strings/bg-bg.json @@ -86,17 +86,17 @@ "ChannelAccessHelp": "Изберете каналите, които да споделите с потребителя. Администраторите ще могат да редактират всички канали, използвайки управлението на метаданни.", "Collections": "Колекции", "ColorSpace": "Цветово пространство", - "CommunityRating": "Обществена ощенка", + "CommunityRating": "Рейтинг на общността", "Composer": "Съчинител", "ConfirmDeleteImage": "Изтриване на изображението?", - "ContinueWatching": "Продължаване на гледането", + "ContinueWatching": "Продължи гледането", "Continuing": "Продължаващо", "CriticRating": "Оценка на критиците", "DateAdded": "Дата на добавяне", "DatePlayed": "Дата на пускане", "DeathDateValue": "Починал/а на: {0}", "Default": "По подразбиране", - "Delete": "Изтриване", + "Delete": "Премахване", "DeleteMedia": "Изтриване на медията", "Desktop": "Работен плот", "DeviceAccessHelp": "Това се отнася само за устройства, които могат да бъдат различени и няма да попречи на достъп от мрежов четец. Филтрирането на потребителски устройства ще предотврати използването им докато не бъдат одобрени тук.", @@ -305,7 +305,7 @@ "LabelCustomCertificatePath": "Път към потребителския сертификат:", "LabelCustomCertificatePathHelp": "Път до файл с шифровъчен стандарт №12 (PKCS #12), съдържащ сертификат и частен ключ за поддръжка на протокол TLS на собствен домейн.", "LabelCustomCss": "CSS по избор:", - "LabelCustomCssHelp": "Добавете собствен стил за Уеб-интерфейса.", + "LabelCustomCssHelp": "Добавете собствен стил към уеб-интерфейса.", "LabelCustomDeviceDisplayName": "Показвано име:", "LabelCustomRating": "Оценка по избор:", "LabelDashboardTheme": "Облик на сървърното табло:", @@ -836,7 +836,7 @@ "AddItemToCollectionHelp": "Добавяне към колекция чрез търсенето им и използване на дясно-щракване с мишката или контекстното меню.", "Absolute": "Aбсолютен", "LabelLanNetworks": "Локални мрежи:", - "LabelKodiMetadataSaveImagePathsHelp": "Това е препоръчително ако имате изображения, пътят към които не е съобразен с изискванията на Kodi", + "LabelKodiMetadataSaveImagePathsHelp": "Това е препоръчително, ако наименованието на изображенията не са съобразени с изискванията на Kodi.", "LabelKodiMetadataSaveImagePaths": "Записване на пътеките към изображенията в nfo файловете", "LabelChannels": "Канали:", "DropShadow": "Сянка", @@ -872,5 +872,36 @@ "CancelSeries": "Откажи сериите", "CancelRecording": "Откажи записа", "ButtonSplit": "Раздели", - "ButtonResetEasyPassword": "Нулиране на бързия ПИН код" + "ButtonResetEasyPassword": "Нулиране на бързия ПИН код", + "ButtonRevoke": "Отмени", + "ButtonEditOtherUserPreferences": "Редакция на потребителския профил, изображение и лични предпочитания.", + "BoxRear": "Комплект (стар)", + "BoxSet": "Комплект", + "AuthProviderHelp": "Избор на доставчик на услуга за Автентификация, която ще се използва за автентификация на потребителската парола.", + "AllowedRemoteAddressesHelp": "Списък с IP адреси или IP/маска записи, разделени със запетая, които ще имат отдалечен достъп. Ако полето не е попълнено всички адреси ще имат отдалечен достъп.", + "BurnSubtitlesHelp": "Определя дали сървърът трябва да записва субтитри във видеоклиповете припрекодиране. Избягването на това значително ще подобри производителността. Изберете Auto, за да запишете формати, базирани на изображения (VOBSUB, PGS, SUB, IDX) и някои ASS или SSA субтитри.", + "AllowFfmpegThrottlingHelp": "Когато прекодирането или запазването на видео стигне достатъчно далеч напред от текущата позиция за възпроизвеждане, поставете на пауза процеса, така ще се изразходват по-малко ресурси. Това е най-полезно, когато се гледа, без да се търси често из видеото. Изключете това, ако имате проблеми с възпроизвеждането.", + "AllowOnTheFlySubtitleExtractionHelp": "Вградените субтитри могат да бъдат извлечени от видеоклиповете и прехвърлени към клиентите като обикновен текст, за да се предотврати транскодирането на видеото. В някои системи това може да отнеме много време и да спре възпроизвеждането на видео по време на процеса на извличане. Деактивирайте това, за да има вградени субтитри, записани с видео кодиране, когато те не се поддържат от клиентското устройство.", + "CinemaModeConfigurationHelp": "Режимът на кино носи театрално изживяване направо във вашата всекидневна с възможност за пускане на трейлъри и персонализирани интродукции преди основния филм.", + "ChangingMetadataImageSettingsNewContent": "Промените в настройките за изтегляне на метаданни или изображения ще се прилагат само за ново съдържание, добавено към вашата библиотека. За да приложите промените към съществуващите заглавия, ще трябва да обновите метаданните им ръчно.", + "DefaultMetadataLangaugeDescription": "Това са настройки по подразбиране и могат да се променят на база библиотека.", + "DefaultErrorMessage": "Възникна грешка при изпълнение на заявката. Моля опитайте по-късно.", + "CustomDlnaProfilesHelp": "Създаване на персонализиран профил за целево устройство или заменяне на системния профил.", + "CopyStreamURL": "Копиране URL на стрийма", + "CopyStreamURLError": "Възникна грешка при копиране на URL.", + "CopyStreamURLSuccess": "URL се копира успешно.", + "Connect": "Свързване", + "ConfirmEndPlayerSession": "Искате ли да изключите Jellyfin на {0}?", + "ConfirmDeletion": "Потвърждаване на изтриването", + "ConfirmDeleteItem": "Изтриването на елемента ще го премахне едновременно от файловата система и библиотеката. Сигурни ли сте, че искате да продължите?", + "ConfigureDateAdded": "Конфигурацията на добавянето на датата се определя в панела на Jellyfin сървъра в секцията за настройка на библиотека", + "ConfirmDeleteItems": "Изтриването на елементите ще ги премахне едновременно от файловата система и библиотеката. Сигурни ли сте, че искате да продължите?", + "ColorTransfer": "Предаване на цвета", + "ColorPrimaries": "Основни цветове", + "DeleteUserConfirmation": "Сигурнили сте че искате да премахнете този потребител?", + "DeleteUser": "Премахване на потребител", + "DeleteImageConfirmation": "Сигурнили сте че искате да премахнете това Изображение?", + "DeleteImage": "Премахване на Исображение", + "ButtonTogglePlaylist": "Списък с изпълнения", + "ButtonToggleContextMenu": "Повече" } diff --git a/src/strings/ca.json b/src/strings/ca.json index 80e4b90067..df1cdf28f6 100644 --- a/src/strings/ca.json +++ b/src/strings/ca.json @@ -7,7 +7,7 @@ "All": "Tot", "AllChannels": "Tots els canals", "AllEpisodes": "Tots els episodis", - "AlwaysPlaySubtitles": "Reprodueix sempre amb subtítols", + "AlwaysPlaySubtitles": "Reprodueix sempre", "AroundTime": "Cap a les {0}", "Artists": "Artistes", "AsManyAsPossible": "Tants com sigui possible", @@ -732,7 +732,7 @@ "XmlTvNewsCategoriesHelp": "Els programes amb aquestes categories es mostraran com a programes de notícies. Separa'n varis emprant '|'.", "XmlTvSportsCategoriesHelp": "Els programes amb aquestes categories es mostraran com a programes esportius. Separa'n varis emprant '|'.", "Books": "Llibres", - "Folders": "Directoris", + "Folders": "Carpetes", "Photos": "Fotos", "Shows": "Programes", "Songs": "Cançons", @@ -741,7 +741,7 @@ "Channels": "Canals", "Collections": "Col·leccions", "Favorites": "Preferits", - "HeaderAlbumArtists": "Artistes dels Àlbums", + "HeaderAlbumArtists": "Artistes del Àlbum", "HeaderFavoriteAlbums": "Àlbums Preferits", "HeaderFavoriteArtists": "Artistes Preferits", "HeaderFavoriteEpisodes": "Episodis Preferits", @@ -795,5 +795,28 @@ "AddedOnValue": "Afegit {0}", "AddItemToCollectionHelp": "Afegiu els elements a les col·leccions buscant-los i fent clic amb el botó dret o toqueu els menús per afegir-los a una col·lecció.", "Actor": "Actor", - "Absolute": "Absolut" + "Absolute": "Absolut", + "ClientSettings": "Configuració del client", + "CinemaModeConfigurationHelp": "El mode Cinema aporta l'experiència del teatre directament a la sala d'estar amb la possibilitat de jugar a tràilers i presentacions personalitzades abans de la funció principal.", + "ChannelNameOnly": "Número de canal", + "ChangingMetadataImageSettingsNewContent": "Els canvis als paràmetres de descàrrega de metadades o d'obra d'art només s'apliquen al contingut nou afegit a la biblioteca. Per aplicar els canvis als títols existents, haureu de refrescar les metadades manualment.", + "ButtonTogglePlaylist": "Llista de reproducció", + "ButtonToggleContextMenu": "més", + "ButtonOff": "Apagar", + "BurnSubtitlesHelp": "Determina si el servidor hauria de gravar-se en els subtítols en transcodificar vídeos. Evitar això millorarà molt el rendiment. Seleccioneu Automàtica per gravar formats basats en imatges (VOBSUB, PGS, SUB, IDX) i certs subtítols ASS o SSA.", + "Browse": "Navega", + "BoxRear": "Caixa (posterior)", + "BoxSet": "conjunt de caixes", + "Box": "Caixa", + "BookLibraryHelp": "Els àudio i llibres de text són compatibles. Reviseu la {0} guia de denominació de llibres {1}.", + "Backdrops": "Fons", + "Backdrop": "Fons", + "AutoBasedOnLanguageSetting": "Auto (basada en la configuració de l’idioma)", + "Artist": "Artista", + "AllowedRemoteAddressesHelp": "Llista d’adreces IP o d’entrades IP / netmasca separades per comes per a xarxes que podran connectar-se de forma remota. Si es deixa en blanc, es permetran totes les adreces remotes.", + "AllowFfmpegThrottlingHelp": "Quan un transcòdi o un remux estigui prou lluny de la posició de reproducció actual, feu una pausa en el procés perquè consumirà menys recursos. Això és més útil per mirar sense buscar sovint. Desactiveu-la si teniu problemes de reproducció.", + "AllowFfmpegThrottling": "Transcodes de l’acceleració", + "AllowOnTheFlySubtitleExtractionHelp": "Els subtítols incrustats es poden extreure de vídeos i entregar-los a clients en text senzill per tal d'evitar la transcodificació de vídeo. En alguns sistemes, això pot trigar molt i fer que la reproducció de vídeo s’aturi durant el procés d’extracció. Desactiveu-ho per tenir subtítols incrustats incrustats amb la transcodificació de vídeo quan no són compatibles amb el dispositiu client de forma nativa.", + "AlbumArtist": "Album artista", + "Album": "Album" } diff --git a/src/strings/cs.json b/src/strings/cs.json index 0a65fc1000..626c63bec6 100644 --- a/src/strings/cs.json +++ b/src/strings/cs.json @@ -17,20 +17,20 @@ "AlwaysPlaySubtitles": "Vždy zobrazovat", "AlwaysPlaySubtitlesHelp": "Titulky odpovídající jazykové předvolbě se načtou bez ohledu na jazyk audia.", "Anytime": "Kdykoliv", - "AroundTime": "Okolo {0}", + "AroundTime": "Okolo", "Art": "Umění", "Artists": "Umělci", "AsManyAsPossible": "Tolikrát jak je možné", "AspectRatio": "Poměr stran", "AttributeNew": "Nové", "Audio": "Zvuk", - "Auto": "Automatizovat", + "Auto": "Automaticky", "Backdrop": "Pozadí", "Backdrops": "Pozadí", "BirthDateValue": "Narozen: {0}", "BirthLocation": "Místo narození", "BirthPlaceValue": "Místo narození: {0}", - "BookLibraryHelp": "Audio a textové knihy jsou podporovány. Přečtěte si {0}pravidla pro názvy knih {1}.", + "BookLibraryHelp": "Audio a textové knihy jsou podporovány. Přečtěte si {0} pravidla pojmenování knih {1}.", "Books": "Knihy", "Box": "Pouzdro", "BoxRear": "Zadní část pouzdra", @@ -60,7 +60,7 @@ "ButtonForgotPassword": "Zapomenuté heslo", "ButtonFullscreen": "Celá obrazovka", "ButtonGotIt": "Mám to", - "ButtonGuide": "Průvodce", + "ButtonGuide": "Programový průvodce", "ButtonHelp": "Nápověda", "ButtonHome": "Domů", "ButtonLearnMore": "Zjistit více", @@ -79,7 +79,7 @@ "ButtonProfile": "Profil", "ButtonQuickStartGuide": "Rychlý průvodce", "ButtonRefresh": "Obnovit", - "ButtonRefreshGuideData": "Obnovit data průvodce", + "ButtonRefreshGuideData": "Obnovit data programového průvodce", "ButtonRemove": "Odstranit", "ButtonRename": "Přejmenovat", "ButtonRepeat": "Opakovat", @@ -102,7 +102,7 @@ "ButtonStop": "Zastavit", "ButtonSubmit": "Potvrdit", "ButtonSubtitles": "Titulky", - "ButtonTrailer": "Ukázka/trailer", + "ButtonTrailer": "Upoutávka", "ButtonUninstall": "Odinstalovat", "ButtonUp": "Zesílit", "ButtonViewWebsite": "Přejít na webové stránky", @@ -118,11 +118,11 @@ "ChannelAccessHelp": "Vyberte kanály, které chcete sdílet s tímto uživatelem. Administrátoři budou moci upravovat všechny kanály pomocí správce metadat.", "ChannelNameOnly": "Kanál {0} jen", "ChannelNumber": "Číslo kanálu", - "CinemaModeConfigurationHelp": "Režim Cinema přináší zážitky jako z kina přímo do vašeho obývacího pokoje s možností přehrát trailery a vlastní intra před hlavním programem.", + "CinemaModeConfigurationHelp": "Tento režim přibližuje domácí sledování filmů zážitku v kině díky možnosti přehrát upoutávky k filmům a vlastní úvodní video před hlavním pořadem.", "Collections": "Kolekce", "CommunityRating": "Hodnocení komunity", "Composer": "Skladatel", - "ConfigureDateAdded": "Konfigurace přidání data je definována v nastavení knihovny v ovládacím panelu", + "ConfigureDateAdded": "Konfigurace přidání data je definována v nastavení knihovny na nástěnce serveru Jellyfin", "ConfirmDeleteImage": "Odstranit obrázek?", "ConfirmDeleteItem": "Smazáním položky odstraníte soubor jak z knihovny médií tak ze souborového systému. Jste si jisti, že chcete pokračovat?", "ConfirmDeleteItems": "Odstraněním těchto položek odstraníte vaše média jak z knihovny médií, tak i ze souborového systému. Jste si jisti, že chcete pokračovat?", @@ -167,8 +167,8 @@ "DownloadingDots": "Stahování...", "Downloads": "Stahování", "DrmChannelsNotImported": "Kanál s DRM nebude importován.", - "DropShadow": "Vrhat stín", - "EasyPasswordHelp": "Váš PIN kód je snadné používat pro přístup v režimu offline s podporovanými Jellyfin aplikacemi, může být také použit pro snadné přihlášení v lokální síti.", + "DropShadow": "Stín", + "EasyPasswordHelp": "Váš jednoduchý kód PIN slouží k přístupu v režimu offline v podporovaných klientech Jellyfin a k snadnému přihlášení v místní síti.", "Edit": "Upravit", "EditImages": "Editace obrázků", "EditSubtitles": "Editovat titulky", @@ -189,16 +189,14 @@ "EndsAtValue": "Končí v {0}", "Episodes": "Epizody", "ErrorAddingListingsToSchedulesDirect": "Došlo k chybě při přidání sestavy do účtu vašeho Direct plánovače. Direct plánovač umožňuje pouze omezený počet sestav na účet. Možná se budete muset přihlásit do webových stránek Direct plánovače a před pokračováním odstranit ostatní výpisy ze svého účtu.", - "ErrorAddingMediaPathToVirtualFolder": "Nastala chyba při přidávání cesty k médiím. Zkontrolujte zda zadaná složka je validní a Jellyfin Server má k této složce přístup.", + "ErrorAddingMediaPathToVirtualFolder": "Při přidávání cesty k médiím došlo k chybě. Zkontrolujte zda je zadaná složka platná a zda má server Jellyfin k této složce přístup.", "ErrorAddingTunerDevice": "Došlo k chybě při přidání zařízení tuneru. Prosím, ujistěte se, že je přístupný a zkuste to znovu.", "ErrorAddingXmlTvFile": "Nastala chyba při přístupu k XMLTV souboru. Ujistěte se, že soubor existuje a zkuste jej znovu otevřít.", - "ErrorDeletingItem": "Nastala chyba při mazání položky z Jellyfin Serveru. Zkontrolujte prosím, že Jellyfin Server má oprávnění k zápisu do složky médií a zkuste to prosím znovu.", + "ErrorDeletingItem": "Při odstranění položky ze serveru Jellyfin došlo k chybě. Zkontrolujte prosím, zda má server Jellyfin oprávnění k zápisu do složky médií, a zkuste to prosím znovu.", "ErrorGettingTvLineups": "Došlo k chybě při stahování TV sestav. Ujistěte se prosím, že zadané informace jsou správné a zkuste to znovu.", "ErrorMessageStartHourGreaterThanEnd": "Čas ukončení musí být větší než čas startu.", "ErrorMessageUsernameInUse": "Uživatelské jméno se již používá. Prosím, vyberte nový název a zkuste to znovu.", "ErrorPleaseSelectLineup": "Vyberte prosím sestavu a zkuste to znovu. Pokud nejsou k dispozici žádné sestavy, zkontrolujte, zda je vaše uživatelské jméno, heslo a poštovní směrovací číslo správné.", - "ErrorReachingJellyfinConnect": "Došlo k chybě při navázání spojení k serveru Jellyfin Connect. Ujistěte se, zda je funkční připojení k internetu a zkuste to znovu.", - "ErrorRemovingJellyfinConnectAccount": "Nastala chyba při odebrání účtu Jellyfin Connect. Zkontrolujte zda máte aktivní internetové připojení a zkuste znovu.", "ErrorSavingTvProvider": "Při ukládání poskytovatele TV došlo k chybě. Prosím, ujistěte se, že je přístupný a zkuste to znovu.", "ExitFullscreen": "Opustit celou obrazovku", "ExtraLarge": "Extra velký", @@ -223,7 +221,7 @@ "Genres": "Žánry", "GroupVersions": "Skupinové verze", "GuestStar": "Hostující hvězda", - "Guide": "Průvodce", + "Guide": "Programový průvodce", "GuideProviderLogin": "Přihlášení", "GuideProviderSelectListings": "Výběr zobrazení", "H264CrfHelp": "Constant Rate faktor (CRF) je výchozím nastavení kvality pro kodér x264. Můžete nastavit hodnoty mezi 0 a 51, kde nižší hodnoty vedou lepší kvalitě (na úkor větší velikosti souborů). Rozumné hodnoty jsou mezi 18 a 28. Výchozí hodnota pro x264 je 23, který můžete použít jako výchozí bod.", @@ -247,7 +245,7 @@ "HeaderAlert": "Upozornění", "HeaderApiKey": "Klíč Api", "HeaderApiKeys": "Klíče Api", - "HeaderApiKeysHelp": "Externí aplikace musí mít API klíč, aby mohla komunikovat s Jellyfin serverem. Klíče jsou vydávány přihlášením pomocí účtu Jellyfin, nebo manuální žádostí o klíč.", + "HeaderApiKeysHelp": "Externí aplikace musí mít klíč k API, aby mohly komunikovat se serverem Jellyfin. Klíče jsou vydávány přihlášením k účtu Jellyfin nebo ruční žádostí o klíč.", "HeaderApp": "Aplikace", "HeaderAudioBooks": "Audio knihy", "HeaderAudioSettings": "Nastavení zvuku", @@ -299,7 +297,7 @@ "HeaderForgotPassword": "Zapomenuté heslo", "HeaderFrequentlyPlayed": "Nejčastěji přehráváno", "HeaderGenres": "Žánry", - "HeaderGuideProviders": "Průvodce poskytovatelů", + "HeaderGuideProviders": "Poskytovatelé programových průvodců", "HeaderHttpHeaders": "Http hlavičky", "HeaderIdentification": "Identifikace", "HeaderIdentificationCriteriaHelp": "Zadejte alespoň jedno identifikační kritérium.", @@ -354,7 +352,7 @@ "HeaderPreferredMetadataLanguage": "Preferovaný jazyk metadat", "HeaderProfile": "Profil", "HeaderProfileInformation": "Informace o profilu", - "HeaderProfileServerSettingsHelp": "Tyto hodnoty určují, jak se Jellyfin Server bude prezentovat v zařízení.", + "HeaderProfileServerSettingsHelp": "Tyto hodnoty určují, jak se server Jellyfin bude zobrazovat v zařízení.", "HeaderRecentlyPlayed": "Naposledy přehráváno", "HeaderRecordingOptions": "Nastavení nahrávání", "HeaderRecordingPostProcessing": "Následné zpracování nahrávek", @@ -425,7 +423,7 @@ "Identify": "Identifikuj", "Images": "Obrázky", "ImportFavoriteChannelsHelp": "Pokud je povoleno, jen kanály označené jako oblíbené budou importována na zařízení tuneru.", - "ImportMissingEpisodesHelp": "Pokud je povoleno, budou informace o chybějících epizodách importovány do databáze Jellyfin a zobrazí se v sezónách seriálu. To může způsobit podstatně delší skenování knihovny.", + "ImportMissingEpisodesHelp": "Informace o chybějících epizodách budou importovány do databáze Jellyfin a zobrazí se u sezón a seriálů. Skenování knihovny se tím může značně prodloužit.", "InstallingPackage": "Instalace {0} (Verze {1})", "InstantMix": "Okamžité míchání", "ItemCount": "{0} položek", @@ -436,7 +434,6 @@ "LabelAccessDay": "Den týdne:", "LabelAccessEnd": "Konec:", "LabelAccessStart": "Začátek:", - "LabelAddConnectSupporterHelp": "Chcete-li přidat uživatele, který není uveden v seznamu, budete muset nejprve propojit svůj účet Jellyfin Connect ze strany profilu uživatele.", "LabelAddedOnDate": "Přidáno {0}", "LabelAirDate": "Dny vysílání:", "LabelAirDays": "Vysíláno:", @@ -464,7 +461,7 @@ "LabelAudio": "Zvuk", "LabelAudioLanguagePreference": "Preferovaný jazyk zvuku:", "LabelBindToLocalNetworkAddress": "Vázat na místní síťovou adresu:", - "LabelBindToLocalNetworkAddressHelp": "Volitelné. Přepsat lokální IP adresu vazanou na http server. Pokud je ponecháno prázdné, server se sváže ke všem dostupným adresám (aplikace bude dostupná na všech síťových zařízení, které server nabízí). Změna této hodnoty vyžaduje restartování Jellyfin Serveru.", + "LabelBindToLocalNetworkAddressHelp": "Volitelné. Změní místní IP adresu, na kterou se váže server HTTP. Pokud je ponecháno prázdné, server bude svázán se všemi dostupnými adresami. Změna této hodnoty vyžaduje restartování serveru Jellyfin.", "LabelBirthDate": "Datum narození:", "LabelBirthYear": "Rok narození:", "LabelBlastMessageInterval": "Doba zobrazení zprávy (v sekundách)", @@ -484,7 +481,7 @@ "LabelCustomDeviceDisplayName": "Jméno pro zobrazení:", "LabelCustomDeviceDisplayNameHelp": "Nahradit vlastním názvem zobrazení nebo ponechte prázdné, aby název byl určen zařízením.", "LabelCustomRating": "Vlastní hodnocení:", - "LabelDashboardTheme": "Téma hlavní nabídky serveru:", + "LabelDashboardTheme": "Téma nástěnky serveru:", "LabelDateAdded": "Datum přidání:", "LabelDateAddedBehavior": "Nový obsah řadit dle data:", "LabelDateAddedBehaviorHelp": "Pokud je hodnota metadat přítomna, bude vždy použita před některou z těchto možností.", @@ -498,7 +495,7 @@ "LabelDidlMode": "DIDL režim:", "LabelDiscNumber": "Číslo disku:", "LabelDisplayLanguage": "Jazyk rozhraní:", - "LabelDisplayLanguageHelp": "Překlad Jellyfin je projekt ve fázi neustálého vývoje.", + "LabelDisplayLanguageHelp": "Překlad projektu Jellyfin se neustále vyvíjí.", "LabelDisplayMissingEpisodesWithinSeasons": "Zobrazit chybějící epizody", "LabelDisplayMissingEpisodesWithinSeasonsHelp": "Toto musí být zapnuto pro knihovny TV v nastavení Jellyfin serveru.", "LabelDisplayMode": "Režim zobrazení:", @@ -511,16 +508,16 @@ "LabelDownloadInternetMetadataHelp": "Jellyfin server může stahovat informace o vašich médiích, aby umožnil vylepšené prezentace.", "LabelDownloadLanguages": "Stahované jazyky:", "LabelDropImageHere": "Sem přetáhněte obrázek nebo klikněte pro procházení.", - "LabelDropShadow": "Vrhat stín:", + "LabelDropShadow": "Typ zvýraznění titulků:", "LabelEasyPinCode": "Kód Easy pin:", "LabelEmbedAlbumArtDidl": "Vložit alba do DIDL", "LabelEmbedAlbumArtDidlHelp": "Některá zařízení preferují tento způsob pro získání alba. Jiné mohou selhat pokud máte tuto volbu povolenu.", "LabelEnableAutomaticPortMap": "Povolit automatické mapování portů", - "LabelEnableAutomaticPortMapHelp": "Pokusí se automaticky namapovat veřejný port místního portu přes UPnP na vašem routeru. Nemusí fungovat u některých modelů routeru. Změny se projeví až po restartování serveru.", + "LabelEnableAutomaticPortMapHelp": "Automaticky zpřístupní port na vašem routeru pomocí technologie UPnP. Nemusí fungovat u některých routerů. Změny se projeví až po restartování serveru.", "LabelEnableBlastAliveMessages": "Vytroubit zprávu do světa", - "LabelEnableBlastAliveMessagesHelp": "Povolit v případě, že server není zjistitelný jinými UPnP zařízeními v síti.", + "LabelEnableBlastAliveMessagesHelp": "Tuto možnost povolte, pokud není server zjistitelný jinými UPnP zařízeními v síti.", "LabelEnableDlnaClientDiscoveryInterval": "Čas pro vyhledání klienta (sekund)", - "LabelEnableDlnaClientDiscoveryIntervalHelp": "Určuje dobu trvání v sekundách mezi SSDP vyhledávání prováděných pomocí Jellyfin.", + "LabelEnableDlnaClientDiscoveryIntervalHelp": "Určuje interval mezi vyhledáváním SSDP, které Jellyfin provádí.", "LabelEnableDlnaDebugLogging": "Povolit DLNA protokolování (pro ladění)", "LabelEnableDlnaDebugLoggingHelp": "Vytváří velké soubory se záznamy a doporučuje se používat pouze pro potřeby odstraňování problémů.", "LabelEnableDlnaPlayTo": "Povolit DLNA přehrávání", @@ -546,8 +543,6 @@ "LabelFormat": "Formát:", "LabelFree": "Zdarma", "LabelFriendlyName": "Přívětivý název:", - "LabelFriendlyServerName": "Název serveru:", - "LabelFriendlyServerNameHelp": "Toto jméno bude použito jako identifikace serveru, ponecháte-li prázdné bude použit název počítače.", "LabelFromHelp": "Například: {0} (na serveru)", "LabelGroupMoviesIntoCollections": "Seskupit filmy do kolekcí", "LabelGroupMoviesIntoCollectionsHelp": "Při zobrazení seznamů filmu, budou filmy patřící do kolekce, zobrazeny jako jedna položka.", @@ -557,7 +552,7 @@ "LabelHardwareAccelerationTypeHelp": "Hardwarová akcelerace vyžaduje další konfiguraci.", "LabelHomeScreenSectionValue": "Sekce domovské obrazovky {0}:", "LabelHttpsPort": "Lokální HTTPS port:", - "LabelHttpsPortHelp": "Číslo portu TCP, ke kterému by se měl připojit HTTPS server Jellyfin.", + "LabelHttpsPortHelp": "Číslo portu TCP, ke kterému by se měl HTTPS server Jellyfin připojit.", "LabelIconMaxHeight": "Maximální výška ikon:", "LabelIconMaxHeightHelp": "Maximální rozlišení ikon nabízené prostřednictvím upnp:icon.", "LabelIconMaxWidth": "Maximální šířka ikon:", @@ -580,10 +575,9 @@ "LabelKodiMetadataSaveImagePathsHelp": "Toto nastavení je doporučeno, pokud používáte názvy souborů (obrázků), které nejsou v souladu s pokyny Kodi.", "LabelLanguage": "Jazyk:", "LabelLastResult": "Poslední výsledky:", - "LabelLimitIntrosToUnwatchedContent": "Přehrávat trailery pouze u nezhlédnutého obsahu", - "LabelLineup": "Hlavní linie:", + "LabelLineup": "V pořadí:", "LabelLocalHttpServerPortNumber": "Lokální HTTP port:", - "LabelLocalHttpServerPortNumberHelp": "Číslo portu TCP, ke kterému by se měl připojit HTTP server Jellyfin.", + "LabelLocalHttpServerPortNumberHelp": "Číslo portu TCP, ke kterému by se měl HTTP server Jellyfin připojit.", "LabelLockItemToPreventChanges": "Uzamknout položku pro zabránění budoucích změn", "LabelLoginDisclaimer": "Zřeknutí se následujících práv při přihlášení:", "LabelLoginDisclaimerHelp": "Zpráva, která se zobrazí v dolní části přihlašovací stránky.", @@ -634,8 +628,8 @@ "LabelNext": "Další", "LabelNotificationEnabled": "Povolit toto oznámení", "LabelNumber": "Číslo:", - "LabelNumberOfGuideDays": "Počet dnů pro stažení dat průvodce:", - "LabelNumberOfGuideDaysHelp": "Stažením více dnů dat průvodce umožní v plánech nastavit budoucí nahrávání více do budoucna. Může však déle trvat stažení těchto dat. Auto vybere možnost podle počtu kanálů.", + "LabelNumberOfGuideDays": "Počet dnů programového průvodce ke stažení:", + "LabelNumberOfGuideDaysHelp": "Stažení více dnů televizního průvodce umožňuje naplánovat nahrávání na delší dobu dopředu, ale trvá déle. Automatické nastavení určí počet podle počtu kanálů.", "LabelOptionalNetworkPath": "(Nepovinné) Sdílená síťová složka:", "LabelOriginalAspectRatio": "Původní poměr stran:", "LabelOriginalTitle": "Originální název:", @@ -653,7 +647,7 @@ "LabelPostProcessorArguments": "Argumenty příkazové řádky pro následné zpracování:", "LabelPostProcessorArgumentsHelp": "Použij {path} jako cestu k nahrávanému souboru.", "LabelPreferredDisplayLanguage": "Preferovaný jazyk zobrazení:", - "LabelPreferredDisplayLanguageHelp": "Překlad Jellyfin je projekt ve fázi neustálého vývoje.", + "LabelPreferredDisplayLanguageHelp": "Překlad projektu Jellyfin se neustále vyvíjí.", "LabelPreferredSubtitleLanguage": "Preferovaný jazyk titulků:", "LabelPrevious": "Předchozí", "LabelProfileAudioCodecs": "Audio kodeky:", @@ -696,7 +690,7 @@ "LabelSkipIfAudioTrackPresent": "Přeskočit, pokud výchozí zvuková stopa odpovídá jazyku stahování", "LabelSkipIfAudioTrackPresentHelp": "Zrušte zaškrtnutí pro zobrazení titulků u všech videí, bez ohledu na jazyk zvuku.", "LabelSkipIfGraphicalSubsPresent": "Přeskočit, jestliže video obsahuje vložené titulky", - "LabelSkipIfGraphicalSubsPresentHelp": "Udržením textových verzí titulků bude mít za následek efektivnější dodávku a sníží tím pravděpodobnost překódování videa.", + "LabelSkipIfGraphicalSubsPresentHelp": "Ponecháním textových titulků je možné dosáhnout efektivnějšího přenosu videa a snížení pravděpodobnosti, že bude video nutné překódovat.", "LabelSonyAggregationFlags": "Agregační příznaky Sony:", "LabelSonyAggregationFlagsHelp": "Určuje obsah prvku aggregationFlags ve jmenném prostoru urn:schemas-sonycom:av.", "LabelSortTitle": "Třídit dle názvu:", @@ -796,13 +790,13 @@ "MessageConfirmProfileDeletion": "Jste si jisti, že chcete smazat tento profil?", "MessageConfirmRecordingCancellation": "Zrušit nahrávání?", "MessageConfirmRemoveMediaLocation": "Jste si jist, že chcete odstranit toto umístění?", - "MessageConfirmRestart": "Jste si jist, že chcete restartovat Jellyfin server?", - "MessageConfirmRevokeApiKey": "Jste si jisti, že chcete odvolat tento klíč API? Připojení k aplikaci k Jellyfin Server bude násilně ukončeno.", + "MessageConfirmRestart": "Opravdu chcete restartovat server Jellyfin?", + "MessageConfirmRevokeApiKey": "Opravdu chcete zrušit tento klíč k API? Připojení dané aplikace k serveru Jellyfin bude náhle ukončeno.", "MessageConfirmShutdown": "Jste si jisti, že chcete server vypnout?", "MessageContactAdminToResetPassword": "Kontaktujte, prosím, vašeho systémového administrátora k obnovení vašeho hesla.", "MessageCreateAccountAt": "Vytvořit účet v {0}", "MessageDeleteTaskTrigger": "Opravdu si přejete odebrat spouštění úlohy?", - "MessageDirectoryPickerBSDInstruction": "Pro BSD, budete možná muset nakonfigurovat úložiště přímo ve Vašem FreeNAS Jail aby k nim Jellyfin povolil přístup.", + "MessageDirectoryPickerBSDInstruction": "V operačním systému FreeBSD či FreeNAS může být nutné nakonfigurovat úložiště přímo pomocí izolační funkce jail, aby k němu měl Jellyfin přístup.", "MessageDirectoryPickerInstruction": "Síťové cesty lze zadat ručně v případě, že tlačítko 'Síť' nedokáže automaticky lokalizovat vaše zařízení. Například, {0} nebo {1}.", "MessageDirectoryPickerLinuxInstruction": "Pro systémy Linux jako Arch Linux, CentOS, Debian, Fedora, OpenSUSE nebo Ubuntu musíte udělit uživateli služby oprávnění alespoň pro čtení.", "MessageDownloadQueued": "Stažení zařazeno.", @@ -818,15 +812,13 @@ "MessageNoAvailablePlugins": "Nejsou dostupné žádné zásuvné moduly.", "MessageNoMovieSuggestionsAvailable": "Žádné návrhy nejsou v současnosti k dispozici. Začněte sledovat a hodnotit filmy, a pak se vám doporučení zobrazí.", "MessageNoPluginsInstalled": "Nemáte instalovány žádné zásuvné moduly.", - "MessageNoTrailersFound": "Nebyly nalezeny žádné ukázky z filmů (trailery). Nainstalujte trailer kanál pro lepší zážitek z filmu přidáním knihovny internetových trailerů.", + "MessageNoTrailersFound": "Nebyly nalezeny žádné upoutávky k filmu. Chcete-li si zlepšit zážitek ze sledování, nainstalujte si kanál s upoutávkami.", "MessageNothingHere": "Tady nic není.", "MessagePasswordResetForUsers": "Obnovení hesla bylo provedeno následujícími uživateli. Nyní se mohou přihlásit pomocí kódů PIN, které byly použity k provedení resetu.", "MessagePlayAccessRestricted": "Přehrávání tohoto obsahu je aktuálně omezeno. Další informace získáte od správce serveru.", "MessagePleaseEnsureInternetMetadata": "Prosím zkontrolujte, zda máte povoleno stahování metadat z internetu.", - "MessagePleaseRestart": "Pro dokončení aktualizací, prosím, restartujte.", - "MessagePleaseRestartServerToFinishUpdating": "Restartujte, prosím, server pro aplikaci aktualizací.", "MessagePluginConfigurationRequiresLocalAccess": "Pro konfiguraci zásuvného modulu se přihlaste přímo na lokální server.", - "MessagePluginInstallDisclaimer": "Zasuvné moduly vytvořené členy Jellyfin komunity jsou skvělý způsob, jak zvýšit svůj Jellyfin prožitek pomocí doplňkových funkcí :-) Před instalací, se prosím seznamte se všemi dopady, které mohou mít na Jellyfin Server, jako je například delší prohledávání knihovny, další zpracování na pozadí, a snížení stability systému.", + "MessagePluginInstallDisclaimer": "Zásuvné moduly vytvořené členy komunity Jellyfin jsou skvělým způsobem, jak si zlepšit prožitek z používání projektu Jellyfin. Před instalací se prosím seznamte se všemi dopady, které mohou doplňky mít na server Jellyfin, např.: pomalejší skenování knihovny, další zpracování na pozadí nebo snížení stability systému.", "MessageReenableUser": "Viz níže pro znovuzapnutí", "MessageSettingsSaved": "Nastavení uloženo.", "MessageTheFollowingLocationWillBeRemovedFromLibrary": "Z vaší knihovny budou odstraněny následující zdroje médií:", @@ -844,7 +836,7 @@ "Mobile": "Mobilní", "Monday": "Pondělí", "MoreFromValue": "Více z {0}", - "MoreUsersCanBeAddedLater": "Další uživatele můžete přidat později na Hlavní nabídce.", + "MoreUsersCanBeAddedLater": "Další uživatele můžete přidat později na nástěnce serveru.", "MoveLeft": "Posunout vlevo", "MoveRight": "Posunout vpravo", "Movies": "Filmy", @@ -875,11 +867,11 @@ "OptionAlbumArtist": "Umělec Alba", "OptionAllUsers": "Všichni uživatelé", "OptionAllowAudioPlaybackTranscoding": "Povolit přehrávání audia, které vyžaduje překódování", - "OptionAllowBrowsingLiveTv": "Povolit přístup k Live TV", + "OptionAllowBrowsingLiveTv": "Povolit přístup k televiznímu vysílání", "OptionAllowContentDownloading": "Povolit stahování médií", "OptionAllowLinkSharing": "Povolit sdílení pomocí sociálních médií", "OptionAllowLinkSharingHelp": "Sdílené jsou pouze webové stránky obsahující informace o médiích. Obsah souboru není nikdy sdílen veřejně. Sdílené položky jsou časově omezené a jejich platnost vyprší za {0} dny.", - "OptionAllowManageLiveTv": "Povolit správu nahrávání z Live TV", + "OptionAllowManageLiveTv": "Povolit správu nahrávání z televize", "OptionAllowMediaPlayback": "Povolit přehrávání médií", "OptionAllowRemoteControlOthers": "Povolit vzdálené ovládání ostatních uživatelů", "OptionAllowRemoteSharedDevices": "Povolit vzdálené ovládání sdílených zařízení", @@ -897,7 +889,7 @@ "OptionAutomaticallyGroupSeriesHelp": "Pokud je povoleno, budou díly seriálu uložené ve více adresářích v této knihovně, automaticky sloučeny k jednomu seriálu.", "OptionBlockBooks": "Knihy", "OptionBlockChannelContent": "Obsah internetového kanálu", - "OptionBlockLiveTvChannels": "Kanály Live TV", + "OptionBlockLiveTvChannels": "Televizní kanály", "OptionBlockMovies": "Filmy", "OptionBlockMusic": "Hudba", "OptionBlockTrailers": "Upoutávky", @@ -921,7 +913,7 @@ "OptionDownloadBackImage": "Zadek", "OptionDownloadDiscImage": "Disk", "OptionDownloadImagesInAdvance": "Stáhnout obrázky pokročilejším způsobem", - "OptionDownloadImagesInAdvanceHelp": "Ve výchozím nastavení se většina obrázků stahuje pouze na žádost aplikace Jellyfin. Povolte tuto možnost pro stažení všech obrázků předem, když se importují nová média. To může způsobit výrazně delší skenování knihovny.", + "OptionDownloadImagesInAdvanceHelp": "Ve výchozím nastavení se většina obrázků stahuje pouze na žádost aplikace Jellyfin. Povolením této možnosti dojde ke stažení všech obrázků předem současně s importem nových médií. Může způsobit výrazně delší skenování knihoven.", "OptionDownloadMenuImage": "Nabídka", "OptionDownloadPrimaryImage": "Primární", "OptionDownloadThumbImage": "Miniatura", @@ -931,7 +923,7 @@ "OptionEnableAccessToAllChannels": "Povolit přístup ze všech kanálů", "OptionEnableAccessToAllLibraries": "Povolit přístup ke všem knihovnám", "OptionEnableExternalContentInSuggestions": "Aktivovat externí obsah v návrzích", - "OptionEnableExternalContentInSuggestionsHelp": "Povolit zahrnutí internetových upoutávek a živých televizních programů do navrhovaného obsahu.", + "OptionEnableExternalContentInSuggestionsHelp": "Povolit zahrnutí internetových upoutávek a televizního vysílání do navrhovaného obsahu.", "OptionEnableForAllTuners": "Povolit pro všechna zařízení tunerů", "OptionEnableM2tsMode": "Povolit M2ts mód", "OptionEnableM2tsModeHelp": "Povolit režim M2TS při kódování do MPEGTS.", @@ -947,7 +939,7 @@ "OptionHasSubtitles": "Titulky", "OptionHasThemeSong": "Tematická hudba", "OptionHasThemeVideo": "Tematické video", - "OptionHasTrailer": "Ukázka/trailer", + "OptionHasTrailer": "Upoutávka", "OptionHideUser": "Skrýt tohoto uživatele z přihlašovacích obrazovek", "OptionHideUserFromLoginHelp": "Vhodné pro soukromé a administrátorské účty. Pro přihlášení musí uživatel manuálně zadat uživatelské jméno a heslo.", "OptionHlsSegmentedSubtitles": "Segmentované titulky HLS", @@ -959,7 +951,7 @@ "OptionMissingEpisode": "Chybějící episody", "OptionMonday": "Pondělí", "OptionNameSort": "Název", - "OptionNew": "Nový...", + "OptionNew": "Nový…", "OptionNone": "Žádný", "OptionOnAppStartup": "Při spuštění aplikace", "OptionOnInterval": "V intervalu", @@ -977,12 +969,12 @@ "OptionReportByteRangeSeekingWhenTranscoding": "Hlásit, že server podporuje vyhledávání bajtů při překódování", "OptionReportByteRangeSeekingWhenTranscodingHelp": "Tento krok je nutný pro některá zařízení, které nemají moc dobrý time seek.", "OptionRequirePerfectSubtitleMatch": "Stahovat jen titulky, které perfektně sedí k mým video souborům", - "OptionResElement": "Zdrojový element", + "OptionResElement": "Prvek \"res\"", "OptionResumable": "Pozastavavitelný", "OptionRuntime": "Délka", "OptionSaturday": "Sobota", "OptionSaveMetadataAsHidden": "Ukládat metadata a obrázky jako skryté soubory", - "OptionSaveMetadataAsHiddenHelp": "Změna bude platit pro nově uložená metadata do budoucna. Existující soubory metadat budou aktualizovány příště, jakmile budou uloženy Jellyfin Serverem.", + "OptionSaveMetadataAsHiddenHelp": "Změna se projeví u všech nově uložených metadat. Existující soubory metadat se aktualizují při příštím uložení serverem Jellyfin.", "OptionSpecialEpisode": "Speciální", "OptionSubstring": "subřetězec", "OptionSunday": "Neděle", @@ -999,9 +991,9 @@ "OptionWeekly": "Týdenní", "OriginalAirDateValue": "Datum vysílání originálu: {0}", "Overview": "Přehled/Obsah", - "PackageInstallCancelled": "Instalace {0} zrušena.", - "PackageInstallCompleted": "Instalace {0} dokončena.", - "PackageInstallFailed": "Instalace {0} selhala.", + "PackageInstallCancelled": "Instalace {0} (verze {1}) zrušena.", + "PackageInstallCompleted": "Instalace {0} (verze {1}) dokončena.", + "PackageInstallFailed": "Instalace {0} (verze {1}) selhala.", "ParentalRating": "Rodičovské hodnocení", "PasswordMatchError": "Heslo a potvrzení hesla musí souhlasit.", "PasswordResetComplete": "Heslo bylo obnoveno.", @@ -1022,7 +1014,7 @@ "PlayNext": "Přehrát další", "PlayNextEpisodeAutomatically": "Automaticky přehrávat další epizodu", "PlayOnAnotherDevice": "Přehrát na jiném zařízení", - "PlaybackErrorNoCompatibleStream": "Žádné kompatibilní streamy nejsou v současné době k dispozici. Zkuste to prosím později, nebo pro více podrobností kontaktujte svého správce systému.", + "PlaybackErrorNoCompatibleStream": "Tento klient není kompatibilní s médiem a server neodesílá kompatibilní formát médií.", "PlaybackErrorNotAllowed": "V současné době nejste oprávněni přehrávat tento obsah. Pro více informací se obraťte se na správce systému.", "PlaybackErrorPlaceHolder": "Chcete-li toto video přehrát, vložte disk.", "Played": "Přehráno", @@ -1030,7 +1022,7 @@ "PleaseAddAtLeastOneFolder": "Přidejte prosím nejméně jednu složku do této knihovny pomocí tlačítka Přidat.", "PleaseConfirmPluginInstallation": "Pro potvrzení, že jste si přečetli text výše a chcete pokračovat v instalaci zásuvných modulů, klikněte na tlačítko OK.", "PleaseEnterNameOrId": "Prosím, zadejte název nebo externí Id.", - "PleaseRestartServerName": "Prosím, restartujte Jellyfin Server - {0}.", + "PleaseRestartServerName": "Prosím restartuje server Jellyfin - {0}.", "PleaseSelectTwoItems": "Vyberte nejméně dvě položky prosím.", "Premiere": "Premiéra", "Premieres": "Premiéry", @@ -1053,7 +1045,7 @@ "RecordingScheduled": "Plán nahrávání.", "Recordings": "Nahrávky", "Refresh": "Obnovit", - "RefreshDialogHelp": "Metadata se aktualizují na základě nastavení a internetových služeb, které jsou povoleny v nastavení Jellyfin Server.", + "RefreshDialogHelp": "Metadata se aktualizují na základě nastavení a internetových služeb, které jsou povoleny na nástěnce serveru Jellyfin.", "RefreshMetadata": "Obnovit metadata", "RefreshQueued": "Obnovení zařazeno.", "ReleaseDate": "Datum vydání", @@ -1067,7 +1059,7 @@ "RepeatOne": "Opakovat jeden", "ReplaceAllMetadata": "Přepsat všechna metadata", "ReplaceExistingImages": "Nahradit existující obrázky", - "RestartPleaseWaitMessage": "Počkejte prosím, než se Jellyfin Server vypne a restartuje. Může to trvat pár minut.", + "RestartPleaseWaitMessage": "Počkejte prosím, než se server Jellyfin vypne a restartuje. Může to trvat několik minut.", "ResumeAt": "Obnovit přehrávání od {0}", "Rewind": "Přetočit zpět", "RunAtStartup": "Spustit po startu", @@ -1089,9 +1081,9 @@ "SeriesRecordingScheduled": "Plán nahrávání seriálu.", "SeriesSettings": "Nastavení seriálu", "SeriesYearToPresent": "{0} - Současnost", - "ServerNameIsRestarting": "Jellyfin Server - {0} je restartován.", - "ServerNameIsShuttingDown": "Jellyfin Server - {0} je vypínán.", - "ServerUpdateNeeded": "Tento Jellyfin Server je třeba aktualizovat. Chcete-li stáhnout nejnovější verzi, navštivte prosím {0}", + "ServerNameIsRestarting": "Server Jellyfin - {0} se restartuje.", + "ServerNameIsShuttingDown": "Server Jellyfin - {0} se vypíná.", + "ServerUpdateNeeded": "Tento server Jellyfin je nutné aktualizovat. Chcete-li stáhnout nejnovější verzi, navštivte prosím {0}", "Settings": "Nastavení", "SettingsSaved": "Nastavení uloženo.", "SettingsWarning": "Změna těchto hodnot může způsobit nestabilitu nebo selhání připojení. Pokud narazíte na nějaké problémy, doporučujeme jej změnit zpět na výchozí hodnotu.", @@ -1126,13 +1118,13 @@ "TabCodecs": "Kodeky", "TabCollections": "Kolekce", "TabContainers": "Obaly", - "TabDashboard": "Hlavní nabídka", + "TabDashboard": "Nástěnka", "TabDevices": "Zařízení", "TabDisplay": "Zobrazení", "TabEpisodes": "Epizody", "TabFavorites": "Oblíbené", "TabGenres": "Žánry", - "TabGuide": "Průvodce", + "TabGuide": "Programový průvodce", "TabLatest": "Nejnovější", "TabLogs": "Záznamy", "TabMovies": "Filmy", @@ -1160,7 +1152,7 @@ "TabSongs": "Skladby", "TabStreaming": "Streamování", "TabSuggestions": "Návrhy", - "TabTrailers": "Ukázky/trailery", + "TabTrailers": "Upoutávky", "TabTranscoding": "Překódování", "TabUpcoming": "Nové", "TabUsers": "Uživatelé", @@ -1182,7 +1174,7 @@ "Unrated": "Nehodnoceno", "Up": "Nahoru", "Upload": "Nahrát", - "UserProfilesIntro": "Jellyfin zahrnuje podporu uživatelských profilů s podrobným nastavením zobrazení, stavem přehrávání a rodičovskou kontrolou.", + "UserProfilesIntro": "Jellyfin podporuje uživatelské profily s podrobným nastavením zobrazení, stavem přehrávání a rodičovskou kontrolou.", "ValueAlbumCount": "{0} alb", "ValueAudioCodec": "Audio kodeky: {0}", "ValueCodec": "Kodek: {0}", @@ -1208,47 +1200,42 @@ "Watched": "Zhlédnuto", "Wednesday": "Středa", "WelcomeToProject": "Vítejte v Jellyfin!", - "WizardCompleted": "To je vše, co nyní potřebujeme. Jellyfin začala shromažďovat informace o vaší knihovně médií. Podívejte se na některé z našich aplikací, a potom klepněte na tlačítko Dokončit pro zobrazení hlavního panelu.", + "WizardCompleted": "To je zatím vše, co potřebujeme. Server Jellyfin začal shromažďovat informace o vaší knihovně médií. Vyzkoušejte některé z našich aplikací a potom klikněte na tlačítko Dokončit pro zobrazení nástěnky.", "Writer": "Napsal", "XmlDocumentAttributeListHelp": "Tyto atributy jsou použity na kořenový prvek každé XML odpovědi.", "XmlTvKidsCategoriesHelp": "Programy s těmito kategoriemi budou zobrazeny jako programy pro děti. Více kategorií oddělte \"|\".", "XmlTvMovieCategoriesHelp": "Programy s těmito kategoriemi budou zobrazeny jako filmy. Více kategorií oddělte \"|\".", "XmlTvNewsCategoriesHelp": "Programy s těmito kategoriemi budou zobrazeny jako zpravodajské pořady. Více kategorií oddělte \"|\".", - "XmlTvPathHelp": "Cesta k souboru XMLTV. Jellyfin tento soubor načte a pravidelně jej kontroluje, zda neobsahuje aktualizace. Jste zodpovědní za vytvoření a aktualizaci souboru.", + "XmlTvPathHelp": "Cesta k souboru XMLTV. Jellyfin tento soubor načte a bude pravidelně kontrolovat, zda neobsahuje aktualizace. Vytvoření a aktualizace souboru je na uživateli.", "XmlTvSportsCategoriesHelp": "Programy s těmito kategoriemi budou zobrazeny jako sportovní pořady. Více kategorií oddělte \"|\".", "Yes": "Ano", "Yesterday": "Včera", "Absolute": "Absolutní", - "AddUserByManually": "Přidat místního uživatele ručním zadáním informací.", "AirDate": "Datum vysílání", "Aired": "Vysíláno", "Alerts": "Upozornění", - "AllComplexFormats": "Všechny komplexní formáty (ASS, SSA, VOBSUB, PGS, SUB/IDX, atd.)", + "AllComplexFormats": "Všechny komplexní formáty (ASS, SSA, VOBSUB, PGS, SUB/IDX, atd.)", "AllLibraries": "Všechny knihovny", "AllowDeletionFromAll": "Povolit smazání médií ze všech knihoven", "AllowMediaConversion": "Povolit konverzi médií", "AllowMediaConversionHelp": "Povolit nebo zakázat přístup k funkci konverze médií.", "AllowOnTheFlySubtitleExtraction": "Povolit extrahování titulků za běhu", - "AllowOnTheFlySubtitleExtractionHelp": "Vložené titulky mohou být extrahovány z videa a dodávány do aplikací ve formě prostého textu, aby se zabránilo překódování videa. V některých systémech to může trvat dlouho a způsobit zasekávání přehrávání videa. Při vypnutí funkce budou během překódování obsažené titulky vypáleny do obrazu, pokud je klientské zařízení nativně nepodporuje.", - "AllowRemoteAccess": "Povolit vzdálené připojení na tento Jellyfin server.", + "AllowOnTheFlySubtitleExtractionHelp": "Vložené titulky je možné vytáhnout z videa a dodat klientům v textové podobě, aby nebylo nutné video překódovat. Na některých systémech to může trvat dlouho a způsobit zasekávání videa. Pokud tuto funkci vypnete a klientské zařízení vložené titulky nepodporuje, při překódování budou vypáleny přímo do obrazu.", + "AllowRemoteAccess": "Povolit vzdálené připojení k tomuto serveru Jellyfin.", "AllowRemoteAccessHelp": "Pokud není zapnuto, všechna vzdálená připojení budou blokována.", - "AllowSeasonalThemesHelp": "Pokud je povoleno, sezónní motivy občas přepíšou nastavení vašeho motivu.", "AllowedRemoteAddressesHelp": "Seznam IP adres nebo síťových masek oddělených čárkou pro sítě, ze kterých se lze vzdáleně připojit. Pokud necháte prázdné, všechny adresy budou povoleny.", "AnamorphicVideoNotSupported": "Anamorfní video není podporováno", - "AndroidUnlockRestoreHelp": "Chcete-li obnovit předchozí nákup, ujistěte se, že jste přihlášeni do zařízení se stejným účtem Google (nebo Amazon), na kterém byl nákup původně proveden. Ujistěte se, že je povolen přístup k úložišti aplikací a není omezen žádnou rodičovskou kontrolou a ujistěte se, že máte aktivní připojení k internetu. Toto budete muset provést pouze jednou, abyste tento předchozí nákup obnovili.", "AnyLanguage": "Jakýkoli jazyk", "Ascending": "Vzestupně", "AudioBitDepthNotSupported": "Bitová hloubka zvuku není podporována", "AudioSampleRateNotSupported": "Frekvence vzorkování zvuku není podporována", "AutoBasedOnLanguageSetting": "Automaticky (na základě jazykového nastavení)", - "AutomaticallyConvertNewContent": "Automaticky zkonvertovat nový obsah", - "AutomaticallyConvertNewContentHelp": "Nový obsah přidaný do této složky bude automaticky zkonvertován.", - "Banner": "Banner", + "Banner": "Výřez plakátu", "BestFit": "Nejvhodnější", "Blacklist": "Černá listina", "BobAndWeaveWithHelp": "Bob and weave (vyšší kvalita, ale pomalejší)", "Browse": "Procházet", - "BurnSubtitlesHelp": "Určuje, zda má server vypalovat titulky při překódování videa. Vynechání tohoto zlepší výkon serveru. Chcete-li vypálit grafické formáty (VOBSUB, PGS, SUB / IDX atd.) a některé titulky ASS / SSA, vyberte možnost Auto.", + "BurnSubtitlesHelp": "Určuje, zda má server při překódování videa vypálit titulky do obrazu. Tato funkce má velký negativní vliv na výkon. Chcete-li vypálit grafické formáty titulků (VOBSUB, PGS, SUB, IDX, atd.) a některé titulky ASS nebo SSA, vyberte možnost Automaticky.", "ButtonInfo": "Info", "ButtonMenu": "Menu", "ButtonOk": "Ok", @@ -1271,7 +1258,7 @@ "ConvertingDots": "Konverze...", "CriticRating": "Kritické hodnocení", "DefaultCameraUploadPathHelp": "Vyberte vlastní cestu nahrávání. Pokud zůstane prázdné, bude použita výchozí složka. Pokud použijete vlastní cestu, bude třeba ji přidat jako knihovnu v nastavení knihovny Jellyfin.", - "Depressed": "Stlačený", + "Depressed": "Vytlačené", "Descending": "Klesající", "DetectingDevices": "Hledání zařízení", "DirectPlayError": "Chyba přímého přehrávání", @@ -1295,7 +1282,7 @@ "Features": "Funkce", "Filters": "Filtry", "Folders": "Složky", - "General": "Hlavní", + "General": "Obecné", "Genre": "Žánr", "GroupBySeries": "Seskupit podle série", "HandledByProxy": "Zpracováno reverzním proxy", @@ -1317,11 +1304,10 @@ "HeaderFetcherSettings": "Nastavení načítání", "HeaderImageLogo": "Logo", "HeaderImageOptions": "Volby obrázku", - "HeaderInviteWithJellyfinConnect": "Pozvat s Jellyfin Connect", "HeaderKodiMetadataHelp": "Chcete-li povolit nebo zakázat Nfo metadata, upravte nastavení knihovny v sekci ukládání metadat.", - "HeaderLiveTV": "Živá TV", - "HeaderLiveTv": "Živá TV", - "HeaderLiveTvTunerSetup": "Nastavení tuneru Live TV", + "HeaderLiveTV": "Televize", + "HeaderLiveTv": "Televize", + "HeaderLiveTvTunerSetup": "Nastavení televizního tuneru", "HeaderMenu": "Menu", "HeaderNewDevices": "Nové zařízení", "HeaderPhotoAlbums": "Fotoalba", @@ -1335,14 +1321,10 @@ "HeaderTV": "TV", "HeaderTypeImageFetchers": "{0} stahovačů obrázků", "HeaderUpcomingEpisodes": "Následující epizody", - "HeaderUpcomingNews": "Následující novinky", "HeaderVideo": "Video", "HeaderVideoType": "Formát videa", "Horizontal": "Vodorovně", - "HowWouldYouLikeToAddUser": "Jak chcete přidat uživatele?", "HttpsRequiresCert": "Chcete-li povolit zabezpečená připojení, budete muset zadat důvěryhodný certifikát SSL, například Let's Encrypt. Zadejte prosím certifikát nebo zakažte zabezpečená připojení.", - "Invitations": "Pozvánky", - "InviteAnJellyfinConnectUser": "Přidejte uživatele odesláním e-mailové pozvánky.", "KeepDownload": "Zachovat stahování", "LabelAlbum": "Album:", "LabelAllowedRemoteAddresses": "Filtr vzdálené IP adresy:", @@ -1355,7 +1337,6 @@ "LabelCameraUploadPathHelp": "Vyberte vlastní umístění nahraných souborů. Toto přepíše jakékoli výchozí nastavení v sekci nahrávání souborů z fotoaparátu. Pokud použijete vlastní umístění, bude potřeba jej také přidat jako knihovnu v nastavení Jellyfin.", "LabelCertificatePassword": "Heslo certifikátu:", "LabelCertificatePasswordHelp": "Pokud certifikát vyžaduje heslo, zadejte jej prosím zde.", - "LabelConvertTo": "Konvertovat na:", "LabelCustomCertificatePath": "Vlastní umístění SSL certifikátu:", "LabelCustomCertificatePathHelp": "Umístění souboru PKCS #12, který obsahuje certifikát a soukromý klíč k povolení podpory TLS na vlastní doméně.", "LabelDateTimeLocale": "Místní nastavení data:", @@ -1372,7 +1353,7 @@ "LabelLimit": "Limit:", "LabelMaxStreamingBitrate": "Maximální kvalita streamování:", "LabelMetadata": "Metadata:", - "LabelOptionalNetworkPathHelp": "Pokud je tato složka sdílena ve vaší síti, může poskytování cesty ke sdílené složce umožnit aplikacím Jellyfin v jiných zařízeních přímý přístup k mediálním souborům.", + "LabelOptionalNetworkPathHelp": "Pokud je tato složka sdílena ve vaší síti, zadání cesty ke sdílené složce umožní aplikacím Jellyfin na jiných zařízeních přímý přístup k souborům s médii. Například {0} nebo {1}.", "LabelPersonRole": "Úloha:", "LabelPlaylist": "Playlist:", "LabelReasonForTranscoding": "Důvod pro překódování:", @@ -1385,7 +1366,6 @@ "LabelSortOrder": "Pořadí řazení:", "LabelSpecialSeasonsDisplayName": "Zobrazovaný název pro zvláštní sezónu:", "LabelSubtitleDownloaders": "Stahovače titulků:", - "LabelSyncNoTargetsHelp": "Vypadá to, že v současné době nemáte žádné aplikace, které podporují stahování offline.", "LabelTVHomeScreen": "Hlavní obrazovka TV režimu:", "LabelTag": "Tag:", "LabelTypeMetadataDownloaders": "{0} stahovače metadat:", @@ -1399,7 +1379,7 @@ "LetterButtonAbbreviation": "A", "LinkApi": "API", "LinksValue": "Odkazy: {0}", - "LiveTV": "Živá TV", + "LiveTV": "Televize", "LiveTvFeatureDescription": "Streamujte televizní vysílání do libovolné aplikace Jellyfin s kompatibilním televizním tunerem instalovaným na serveru Jellyfin.", "Logo": "Logo", "ManageLibrary": "Spravovat knihovnu", @@ -1458,33 +1438,32 @@ "OptionProfileAudio": "Audio", "OptionProfileVideo": "Video", "OptionProfileVideoAudio": "Video Audio", - "OptionProtocolHls": "Živý HTTP stream", + "OptionProtocolHls": "Přímý přenos z internetu", "OptionProtocolHttp": "HTTP", "OptionRequirePerfectSubtitleMatchHelp": "Vyžadování dokonalé shody filtruje titulky tak, aby obsahovaly pouze ty, které byly testovány a ověřeny s vaším přesným videosouborem. Zrušení zaškrtnutí tohoto políčka zvýší pravděpodobnost stahování titulků, ale zvýší pravděpodobnost chybného nebo nesprávného textu titulků.", "PasswordResetProviderHelp": "Zvolte poskytovatele resetování hesla, který bude použit, když tento uživatel o něj požádá", "PlaybackSettings": "Nastavení přehrávání", "PlaybackSettingsIntro": "Chcete-li nastavit výchozí volby přehrávání, zastavte přehrávání videa a klepněte na ikonu uživatele v pravé horní části aplikace.", - "PluginInstalledMessage": "Plugin byl úspěšně nainstalován. Jellyfin server bude muset být restartován, aby se změny projevily.", + "PluginInstalledMessage": "Zásuvný modul byl úspěšně nainstalován. Server Jellyfin bude nutné restartovat, aby se změny projevily.", "PluginTabAppClassic": "Jellyfin pro Windows Media Center", "PreferEmbeddedTitlesOverFileNames": "Preferovat vložené názvy nad názvy souborů", "PreferEmbeddedTitlesOverFileNamesHelp": "Toto určuje výchozí název zobrazení, pokud nejsou k dispozici žádná metadata z internetu nebo místní metadata.", "PreferredNotRequired": "Preferováno, ale není vyžadováno", - "Raised": "Vyvolané", + "Raised": "Vystupující", "Rate": "Hodnocení", "RequiredForAllRemoteConnections": "Požadováno pro všechna vzdálená připojení", "SaveSubtitlesIntoMediaFolders": "Titulky ukládat do mediálních složek", "SaveSubtitlesIntoMediaFoldersHelp": "Ukládání titulků vedle video souborů umožní jejich snadnější správu.", "ScanLibrary": "Skenovat knihovnu", "SeriesDisplayOrderHelp": "Seřadit epizody podle data vysílání, pořadí DVD nebo absolutního číslování.", - "ServerRestartNeededAfterPluginInstall": "Jellyfin server bude nutné po instalaci pluginu restartovat.", + "ServerRestartNeededAfterPluginInstall": "Server Jellyfin bude nutné po instalaci zásuvného modulu restartovat.", "ShowAdvancedSettings": "Zobrazit rozšířená nastavení", - "ShowTitle": "Ukázat název", - "ShowYear": "Ukázat rok", + "ShowTitle": "Zobrazit název", + "ShowYear": "Zobrazit rok", "SimultaneousConnectionLimitHelp": "Maximální počet povolených současných streamů. Pro vypnutí omezení zadejte 0.", "SmallCaps": "Malá písmena", "Smaller": "Menší", "Sort": "Třídit", - "StatsForNerds": "Statistiky pro šprty", "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Tato nastavení platí také pro jakékoli přehrávání na Chromecastu spuštěné tímto zařízením.", "SubtitleAppearanceSettingsDisclaimer": "Tato nastavení se nevztahuje na grafické titulky (PGS, DVD atd.) nebo ASS/SSA titulky, které mají vlastní vložené styly.", "SubtitleDownloadersHelp": "Povolte a zařaďte preferované stahovače titulků v pořadí podle priority.", @@ -1493,16 +1472,16 @@ "TV": "TV", "TabDirectPlay": "Přímé přehrávání", "TabInfo": "Info", - "TabLiveTV": "Živá TV", + "TabLiveTV": "Televize", "TabMetadata": "Metadata", "TabPlaylist": "Playlist", "TabServer": "Server", "TagsValue": "Tagy: {0}", "ThemeSongs": "Tematická hudba", "ThemeVideos": "Tematická videa", - "Trailers": "Trailery", + "Trailers": "Upoutávky", "TvLibraryHelp": "Podívejte se na {0}průvodce pojmenováním TV pořadů{1}.", - "Uniform": "Uniformní", + "Uniform": "Obrys", "Unplayed": "Nepřehrané", "UserAgentHelp": "Zadejte vlastní HTTP hlavičku user agenta.", "ValueMinutes": "{0} min", @@ -1540,7 +1519,7 @@ "LabelPlayMethod": "Způsob přehrání:", "LabelPlayer": "Přehrávač:", "LabelFolder": "Složka:", - "LabelBaseUrlHelp": "Zde můžete přidat vlastní podsložku aby bylo možné přistupovat na server z unikátnější adresy.", + "LabelBaseUrlHelp": "Přidá vlastní řetězec na konec adresy serveru, např.: http://priklad.cz/<vlastni-retezec>", "LabelBaseUrl": "Výchozí URL:", "LabelBitrate": "Datový tok:", "LabelAudioSampleRate": "Vzorkovací frekvence zvuku:", @@ -1561,22 +1540,20 @@ "CopyStreamURLError": "Při kopírování URL došlo k chybě.", "LabelVideoResolution": "Rozlišení videa:", "LabelStreamType": "Typ streamu:", - "EnableFastImageFadeInHelp": "Povolte rychlejší animaci pro načtené obrázky", - "EnableFastImageFadeIn": "Rychlé zmizení obrazu", "LabelPlayerDimensions": "Zobrazené rozlišení:", "LabelDroppedFrames": "Vynechané snímky:", "LabelCorruptedFrames": "Poškozené snímky:", - "OptionForceRemoteSourceTranscoding": "Vynutit transkódování vzdálených zdrojů médií (jako např. živá TV)", + "OptionForceRemoteSourceTranscoding": "Vynutit překódování vzdálených zdrojů médií (např.: televizního vysílání)", "NoCreatedLibraries": "Zdá se, že jste dosud nevytvořili žádnou knihovnu. {0}Chtěli byste nějakou vytvořit nyní?{1}", "AskAdminToCreateLibrary": "Požádejte administrátora o vytvoření knihovny.", - "AllowFfmpegThrottlingHelp": "Když se překódování nebo remux dostane dostatečně daleko dopředu od aktuální pozice přehrávání, pozastaví se proces, aby spotřeboval méně zdrojů. To je nejužitečnější při sledování bez častého vyhledávání. Pokud máte problémy s přehráváním, vypněte tuto funkci.", + "AllowFfmpegThrottlingHelp": "Pozastaví proces překódování či remuxování, pokud je dostatečně napřed, aby se spotřebovalo méně zdrojů. Nejvhodnější, pokud příliš nepřeskakujete. Pokud máte problémy s přehráváním, tuto funkci vypněte.", "AllowFfmpegThrottling": "Omezit překódování", "BoxSet": "Sbírka", "Track": "Stopa", "Season": "Sezóna", "ReleaseGroup": "Vydavatel", - "PreferEmbeddedEpisodeInfosOverFileNames": "Preferovat vložené informace o epizodě před názvy souborů", - "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Používá se informace o epizodě z vložených metadat, pokud jsou k dispozici.", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferovat vloženou informaci o epizodě před názvem souboru", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Používat informaci o epizodě z vložených metadat, pokud jsou k dispozici.", "Person": "Osoba", "OtherArtist": "Ostatní interpreti", "Movie": "Film", @@ -1593,6 +1570,68 @@ "DailyAt": "Denně v {0}", "PersonRole": "jako {0}", "ListPaging": "{0}-{1} ze {2}", - "WriteAccessRequired": "Jellyfin Server potřebuje oprávnění pro zápis v této složce. Zkontrolujte oprávnění a zkuste to znovu.", - "PathNotFound": "Cesta nebyla nalezena. Zkontrolujte, zda je platná a zkuste to znovu." + "WriteAccessRequired": "Server Jellyfin vyžaduje oprávnění pro zápis do této složky. Zkontrolujte oprávnění a zkuste to znovu.", + "PathNotFound": "Cesta nebyla nalezena. Zkontrolujte, zda je platná a zkuste to znovu.", + "WeeklyAt": "V {0} v {1}", + "LastSeen": "Naposledy zobrazené {0}", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "LabelLibraryPageSizeHelp": "Určuje počet položek k zobrazení na stránce knihovny. Nastavte na 0 pro vypnutí stránkování.", + "LabelLibraryPageSize": "Velikost stránky knihovny:", + "LabelDeinterlaceMethod": "Metoda odstranění prokládání:", + "DeinterlaceMethodHelp": "Vyberte metodu odstranění prokládání obrazu při překódování obsahu.", + "UnsupportedPlayback": "Jellyfin nedokáže dešifrovat obsah chráněný Správou digitálních práv (DRM), ale pokusí se zobrazit veškerý obsah, včetně toho chráněného. Některé soubory se nemusí vůbec zobrazit kvůli šifrování nebo jiným nepodporovaným funkcím, např.: interaktivním názvům.", + "MessageUnauthorizedUser": "Momentálně nemáte oprávnění k přístupu na server. Další informace získáte od správce serveru.", + "Filter": "Filtr", + "New": "Nové", + "ButtonTogglePlaylist": "Playlist", + "ButtonToggleContextMenu": "Více", + "LabelNightly": "Nightly", + "LabelStable": "Stabilní", + "LabelChromecastVersion": "Verze Chromecastu", + "ApiKeysCaption": "Seznam povolených API klíčů", + "LabelEnableHttpsHelp": "Umožní serveru naslouchat na určeném portu HTTPS. K fungování je nutné nakonfigurovat i platný certifikát.", + "LabelEnableHttps": "Povolit HTTPS", + "HeaderServerAddressSettings": "Nastavení adresy serveru", + "HeaderRemoteAccessSettings": "Nastavení vzdáleného přístupu", + "HeaderHttpsSettings": "Nastavení HTTPS", + "LabelRequireHttpsHelp": "Server automaticky přesměruje všechny požadavky z HTTP na HTTPS. Pokud server nenaslouchá na portu HTTPS, tato funkce nemá žádný účinek.", + "LabelRequireHttps": "Vyžadovat HTTPS", + "TabDVR": "Nahrávání", + "HeaderDVR": "Nahrávání", + "SaveChanges": "Uložit změny", + "LabelSyncPlayPlaybackDiff": "Rozdíl v době přehrávání:", + "SyncPlayAccessHelp": "Určuje úroveň přístupu k synchronizaci přehrávání, kterou tento uživatel bude mít. Tato funkce umožňuje synchronizovat přehrávání s dalšími zařízeními.", + "MessageSyncPlayErrorMedia": "Zapnutí synchronizace přehrávání se nezdařilo. Chyba média.", + "MessageSyncPlayErrorMissingSession": "Zapnutí synchronizace přehrávání se nezdařilo. Nebyla nalezena relace.", + "MessageSyncPlayErrorNoActivePlayer": "Nebyl nalezen žádný aktivní přehrávač. Synchronizace přehrávání byla vypnuta.", + "MessageSyncPlayErrorAccessingGroups": "Při načítání seznamu skupin došlo k chybě.", + "MessageSyncPlayLibraryAccessDenied": "Přístup k tomuto obsahu je omezen.", + "MessageSyncPlayJoinGroupDenied": "K použití synchronizace přehrávání je vyžadováno povolení.", + "MessageSyncPlayCreateGroupDenied": "K vytvoření skupiny je vyžadováno povolení.", + "MessageSyncPlayGroupDoesNotExist": "Připojení ke skupině se nezdařilo, protože skupina neexistuje.", + "MessageSyncPlayPlaybackPermissionRequired": "K přehrávání je vyžadováno povolení.", + "MessageSyncPlayNoGroupsAvailable": "Neexistují žádné skupiny. Začněte něco přehrávat.", + "MessageSyncPlayGroupWait": "Přehrávání uživatele {0} se načítá…", + "MessageSyncPlayUserLeft": "Uživatel {0} opustil skupinu.", + "MessageSyncPlayUserJoined": "Uživatel {0} se připojil do skupiny.", + "MessageSyncPlayDisabled": "Synchronizace přehrávání zakázána.", + "MessageSyncPlayEnabled": "Synchronizace přehrávání povolena.", + "LabelSyncPlayAccess": "Přístup k funkci synchronizace přehrávání", + "LabelSyncPlayAccessNone": "Zakázáno pro tohoto uživatele", + "LabelSyncPlayAccessJoinGroups": "Povolit uživateli připojovat se do skupin", + "LabelSyncPlayAccessCreateAndJoinGroups": "Povolit uživateli vytvářet a připojovat se do skupin", + "LabelSyncPlayLeaveGroupDescription": "Zakázat synchronizaci přehrávání", + "LabelSyncPlayLeaveGroup": "Opustit skupinu", + "LabelSyncPlayNewGroupDescription": "Vytvořit skupinu", + "LabelSyncPlayNewGroup": "Nová skupina", + "LabelSyncPlaySyncMethod": "Způsob synchronizace:", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Časový rozdíl mezi serverem:", + "HeaderSyncPlayEnabled": "Synchronizace přehrávání povolena", + "HeaderSyncPlaySelectGroup": "Připojit ke skupině", + "EnableDetailsBannerHelp": "Zobrazí obrázek ve vrchní části detailu položky.", + "EnableDetailsBanner": "Obrázek detailu", + "ShowMore": "Zobrazit více", + "ShowLess": "Zobrazit méně" } diff --git a/src/strings/da.json b/src/strings/da.json index 22ddc52200..5cc1f4ab56 100644 --- a/src/strings/da.json +++ b/src/strings/da.json @@ -12,11 +12,11 @@ "AllChannels": "Alle kanaler", "AllEpisodes": "Alle episoder", "AllLibraries": "Alle biblioteker", - "AllowHWTranscodingHelp": "Hvis aktiveret, omkoder tuneren streams on-the-fly. Dette kan hjælpe med at reducere omkodning der kræves af Jellyfin Server.", + "AllowHWTranscodingHelp": "Tillader tuneren at transkode streams \"on the fly\". Dette kan hjælpe med at reducere transkodningen, der kræves af serveren.", "AllowMediaConversion": "Tillad media konvertering", "AllowMediaConversionHelp": "Giv eller nægt adgang til Konvertér Media featuren.", "AllowOnTheFlySubtitleExtraction": "Tillad udtræk af undertekster on-the-fly", - "AllowOnTheFlySubtitleExtractionHelp": "Indeholdte undertekster kan trækkes ud af videoer og leveres til Jellyfin apps i ren tekst for at afhjælpe video kodning. På nogle systemer kan dette tage lang tid og forårsage at afspilning kan hænge mens den udtrækker. Slå dette fra, for at have undertekster brændt ind i video kodningen når det er supporteret på klient enheden.", + "AllowOnTheFlySubtitleExtractionHelp": "Indeholdte undertekster kan trækkes ud af videoer og leveres til klienter i ren tekst for at afhjælpe video omkodning. På nogle systemer kan dette tage lang tid og forårsage at afspilning kan hænge mens den udtrækker. Slå dette fra, for at have undertekster brændt ind i vha. video omkodningen når der ikke er indbygget understøttelse for ren tekst undertekster i klient enheden.", "AllowRemoteAccess": "Tillad fjernadgang til denne Jellyfin Server.", "AllowRemoteAccessHelp": "Hvis ikke markeret, vil alle fjernforbindelser blive blokeret.", "AllowedRemoteAddressesHelp": "Komma seperareret liste over IP adresser og netmasker der har ret til fjernadgang. Hvis blank er alle adresser tilladte.", @@ -29,7 +29,7 @@ "BirthDateValue": "Født: {0}", "BirthLocation": "Fødselslokation", "BirthPlaceValue": "Fødselssted: {0}", - "BookLibraryHelp": "Lyd- og tekstbøger er understøttet. Se {0}Jellyfins guide til navngivning af bøger{1}.", + "BookLibraryHelp": "Lyd- og tekstbøger er understøttet. Se {0}guiden til navngivning af bøger{1}.", "Browse": "Gennemse", "BrowsePluginCatalogMessage": "Gennemse vores plugin-katalog for at se tilgængelige plugins.", "ButtonAdd": "Tilføj", @@ -53,7 +53,7 @@ "ButtonEdit": "Rediger", "ButtonEditImages": "Rediger billeder", "ButtonEditOtherUserPreferences": "Rediger denne brugers profil, billede og personlige indstillinger.", - "ButtonForgotPassword": "Glemt adgangskode", + "ButtonForgotPassword": "Glemt Adgangskode", "ButtonFullscreen": "Fuld skærm", "ButtonGotIt": "Forstået", "ButtonHelp": "Hjælp", @@ -136,30 +136,30 @@ "DoNotRecord": "Optag ikke", "Download": "Hent", "DrmChannelsNotImported": "Kanaler med DRM importeres ikke.", - "EasyPasswordHelp": "Din pinkode bruges til offline adgang til understøttede Jellyfin apps, og kan også bruges til nemt login inden for eget netværk.", + "EasyPasswordHelp": "Din nemme pin-kode bruges til offline adgang på understøttede klienter og kan også bruges til nem login på netværket.", "Edit": "Rediger", "EditImages": "Rediger billeder", "EditSubtitles": "Rediger undertekster", - "EnableCinemaMode": "Aktiver biograftilstand", - "EnableColorCodedBackgrounds": "Aktiver farvekodet baggrunde", + "EnableCinemaMode": "Aktiver Biograftilstand", + "EnableColorCodedBackgrounds": "Aktiver Farvekodet baggrunde", "EnableHardwareEncoding": "Aktiver hardware indkoding", - "EnablePhotos": "Aktiver fotos", - "EnablePhotosHelp": "Fotos bliver opdaget og vist sammen med andre mediefiler.", + "EnablePhotos": "Vis fotoer", + "EnablePhotosHelp": "Billeder registreres og vises sammen med andre mediefiler.", "EnableStreamLooping": "Auto-gentag live streams", "EnableStreamLoopingHelp": "Aktiver dette hvis live streams kun indeholder få sekunders data og skal efterspørgsel hele tiden. Aktivering af dette uden det er nødvendigt kan forårsage problemer.", "Ended": "Færdig", "EndsAtValue": "Slutter {0}", - "ErrorAddingListingsToSchedulesDirect": "Der opstod en fejl under tilføjelse af opstilling til din Schedules Direct-konto. Schedules Direct tillader kun et begrænset antal opstillinger pr. konto. Du bliver muligvis nød til at logge på Schedules Direct-hjemmesiden og fjerne andre lister fra din konto for at fortsætte.", + "ErrorAddingListingsToSchedulesDirect": "Der opstod en fejl ved tilføjelse af lineup til din Schedules Direct-konto. Schedules Direct tillader kun et begrænset antal opstillinger pr. Konto. Det kan være nødvendigt at du logger ind på Schedules Direct-webstedet og fjerner andre lister fra din konto, før du fortsætter.", "ErrorAddingMediaPathToVirtualFolder": "Der opstod en fejl under tilføjelse af mediesti. Kontroller venligst at stien er gyldig og at Jellyfin Server-processen har adgang til denne lokation.", "ErrorAddingTunerDevice": "Der opstod en fejl under tilføjelse af tuner-enhed. Kontroller venligst at den er tilgængelig og prøv igen.", - "ErrorAddingXmlTvFile": "Der opstod en fejl under tilgang til XmlTV-filen. Kontroller venligst at filen findes og prøv igen.", - "ErrorGettingTvLineups": "Der opstod en fejl under download af tv-opstillinger. Kontroller venligst at dine informationer er korrekte og prøv igen.", + "ErrorAddingXmlTvFile": "Der opstod en fejl under tilgang til XMLTV-filen. Kontroller venligst at filen findes og prøv igen.", + "ErrorGettingTvLineups": "Der opstod en fejl under download af TV-opstillinger. Kontroller venligst at dine informationer er korrekte og prøv igen.", "ErrorMessageStartHourGreaterThanEnd": "Slut tid skal være større end start tid.", "ErrorPleaseSelectLineup": "Vælg venligst en opstilling og prøv igen. Hvis ingen opstillinger er tilgængelige, så kontroller venligst at dit brugernavn, adgangskode og postnummer er korrekt.", "ErrorSavingTvProvider": "Der opstod en fejl i forsøget på at gemme udbyder. Kontroller venligst at den er tilgængelig og prøv igen.", "EveryNDays": "Hver {0} dage", "ExitFullscreen": "Afslut fuldskærm", - "ExtractChapterImagesHelp": "Udvinding af kapitelbilleder lader Jellyfin apps vise grafiske menuer for scenevalg. Processen kan være langsom, cpu-intensiv og kræver muligvis adskillige gigabytes af lagerplads. Den køres når videoer opdages og desuden som en planlagt opgave, natligt. Den planlagte opgave kan konfigureres i området for planlagte opgaver. Det anbefales ikke at køre denne opgave under timer med spidsbelastning.", + "ExtractChapterImagesHelp": "Udtrækning af kapitelbilleder giver klienter mulighed for at vise grafiske scenevalgmenuer. Processen kan være langsom, ressourcekrævende og kan kræve flere gigabyte plads. Det kører, når videoer opdages, og også som en planlagt nat opgave. Skemaet kan konfigureres i det planlagte opgaverområde. Det anbefales ikke at køre denne opgave i de mest brugte timer.", "FFmpegSavePathNotFound": "Vi er ikke i stand til at finde FFmpeg via stien du har angivet. FFprobe er også påkrævet og skal findes i samme mappe. Disse komponenter er som regel pakket i den samme download. Kontroller venligst stien og prøv igen.", "FastForward": "Spol fremad", "Favorite": "Favorit", @@ -170,9 +170,9 @@ "FolderTypeBooks": "Bøger", "FolderTypeMovies": "Film", "FolderTypeMusic": "Musik", - "FolderTypeMusicVideos": "Musikvideoer", + "FolderTypeMusicVideos": "Musik Videoer", "FolderTypeTvShows": "TV", - "FolderTypeUnset": "Ikke valgt (blandet indhold)", + "FolderTypeUnset": "Blandet Indhold", "Friday": "Fredag", "Fullscreen": "Fuldskærm", "General": "Generel", @@ -183,7 +183,7 @@ "H264CrfHelp": "Den Konstante Ratefaktor (CRF) er standardindstillingen for X264-koderen. Du kan sætte værdien i mellem 0 og 51, hvor de lavere værdier resulterer i bedre kvalitet (på bekostning af større filstørrelser). Fornuftige værdier er i mellem 18 og 28. Standarden for X264 er 23, så du kan bruge dette som udgangspunkt.", "EncoderPresetHelp": "Vælg en hurtigere værdi for at forbedre ydeevne, eller en langsommere værdi for at forbedre kvalitet.", "HDPrograms": "HD-programmer", - "HardwareAccelerationWarning": "Aktivering af hardwareacceleration kan forårsage ustabilitet i nogle miljøer. Kontroller at dit operativsystem og videodriver er ajourført. Hvis du har problemer med at afspille video efter aktivering af dette, bliver du nød til at skifte tilbage til Auto.", + "HardwareAccelerationWarning": "Aktivering af hardwareacceleration kan forårsage ustabilitet i nogle miljøer. Kontroller at dit operativsystem og videodriver er opdateret. Hvis du har problemer med at afspille video efter aktivering af dette, bliver du nød til at skifte tilbage til Ingen.", "HeaderAccessSchedule": "Adgangsskema", "HeaderAccessScheduleHelp": "Skab et adgangsskema for at begrænse adgangen til bestemte tidsrum.", "HeaderActiveDevices": "Aktive enheder", @@ -197,9 +197,9 @@ "HeaderAdditionalParts": "Andre stier", "HeaderAlert": "Advarsel", "HeaderAllowMediaDeletionFrom": "Tillad Media Sletning Fra", - "HeaderApiKey": "API nøgle", - "HeaderApiKeys": "API nøgler", - "HeaderApiKeysHelp": "Eksterne applikationer skal have en API nøgle for at kunne kommunikere med Jellyfin. Nøgler udstedes ved at logge ind med en Jellyfin konto, eller ved manuelt at tildele applikationen en nøgle.", + "HeaderApiKey": "API Nøgle", + "HeaderApiKeys": "API Nøgler", + "HeaderApiKeysHelp": "Eksterne applikationer skal have en API-nøgle for at kunne kommunikere med Jellyfin. Nøgler udstedes ved at logge ind med en Jellyfin konto, eller ved manuelt at tildele applikationen en nøgle.", "HeaderAudioSettings": "Lydindstillinger", "HeaderAutomaticUpdates": "Automatiske opdateringer", "HeaderBlockItemsWithNoRating": "Klokér titler uden eller med ukendt bedømmelses information:", @@ -266,7 +266,7 @@ "HeaderItems": "Element", "HeaderKeepRecording": "Bevar Optagelse", "HeaderKeepSeries": "Bevar Serie", - "HeaderKodiMetadataHelp": "For at aktivere NFO metadata, rediger et bibliotek i Jellyfin biblioteks redigering og find metadata gemmer sektionen.", + "HeaderKodiMetadataHelp": "For at aktivere eller deaktivere NFO-metadata skal du redigere et bibliotek i Jellyfin-biblioteksopsætningen og finde afsnittet om metadata.", "HeaderLatestEpisodes": "Sidste episoder", "HeaderLatestMedia": "Seneste medier", "HeaderLatestMovies": "Seneste film", @@ -287,7 +287,7 @@ "HeaderMusicVideos": "Musikvideoer", "HeaderMyDevice": "Min Enhed", "HeaderMyMedia": "Mine medier", - "HeaderNewApiKey": "Ny API nøgle", + "HeaderNewApiKey": "Ny API Nøgle", "HeaderNewDevices": "Nye Enheder", "HeaderNextUp": "Næste", "HeaderOnNow": "Vises Nu", @@ -373,7 +373,7 @@ "Images": "Billeder", "ImportFavoriteChannelsHelp": "Hvis aktiveret, importeres der udelukkende kanaler der er markeret som favoritter på tuner-enheden.", "ImportMissingEpisodesHelp": "hvis aktiveret, vil information omkring manglende episoder bliver importeret ind i din Jellyfin-database og blive vist i sæsoner og serier. Dette medfører muligvis længere biblioteksscanninger.", - "InstallingPackage": "Installerer {0}", + "InstallingPackage": "Installerer {0} (version {1})", "InstantMix": "Instant Mix", "ItemCount": "{0} elementer", "Items": "emner", @@ -397,13 +397,13 @@ "LabelAll": "Alle", "LabelAllowHWTranscoding": "Tillad hardware-omkodning", "LabelAllowServerAutoRestart": "Tillad serveren at genstarte automatisk for at påføre opdateringer", - "LabelAllowServerAutoRestartHelp": "Serveren vil kun genstarte i inaktive perioder, når ingen brugere er aktive", + "LabelAllowServerAutoRestartHelp": "Serveren vil kun genstarte i inaktive perioder, når ingen brugere er aktive.", "LabelAllowedRemoteAddresses": "Fjernadgang IP adresse filter:", "LabelAllowedRemoteAddressesMode": "Fjernadgang IP adresse filter mode:", "LabelAppName": "App navn", - "LabelAppNameExample": "F. eks: Sickbeard, NzbDrone", + "LabelAppNameExample": "F. eks: Sickbeard, Sonarr", "LabelArtists": "Artister:", - "LabelArtistsHelp": "Angiv flere ved at sætte mellem dem ;", + "LabelArtistsHelp": "Angiv flere ved at sætte ;", "LabelAudioLanguagePreference": "Foretrukket lydsprog:", "LabelAutomaticallyRefreshInternetMetadataEvery": "Genopfrisk automatisk metadata fra internettet:", "LabelBindToLocalNetworkAddress": "Bind til lokal netværksadresse:", @@ -411,9 +411,9 @@ "LabelBirthDate": "Fødselsdato:", "LabelBirthYear": "Fødselsår:", "LabelBlastMessageInterval": "Interval mellem 'i live' beskeder (sekunder)", - "LabelBlastMessageIntervalHelp": "Angiver intervallet i sekunder mellem serverens 'i live' beskeder.", + "LabelBlastMessageIntervalHelp": "Bestemmer varigheden i sekunder mellem eksplosive levende meddelelser.", "LabelCachePath": "Cachesti:", - "LabelCachePathHelp": "Angiv en brugerdefineret lokation for server cachefiler, så som billeder. Efterlad blankt for at benytte serverens standard.", + "LabelCachePathHelp": "Angiv en brugerdefineret placering for servercache-filer, såsom billeder. Lad være tom for at bruge serverens standard.", "LabelCancelled": "Annulleret", "LabelChannels": "Kanaler:", "LabelCollection": "Samling:", @@ -425,8 +425,8 @@ "LabelCustomCertificatePath": "Brugerdefineret SSL certifikat sti:", "LabelCustomCertificatePathHelp": "Sti til PKCS #12 fil indeholdende et certifikat og privat nøgle for at aktivere TLS understøttelse på et brugerdefineret domæne.", "LabelCustomCss": "Brugerdefineret CSS:", - "LabelCustomCssHelp": "Anvend din egen css til webinterfacet.", - "LabelCustomDeviceDisplayName": "Vist navn:", + "LabelCustomCssHelp": "Anvend din egen stil til webinterfacet.", + "LabelCustomDeviceDisplayName": "Visningsnavn:", "LabelCustomDeviceDisplayNameHelp": "Angiv en brugerdefineret navn. hvis der ikke angives et navn, bruges det navn enheden sender.", "LabelCustomRating": "Brugerdefineret bedømmelse:", "LabelDateAdded": "Dato for tilføjelse:", @@ -444,24 +444,24 @@ "LabelDisplayOrder": "Visningsorden:", "LabelDisplaySpecialsWithinSeasons": "Vis specialepisoder sammen med den sæson de blev sent i", "LabelDownMixAudioScale": "Forøg lydstyrke ved nedmiksning:", - "LabelDownMixAudioScaleHelp": "Forøg lydstyrken når der nedmikses. Sæt værdien til 1 for at beholde den originale lydstyrke.", + "LabelDownMixAudioScaleHelp": "Forøg lydstyrken når der nedmikses. Sæt værdien til en, for at beholde den originale lydstyrke.", "LabelDownloadLanguages": "Hent sprog:", - "LabelDropImageHere": "Smid billede her.", + "LabelDropImageHere": "Drop billede her, eller Tryk for at vælge.", "LabelEasyPinCode": "Pinkode:", "LabelEmbedAlbumArtDidl": "Indsæt album billede i DIDL", "LabelEmbedAlbumArtDidlHelp": "Nogle enheder foretrækker denne metode til overførsel af album billede. Andre kan fejle når dette er aktiveret.", "LabelEnableAutomaticPortMap": "Aktiver automatisk port mapping", - "LabelEnableAutomaticPortMapHelp": "Forsøg at mappe den offentlige port til den lokale port med uPnP. Dette virker ikke med alle routere.", + "LabelEnableAutomaticPortMapHelp": "Forsøg at mappe den offentlige port til den lokale port med uPnP. Dette virker ikke med alle routere. Ændringerne vil først træde i kræft et en server genstart.", "LabelEnableBlastAliveMessages": "Masseudsend 'i live' beskeder", "LabelEnableBlastAliveMessagesHelp": "Aktiver dette hvis UPnP enheder har problemer med forbindelsen til serveren.", "LabelEnableDlnaClientDiscoveryInterval": "Interval for klientsøgning (sekunder)", "LabelEnableDlnaClientDiscoveryIntervalHelp": "angiver intervallet i sekunder mellem Jellyfins SSDP søgninger.", "LabelEnableDlnaDebugLogging": "Aktiver debu logning af DLNA", - "LabelEnableDlnaDebugLoggingHelp": "Dette generer meget store logfiler, og er kun anbefalet at bruge til fejlfindingsformål.", + "LabelEnableDlnaDebugLoggingHelp": "Opret store logfiler og skal kun bruges efter behov til fejlfinding.", "LabelEnableDlnaPlayTo": "Aktiver DLNA \"Afspil Til\"", - "LabelEnableDlnaPlayToHelp": "Jellyfin kan finde enheder i dit netværk og tilbyde at kontrollere dem.", - "LabelEnableDlnaServer": "Aktiver DNLA server", - "LabelEnableDlnaServerHelp": "Tillader UPnP enheder i dit netværk at gennemse og afspille Jellyfins indhold.", + "LabelEnableDlnaPlayToHelp": "Find enheder i dit netværk og tilbyd at fjernstyre dem.", + "LabelEnableDlnaServer": "Aktiver DLNA server", + "LabelEnableDlnaServerHelp": "Tillader UPnP enheder i dit netværk at gennemse og afspille indhold.", "LabelEnableRealtimeMonitor": "Aktiver realtidsovervågning", "LabelEnableRealtimeMonitorHelp": "Ændringer vil blive behandlet øjeblikkeligt på understøttede filsystemer.", "LabelEnableSingleImageInDidlLimit": "Begræns til et enkelt indlejret billede", @@ -471,56 +471,56 @@ "LabelEvent": "Hændelse:", "LabelEveryXMinutes": "Hver:", "LabelExtractChaptersDuringLibraryScan": "Udtræk kapitelbilleder under biblioteksskanning", - "LabelExtractChaptersDuringLibraryScanHelp": "Aktiver dette for at udtrække kapitelbillleder mens videofiler bliver importeret under biblioteksskanningen. Hvi det ikke er aktiveret, bliver de udtrukket når den planlagte opgave kapitelbilleder kører, og lader den almindelige biblioteksskanning afslutte hurtigere.", + "LabelExtractChaptersDuringLibraryScanHelp": "Generere kapitelbillleder mens videofiler bliver importeret under biblioteksskanningen. Alternativt bliver de udtrukket når den planlagte opgave kapitelbilleder kører, hvilket tillader den almindelige biblioteksskanning at afslutte hurtigere.", "LabelFailed": "Fejlet", - "LabelFileOrUrl": "Fil eller url:", + "LabelFileOrUrl": "Fil eller URL:", "LabelFinish": "Afslut", "LabelForgotPasswordUsernameHelp": "Indtast dit brugernavn, hvis du kan huske det.", "LabelFriendlyName": "System venligt navn:", - "LabelServerNameHelp": "Dette navn bruges til at identificere serveren. Hvis det ikke udfyldes, bruges computerens navn.", + "LabelServerNameHelp": "Dette navn bruges til at identificere serveren. Som udgangspunkt anvendes computerens navn.", "LabelGroupMoviesIntoCollections": "Grupper film i samlinger", "LabelGroupMoviesIntoCollectionsHelp": "Film i samlinger vil blive vist som en samlet enhed i filmlister.", "LabelH264Crf": "H264-kodning CRF:", "LabelEncoderPreset": "Forudindstillet H264-kodning:", "LabelHardwareAccelerationType": "Hardwareacceleration:", - "LabelHardwareAccelerationTypeHelp": "Kun tilgængelig for understøttede systemer.", + "LabelHardwareAccelerationTypeHelp": "Hardwareacceleration kræver yderligere konfiguration.", "LabelHttpsPort": "Lokalt HTTPS portnummer:", - "LabelHttpsPortHelp": "Det portnummer Jellyfins https-server bruger.", - "LabelIconMaxHeight": "Max højde på ikoner:", - "LabelIconMaxHeightHelp": "Maksimumopløsningen på ikoner der bliver vist med upnp:icon", - "LabelIconMaxWidth": "Max bredde på ikoner:", - "LabelIconMaxWidthHelp": "Maksimumopløsningen på ikoner der bliver vist med upnp:icon", + "LabelHttpsPortHelp": "Det TCP-portnummer, som Jellyfins HTTPS-server skal benytte.", + "LabelIconMaxHeight": "Maximal højde af ikoner:", + "LabelIconMaxHeightHelp": "Maksimalopløsningen af ikoner, der bliver vist med upnp:icon.", + "LabelIconMaxWidth": "Maximal bredde på ikoner:", + "LabelIconMaxWidthHelp": "Maksimalopløsningen på ikoner der bliver vist med upnp:icon.", "LabelIdentificationFieldHelp": "En case-insensitive substring eller regex ekspression.", "LabelImageFetchersHelp": "Aktiver og ranger dine fortrukne billede-hentere i en prioriteret rækkefølge.", "LabelImageType": "Billedtype:", "LabelImportOnlyFavoriteChannels": "Begræns til kanaler der er markeret som favoritter", "LabelInNetworkSignInWithEasyPassword": "Tillad login inden for eget netværk med pinkode", - "LabelInNetworkSignInWithEasyPasswordHelp": "Aktiver dette for at loge ind i Jellyfin apps med din pinkode inden for dit eget netværk. Din almindelige adgangskode skal du så kun bruge når du ikke er hjemme. Hvis pinkoden er tom, kan du logge ind uden adgangskode inden for dit eget netværk.", + "LabelInNetworkSignInWithEasyPasswordHelp": "Brug den lette pinkode til at logge ind på klienter i dit lokale netværk. Din almindelige adgangskode er kun nødvendig hjemmefra. Hvis pinkoden efterlades tom, behøver du ikke en adgangskode på dit hjemmenetværk.", "LabelKeepUpTo": "Bevar op til:", "LabelKidsCategories": "Børnekategorier:", "LabelKodiMetadataDateFormat": "Format for udgivelsesdato:", - "LabelKodiMetadataDateFormatHelp": "Alle datoer i NFO-filer vil blive læst og skrevet med dette format.", - "LabelKodiMetadataEnableExtraThumbs": "kopier extrafanart til extrathumbs", + "LabelKodiMetadataDateFormatHelp": "Alle datoer i NFO-filer vil blive analyseret med dette format.", + "LabelKodiMetadataEnableExtraThumbs": "Kopier ekstra fanart til extrathumbs", "LabelKodiMetadataEnableExtraThumbsHelp": "Ved hentning af billeder, kan de gemmes i både extrafanart og extrathumbs. Dette giver maksimal Kodi skin kompatibilitet.", "LabelKodiMetadataEnablePathSubstitution": "Aktiver stisubstitution", "LabelKodiMetadataEnablePathSubstitutionHelp": "Aktiverer stisubstitution for billedstier med serverens stisubstitutionsindstillinger.", "LabelKodiMetadataSaveImagePaths": "Gem stier til billeder i Nfo-filer", "LabelKodiMetadataSaveImagePathsHelp": "Dette er anbefalet hvis du har billedfiler med navne der ikke lever op til Kodis retningslinjer.", - "LabelKodiMetadataUser": "Gem brugers set data til NFO'er for:", - "LabelKodiMetadataUserHelp": "Aktiver dette for at komme set data til NFO filer som andre programmer kan bruge.", + "LabelKodiMetadataUser": "Gem brugerdata til NFO-filer til:", + "LabelKodiMetadataUserHelp": "Gem overvågningsdata til NFO-filer til andre applikationer.", "LabelLanNetworks": "LAN netwærk:", "LabelLanguage": "Sprog:", "LabelLineup": "Opstilling:", - "LabelLocalHttpServerPortNumber": "Lokalt http portnummer:", - "LabelLocalHttpServerPortNumberHelp": "Det portnummer Jellyfins http-server bruger.", + "LabelLocalHttpServerPortNumber": "Lokalt HTTP-portnummer:", + "LabelLocalHttpServerPortNumberHelp": "Det TCP-portnummer, som Jellyfin's HTTP-server skal binde til.", "LabelLockItemToPreventChanges": "Lås for at undgå fremtidige ændringer", "LabelLoginDisclaimer": "Login ansvarsfraskrivelse:", - "LabelLoginDisclaimerHelp": "Dette bliver vist i bunden af loginsiden.", - "LabelManufacturer": "Producent", - "LabelManufacturerUrl": "Producent url", + "LabelLoginDisclaimerHelp": "En besked, som vises i bunden af loginsiden.", + "LabelManufacturer": "Producent:", + "LabelManufacturerUrl": "Producentens URL", "LabelMaxBackdropsPerItem": "Maksimum antal af bagtæpper per element:", "LabelMaxParentalRating": "Højst tilladte aldersgrænse:", - "LabelMaxResumePercentage": "Maks. fortsæt procentdel:", + "LabelMaxResumePercentage": "Maksimal fortsæt procentdel:", "LabelMaxResumePercentageHelp": "Medier anses som fuldt afspillet, hvis de stoppes efter denne tid.", "LabelMaxScreenshotsPerItem": "Maksimum antal af skærmbilleder per element:", "LabelMaxStreamingBitrate": "Maks. streaming kvalitet:", @@ -538,9 +538,9 @@ "LabelMethod": "Metode:", "LabelMinBackdropDownloadWidth": "Minimum baggrundsbillede bredde:", "LabelMinResumeDuration": "Min. fortsæt tidsrum (sekunder):", - "LabelMinResumeDurationHelp": "Medier med kortere afspilningstid en denne kan ikke fortsættes.", - "LabelMinResumePercentage": "Min. fortsæt procentdel:", - "LabelMinResumePercentageHelp": "Medier anses om ikke afspillet, hvis de stoppes inden denne tid.", + "LabelMinResumeDurationHelp": "Den korteste videolængde i sekunder, der gemmer afspilningsplacering og giver dig mulighed for at genoptage.", + "LabelMinResumePercentage": "Minimal fortsæt procentdel:", + "LabelMinResumePercentageHelp": "Medier anses som ikke afspillede, hvis de stoppes inden denne tid.", "LabelMinScreenshotDownloadWidth": "Minimum skærmbillede bredde:", "LabelModelDescription": "Modelbeskrivelse", "LabelModelName": "Modelnavn", @@ -548,10 +548,10 @@ "LabelMonitorUsers": "Overvåg aktivitet fra:", "LabelMovieCategories": "Filmkategorier:", "LabelMoviePrefix": "Film-præfiks:", - "LabelMoviePrefixHelp": "Angiv venligst her hvis der tilføjes et præfiks til filmtitler, så Jellyfin kan håndtere det korrekt.", + "LabelMoviePrefixHelp": "Angiv venligst her hvis der tilføjes et præfiks til filmtitler, så serveren kan håndtere det korrekt.", "LabelMovieRecordingPath": "Film afspilningssti (valgfri):", "LabelMusicStreamingTranscodingBitrate": "Bitrate for musiktranskodning:", - "LabelMusicStreamingTranscodingBitrateHelp": "Angiv en maksimal bitrate når der streames musik", + "LabelMusicStreamingTranscodingBitrateHelp": "Angiv en maksimal bitrate når der streames musik.", "LabelName": "Navn:", "LabelNewName": "Nyt navn:", "LabelNewPassword": "Ny kode:", @@ -590,10 +590,10 @@ "LabelProtocol": "Protokol:", "LabelProtocolInfo": "Protokolinformation:", "LabelProtocolInfoHelp": "Den værdi der bruges til svar på GetProtocolInfo-forespørgsler fra enheden.", - "LabelPublicHttpPort": "Offentligt http portnummer:", - "LabelPublicHttpPortHelp": "Det offentlige portnummer som bliver knyttet til det lokale http portnummer.", - "LabelPublicHttpsPort": "Offentligt https portnummer:", - "LabelPublicHttpsPortHelp": "Det offentlige portnummer som bliver knyttet til det lokale https portnummer.", + "LabelPublicHttpPort": "Offentligt HTTP-portnummer:", + "LabelPublicHttpPortHelp": "Det offentlige portnummer som bliver knyttet til det lokale HTTP-portnummer.", + "LabelPublicHttpsPort": "Offentligt HTTPS portnummer:", + "LabelPublicHttpsPortHelp": "Det offentlige portnummer som bliver knyttet til det lokale HTTPS portnummer.", "LabelReadHowYouCanContribute": "Lær hvordan du kan bidrage.", "LabelRecord": "Optag:", "LabelRecordingPath": "Standard afspilningssti:", @@ -614,7 +614,7 @@ "LabelSerialNumber": "Serienummer", "LabelSeriesRecordingPath": "Serier afspilningssti (valgfri):", "LabelServerHost": "Vært:", - "LabelServerHostHelp": "F. eks: 192.168.1.100 eller https://myserver.com", + "LabelServerHostHelp": "F. eks: 192.168.1.100:8096 eller https://myserver.com", "LabelSimultaneousConnectionLimit": "samtidige stream begrænsning:", "LabelSkipIfAudioTrackPresent": "Undlad hvis standardlydsporet er det samme sprog", "LabelSkipIfAudioTrackPresentHelp": "Angiv ikke dette for at sikre at alle videoer har undertekster, uanset hvilket sprog lydsporet anvender.", @@ -632,11 +632,6 @@ "LabelSubtitleFormatHelp": "F. eks: srt", "LabelSubtitlePlaybackMode": "Underteksttilstand:", "LabelSupportedMediaTypes": "Understøttede medieformater:", - "LabelSyncJobName": "Navn til synkroniserings job:", - "LabelSyncPath": "Synked indholdssti:", - "LabelSyncTempPath": "Sti for midlertidige filer:", - "LabelSyncTempPathHelp": "Specificér en brugerdefineret synkroniserings arbejds-mappe. Konverterede filer vil under synkroniseringsprocessen blive gemt her.", - "LabelSyncTo": "Synkroniser til:", "LabelTheme": "Tema:", "LabelTime": "Tid:", "LabelTimeLimitHours": "Tidsgrænse (timer):", @@ -647,7 +642,7 @@ "LabelTranscodingTempPathHelp": "Definér en bugerdefineret sti til transkodede filer til klienter. Lad den stå tom for at bruge standardmappen i serverens datamappe.", "LabelTranscodingTemporaryFiles": "Midlertidige filer til omkodning:", "LabelTranscodingThreadCount": "Antal af omkodningstråde:", - "LabelTranscodingThreadCountHelp": "Vælg det maksimale antal af tråde der bruges under omkodning. Reduktion af antallet af tråde sænker cpu-forbrug, men resulterer muligvis i at konverteringer ikke foregår hurtigt nok til en jævn afspilning.", + "LabelTranscodingThreadCountHelp": "Vælg det maksimale antal af tråde der bruges under transcoding. Reduktion af antallet af tråde sænker CPU-forbrug, men resulterer muligvis i at konverteringer ikke foregår hurtigt nok til en jævn afspilning.", "LabelTriggerType": "Udløsertype:", "LabelTunerIpAddress": "IP-adresse for Tuner:", "LabelTunerType": "Tunertype:", @@ -707,37 +702,37 @@ "MessageConfirmRemoveMediaLocation": "Er du sikker på du ønsker at fjerne denne lokalisation?", "MessageConfirmRestart": "Er du sikker på du ønsker at genstarte Jellyfin?", "MessageConfirmRevokeApiKey": "Er du sikker på du ønsker at invalidere denne api nøgle? Applikationens forbindelse til Jellyfin vil blive afbrudt øjeblikkeligt.", - "MessageConfirmShutdown": "Er du sikker på du ønsker at lukke Jellyfin?", + "MessageConfirmShutdown": "Er du sikker på du ønsker at slukke for serveren?", "MessageContactAdminToResetPassword": "Kontakt venligst din systemadministrator for at nulstille din adgangskode.", "MessageCreateAccountAt": "Opret en konto hos {0}", "MessageDeleteTaskTrigger": "Er du sikker på du ønsker at slette denne task trigger?", "MessageDirectoryPickerBSDInstruction": "For BSD skal du muligvis konfigurere lager i dit FreeNAS Jail, før Jellyfin kan tilgå det.", "MessageDirectoryPickerInstruction": "Netværksstier kan indtastes manuelt i tilfælde af at netværksknappen ikke kan lokalisere dine enheder. Foreksempel, {0} eller {1}.", - "MessageDirectoryPickerLinuxInstruction": "For Linux på Arch Linux, CentOS, Debian, Fedora, OpenSuse eller Ubuntu skal du give Jellyfin-systembrugeren minimum læseadgang til dine lagerlokationer.", + "MessageDirectoryPickerLinuxInstruction": "For Linux på Arch Linux, CentOS, Debian, Fedora, openSUSE eller Ubuntu, skal du give servicebrugeren mindst læseadgang til dine lagerpladser.", "MessageDownloadQueued": "Download sat i kø.", "MessageEnablingOptionLongerScans": "Aktivering af denne indstilling kan resultere i væsentlig længere biblioteks skan.", "MessageFileReadError": "Der opstod en fejl i forsøget på at læse filen.", "MessageForgotPasswordFileCreated": "Den følgende fil er blevet oprettet på din server og indeholder instruktioner vedrørende hvordan du skal fortsætte:", "MessageForgotPasswordInNetworkRequired": "Prøv igen inde i dit hjemmenetværk for at igangsætte nulstilling af din adgangskode.", - "MessageInstallPluginFromApp": "Dette plugin skal være installeret inde i den app du ønsker at benytte det fra.", + "MessageInstallPluginFromApp": "Dette plugin skal installeres fra den app, du har til hensigt at bruge det i.", "MessageInvalidForgotPasswordPin": "En ugyldig eller udløbet pinkode blev indtastet. Prøv igen.", "MessageInvalidUser": "Ukendt brugernavn eller adgangskode. Prøv igen.", "MessageItemSaved": "Element gemt.", "MessageItemsAdded": "Emne tilføjet.", - "MessageLeaveEmptyToInherit": "Efterlad tom for at arve indstillinger fra en overliggende post eller den globale standardværdi.", + "MessageLeaveEmptyToInherit": "Lad være tom for at arve indstillinger fra et overordnet element eller den globale standardværdi.", "MessageNoAvailablePlugins": "Ingen tilgængelige plugins.", "MessageNoMovieSuggestionsAvailable": "Ingen filmforslag er tilgængelige. Begynd at se og vurder dine film, og kom tilbage for at se dine anbefalinger.", "MessageNoPluginsInstalled": "Du har ingen plugins installeret.", "MessageNoTrailersFound": "Ingen trailere fundet. Installer Trailer kanalen for at tilføje et bibliotek med trailere fra internettet.", "MessageNothingHere": "Her er ingenting.", - "MessagePasswordResetForUsers": "Adgangskoder blev fjernet for følgende brugere. For at logge ind, skal der benyttes en blank adgangskode.", + "MessagePasswordResetForUsers": "Følgende brugere har fået nulstillet deres adgangskoder. De kan nu logge på med de pinkoder, der blev brugt til at udføre nulstillingen.", "MessagePleaseEnsureInternetMetadata": "Sørg venligst for at hentning af metadata fra internettet er aktiveret.", "MessagePleaseWait": "Vent venligst. Dette kan tage et minut.", "MessagePluginConfigurationRequiresLocalAccess": "For at konfigurerer dette plugin log da venligst direkte ind på din lokale server.", "MessagePluginInstallDisclaimer": "Plugins fremstillet af medlemmer fra Jellyfin-fællesskabet er en alle tiders måde at forbedre din oplevelse af Jellyfin med yderligere features og fordele. Før installation, bedes du venligst være opmærksom på de effekter de kan have på din Jellyfin Server; så som lange scantider på biblioteker, yderligere baggrundsbehandling og forringet systemstabilitet.", "MessageReenableUser": "Se nedenfor om genaktivering", "MessageSettingsSaved": "Indstillinger er gemt.", - "MessageTheFollowingLocationWillBeRemovedFromLibrary": "Følgende medielokationer bliver fjernet fra dit Jellyfin-bibliotek:", + "MessageTheFollowingLocationWillBeRemovedFromLibrary": "Følgende medieplaceringer fjernes fra dit bibliotek:", "MessageUnableToConnectToServer": "Vi kan ikke forbinde til den valgte server på nuværende tidspunkt. Sikrer dig venligst at serveren kører og prøv igen.", "MessageUnsetContentHelp": "Indhold vil blive vist som almindelige mapper. For det bedste resultat benyt metadata manageren til at vælge indholdstypen i undermapper.", "MessageYouHaveVersionInstalled": "Du har version {0} installeret.", @@ -747,10 +742,10 @@ "MinutesBefore": "minutter før", "Monday": "Mandag", "MoreFromValue": "Mere fra {0}", - "MoreUsersCanBeAddedLater": "Flere brugere kan tilføjes senere i betjeningspanelet.", + "MoreUsersCanBeAddedLater": "Flere brugere kan tilføjes senere fra dashboardet.", "MoveLeft": "Flyt mod venstre", "MoveRight": "Flyt mod højre", - "MovieLibraryHelp": "Gennemse {0}Jellyfin film navngivningsguiden{1}.", + "MovieLibraryHelp": "Gennemse {0}film navngivningsguiden{1}.", "Movies": "Film", "Mute": "Afbryd lyd", "MySubtitles": "Mine Undertekster", @@ -782,7 +777,7 @@ "OptionAllowMediaPlaybackTranscodingHelp": "At begrænse adgang til omkodning kan forårsage afspilningsfejl i Jellyfin apps på grund af usupporterede medie formater.", "OptionAllowRemoteControlOthers": "Tillad fjernstyring af andre brugere", "OptionAllowRemoteSharedDevices": "Tillad fjernstyring af delte enheder", - "OptionAllowRemoteSharedDevicesHelp": "DLNA-enheder er delte indtil en bruger begynder at bruge den.", + "OptionAllowRemoteSharedDevicesHelp": "DLNA-enheder betragtes som delt, indtil en bruger begynder at kontrollere dem.", "OptionAllowSyncTranscoding": "Tillad medie hentning og synkronisering der kræver omkodning", "OptionAllowUserToManageServer": "Tillad denne bruger at administrere serveren", "OptionAllowVideoPlaybackRemuxing": "Tillad videoafspilning som kræver konvertering uden omkodning", @@ -811,13 +806,13 @@ "OptionDisableUserHelp": "Hvis deaktiveret vil serveren ikke tillade forbindelser fra denne bruger. Eksisterende forbindelser vil blive afbrudt øjeblikkeligt.", "OptionDislikes": "Ikke-Lide", "OptionDisplayFolderView": "Få vist en mappevisning til at se enkle mediemapper", - "OptionDisplayFolderViewHelp": "Hvis aktiveret, vil Jellyfin apps vise en Mappekategori ved siden af dine mediebiblioteker. Dette er brugbart, hvis du vil have vist enkle mappevisninger.", + "OptionDisplayFolderViewHelp": "Vis mapper sammen med dine andre mediebiblioteker. Dette kan være nyttigt, hvis du gerne vil have en almindelig mappevisning.", "OptionDownloadArtImage": "Billede", "OptionDownloadBackImage": "Bagside", "OptionDownloadBoxImage": "Boks", "OptionDownloadDiscImage": "Disk", "OptionDownloadImagesInAdvance": "Download billeder på forhånd", - "OptionDownloadImagesInAdvanceHelp": "Som standard downloades billeder kun når de anmodes af en Jellyfin app. Aktiver denne indstilling for at downloade alle billeder på forhånd, i mens nyt medie importeres. Dette resulterer muligvis i længere scanninger af bibliotek.", + "OptionDownloadImagesInAdvanceHelp": "Som standard downloades de fleste billeder kun, når de anmodes fra en Jellyfin-app. Aktivér denne mulighed for at downloade alle billeder på forhånd, da nye medier importeres. Dette kan forårsage betydeligt længere biblioteksscanninger.", "OptionDownloadPrimaryImage": "Primær", "OptionDownloadThumbImage": "Miniature", "OptionDvd": "DVD", @@ -826,7 +821,7 @@ "OptionEnableAccessToAllChannels": "Tillad adgang til alle kanaler", "OptionEnableAccessToAllLibraries": "Tillad adgang til alle biblioteker", "OptionEnableExternalContentInSuggestions": "Aktiver eksternt indhold i anbefalinger", - "OptionEnableExternalContentInSuggestionsHelp": "Tillad at internet-trailers og live-tv-programmer bliver inkluderet i det anbefalede indhold.", + "OptionEnableExternalContentInSuggestionsHelp": "Tillad at internet-trailers og live TV programmer bliver inkluderet i det anbefalede indhold.", "OptionEnableForAllTuners": "Aktiver for alle tuner-enheder", "OptionEnableM2tsMode": "Aktiver M2ts tilstand", "OptionEnableM2tsModeHelp": "Aktiver M2ts tilstand når der omkodes til mpegts.", @@ -844,8 +839,8 @@ "OptionHasThemeVideo": "Temavideo", "OptionHideUser": "Vis ikke denne bruger på loginsiden", "OptionHideUserFromLoginHelp": "Nyttigt for private kontoer eller skjulte administratorkontoer. Brugeren skal logge ind ved at skive sit brugernavn og adgangskode.", - "OptionHlsSegmentedSubtitles": "Hls segmented undertekster", - "OptionHomeVideos": "Hjemmevideoer og billeder", + "OptionHlsSegmentedSubtitles": "HLS segmenterede undertekster", + "OptionHomeVideos": "Billeder", "OptionIgnoreTranscodeByteRangeRequests": "Ignorer forespørgsler vedrørende transcode byte interval", "OptionIgnoreTranscodeByteRangeRequestsHelp": "Hvis aktiveret vil disse forespørgsler blive efterkommet, men byte range headeren ignoreret.", "OptionImdbRating": "IMDB bedømmelse", @@ -883,7 +878,7 @@ "OptionThursday": "Torsdag", "OptionTrackName": "Nummerets navn", "OptionTuesday": "Tirsdag", - "OptionTvdbRating": "Tvdb bedømmelse", + "OptionTvdbRating": "TVDB bedømmelse", "OptionUnairedEpisode": "Ikke sendte episoder", "OptionUnplayed": "Ikke afspillet", "OptionWakeFromSleep": "Vågner fra dvale", @@ -893,9 +888,9 @@ "OptionWeekly": "Ugentlig", "OriginalAirDateValue": "Originalt sendt: {0}", "Overview": "Overblik", - "PackageInstallCancelled": "{0} installation afbrudt.", - "PackageInstallCompleted": "{0} installation udført.", - "PackageInstallFailed": "{0} installationen mislykkedes.", + "PackageInstallCancelled": "{0} (version {1}) installation annulleret.", + "PackageInstallCompleted": "{0} (version {1}) installation udført.", + "PackageInstallFailed": "{0} (version {1}) installationen mislykkedes.", "ParentalRating": "Parental Rating", "PasswordMatchError": "Adgangskode og bekræft adgangskode skal være ens.", "PasswordResetComplete": "Adgangskoden er blevet nulstillet.", @@ -913,7 +908,7 @@ "Played": "Afspillet", "PleaseAddAtLeastOneFolder": "Tilføj venligst som minimum en enkelt mappe til dette bibliotek ved at klikke på Tilføj-knappen.", "PleaseConfirmPluginInstallation": "Klik venligst OK for at bekræfte at du har læst ovenstående og ønsker at fortsætte med installationen af plugin.", - "PleaseEnterNameOrId": "Indtast venligst et navn eller eksternt Id.", + "PleaseEnterNameOrId": "Indtast venligst et navn eller eksternt ID.", "PleaseRestartServerName": "Genstart venligst Jellyfin Server - {0}.", "PleaseSelectTwoItems": "Vælg venligst mindst to elementer.", "PluginInstalledMessage": "Plugin blev installeret med success. Jellyfin serveren skal genstartes for at aktivere det.", @@ -1025,7 +1020,7 @@ "TabProfiles": "Profiler", "TabRecordings": "Optagelser", "TabResponses": "Svar", - "TabResumeSettings": "Indstillinger for Genoptag", + "TabResumeSettings": "Genoptag", "TabScheduledTasks": "Planlagte opgaver", "TabSeries": "Serier", "TabSettings": "Indstillinger", @@ -1044,13 +1039,13 @@ "TitlePlayback": "Afspilning", "TrackCount": "{0} numre", "Tuesday": "Tirsdag", - "TvLibraryHelp": "Gennemse {0}Jellyfin TV navngivningsguiden{1}.", + "TvLibraryHelp": "Gennemgå {0} TV-navneguiden {1}.", "UninstallPluginConfirmation": "Er du sikker på du vil afinstallere {0}?", "UninstallPluginHeader": "Afinstaller plugin", "Unmute": "Genoptag lyd", "Unrated": "Ingen bedømmelse", - "UserAgentHelp": "Angiv en brugerdefineret user-agent http header, hvis nødvendigt.", - "UserProfilesIntro": "Jellyfin har indbygget understøttelse af brugerprofiler. Dette giver hver bruger sine egne indstillinger for visning, afspilningsstatus og forældrekontrol.", + "UserAgentHelp": "Angiv en brugerdefineret bruger-agent HTTP-header.", + "UserProfilesIntro": "Jellyfin inkluderer support til brugerprofiler med granuleret displayindstillinger, afspilningstilstand og forældrekontrol.", "ValueAlbumCount": "{0} album", "ValueAudioCodec": "Lyd codec: {0}", "ValueConditions": "Forhold: {0}", @@ -1072,17 +1067,17 @@ "ViewPlaybackInfo": "Vis afspilnings information", "Wednesday": "Onsdag", "WelcomeToProject": "Velkommen til Jellyfin!", - "WizardCompleted": "Det er alt vi behøver for nu. Jellyfin er begyndt at indsamle information omkring dit mediebibliotek. Tjek nogle af vores apps og klik derefter på Færdig for at se Server betjeningspanelet.", + "WizardCompleted": "Det er alt, hvad vi har brug for nu. Jellyfin er begyndt at indsamle information om dit mediebibliotek. Se nogle af vores apps, og klik derefter på Udfør for at se Dashboard.", "Writer": "Forfatter", - "XmlDocumentAttributeListHelp": "Disse attributter bliver tilføjet til rodelementet i alle XML svar.", + "XmlDocumentAttributeListHelp": "Disse attributter anvendes til rodelementet i hvert XML-svar.", "XmlTvKidsCategoriesHelp": "Programmer med disse kategorier bliver vist som programmer for børn. Adskil flere med '|'.", "XmlTvMovieCategoriesHelp": "Programmer med disse kategorier bliver vist som film. Adskil flere med '|'.", "XmlTvNewsCategoriesHelp": "Programmer med disse kategorier bliver vist som nyhedsprogrammer. Adskil flere med '|'.", - "XmlTvPathHelp": "En sti til en xml tv-fil. Jellyfin læser denne fil og kontrollerer periodisk for opdateringer. Du er ansvarlig for at oprette og opdatere filen.", + "XmlTvPathHelp": "En sti til en XMLTV fil. Jellyfin læser denne fil og kontrollerer periodisk for opdateringer. Du er ansvarlig for at oprette og opdatere filen.", "XmlTvSportsCategoriesHelp": "Programmer med disse kategorier bliver vist som sportsprogrammer. Adskil flere med '|'.", "Yesterday": "I går", "AirDate": "Luftdata", - "Albums": "Album", + "Albums": "Albums", "Artists": "Kunstnere", "Books": "Bøger", "Collections": "Samlinger", @@ -1092,15 +1087,14 @@ "Absolute": "Absolut", "AccessRestrictedTryAgainLater": "Adgang er begrænset. Prøv igen senere.", "Aired": "Blev sendt", - "AllComplexFormats": "Alle komplekse formater (ASS, SSA, VOBSUB, PGS, SUB/IDX osv.)", + "AllComplexFormats": "Alle Komplekse Formater (ASS, SSA, VOBSUB, PGS, SUB,IDX osv.)", "AllLanguages": "Alle sprog", - "AlwaysPlaySubtitles": "Afspil altid undertekster", + "AlwaysPlaySubtitles": "Afspil Altid", "AlwaysPlaySubtitlesHelp": "Undertekster, der matcher dine sprogindstillinger, vil altid blive indlæst uanset lydsprog.", "HeaderLiveTV": "Live-TV", "Shows": "Serier", "Songs": "Sange", - "AndroidUnlockRestoreHelp": "For at gendanne dit tidligere køb skal du sørge for, at du er logget ind på enheden med den samme Google- eller Amazon-konto, som oprindeligt gjorde købet. Sørg for, at app store er aktiveret og ikke begrænset af forældrekontrol, og sørg for, at du har en aktiv internetforbindelse. Du skal kun gøre dette én gang for at gendanne dit tidligere køb.", - "AnyLanguage": "Ethvert sprog", + "AnyLanguage": "Hvilken som helst sprog", "Art": "Kunst", "Ascending": "Stigende", "AudioBitDepthNotSupported": "Lyd bit dybde ikke understøttet", @@ -1111,17 +1105,13 @@ "AudioSampleRateNotSupported": "Lydens samplerate ikke understøttet", "Auto": "Auto", "AutoBasedOnLanguageSetting": "Automatisk (baseret på sprogindstilling)", - "AutomaticallyConvertNewContent": "Konverter automatisk nyt indhold", - "AutomaticallyConvertNewContentHelp": "Nyt indhold tilføjet til denne mappe vil blive konverteret automatisk.", - "AutomaticallySyncNewContent": "Download nyt indhold automatisk", - "AutomaticallySyncNewContentHelp": "Nyt indhold tilføjet til denne mappe vil automatisk blive downloadet til enheden.", "Backdrop": "Baggrund", "Backdrops": "Baggrunde", - "Banner": "Bannere", + "Banner": "Banner", "Blacklist": "Blackliste", "Box": "Boks", "BoxRear": "Boks (bagside)", - "BurnSubtitlesHelp": "Bestemmer om serveren skal brænde underteksterne ind i videoen når den konverterer baseret på undertekstformatet. Det vil øge serverens ydeevne ikke at brænde underteksterne i filen. Vælg Automatisk for at brænde billedbaserede formater (VOBSUB, PGS, SUB/IDX, osv) og nogle ASS/SSA undertekster.", + "BurnSubtitlesHelp": "Bestemmer om serveren skal brænde undertekster, når der afspilles transcoding videoer. Undgå dette vil forbedre ydelsen meget. Vælg Auto for at brænde billedbaserede formater (VOBSUB, PGS, SUB, IDX) og bestemte ASS- eller SSA-undertekster.", "ButtonFilter": "Filter", "ButtonGuide": "Vejledning", "ButtonInfo": "Information", @@ -1174,14 +1164,14 @@ "DisplayInOtherHomeScreenSections": "Visning på hjemmeskærm sektioner som seneste medier og se videre", "DisplayMissingEpisodesWithinSeasons": "Vis manglende afsnit inde i sæsoner", "DisplayMissingEpisodesWithinSeasonsHelp": "Dette skal også være aktiveret for TV biblioteker i serverens indstillinger.", - "DisplayModeHelp": "Vælg skærmtypen du kører Jellyfin på.", + "DisplayModeHelp": "Vælg det ønskede tema for grænsefladen.", "Down": "Ned", "DownloadItemLimitHelp": "Valgfri. Sæt en begrænsning på antallet af ting der vil blive hentet.", "Downloaded": "Hentet", "DownloadingDots": "Henter...", "Downloads": "Hentninger", "DownloadsValue": "{0} hentninger", - "DropShadow": "Drop skygge", + "DropShadow": "Drop Skygge", "DvrFeatureDescription": "Tidsindstil individuelle TV optagelser, serie optagelser, og mere med Jellyfin DVR.", "EditMetadata": "Redigér metadata", "EnableBackdrops": "Baggrundsbilleder", @@ -1198,10 +1188,8 @@ "Episodes": "Afsnit", "ErrorAddingGuestAccount1": "Der skete en fejl ved tilføjelsen af Jellyfin Connect kontoen. Har din gæst lavet en Jellyfin konto? De kan regsistrere sig på {0}.", "ErrorAddingGuestAccount2": "Hvis du stadig har problemer, så send venligst en email til {0}, og inkludér din email adresse såvel som deres.", - "ErrorAddingJellyfinConnectAccount1": "Der skete en fejl ved tilføjelsen af Jellyfin Connect kontoen. Har du lavet en Jellyfin konto? Registrer dig på {0}.", - "ErrorAddingJellyfinConnectAccount2": "Hvis du stadig har et problem, så send venligst en email til {0} fra den email adresse tilknyttet Jellyfin kontoen.", "ErrorDeletingItem": "Der skete en fejl ved sletningen af mediet fra Jellyfin Server. Tjek venligst at Jellyfin Server har skrive adgang til mediemappen og prøv igen.", - "ExtraLarge": "Ekstra stor", + "ExtraLarge": "Ekstra Stor", "Extras": "Bonusmateriale", "Features": "Funktioner", "Filters": "Filtre", @@ -1236,7 +1224,6 @@ "HeaderNavigation": "Navigation", "HeaderNextEpisodePlayingInValue": "Næste afsnit afspilles om {0}", "HeaderNextVideoPlayingInValue": "Næste video afspilles om {0}", - "HeaderOffline": "Offline", "HeaderPhotoAlbums": "Foto Albummer", "HeaderPlayOn": "Afspil På", "HeaderProgram": "Program", @@ -1252,36 +1239,32 @@ "HeaderTags": "Mærker", "HeaderTopPlugins": "Bedste Plugins", "HeaderType": "Type", - "HeaderUpcomingNews": "Kommende Nyheder", "HeaderVideo": "Video", "HeaderVideoQuality": "Video Kvalitet", "HeaderVideoType": "Video Type", - "HeaderWaitingForWifi": "Venter på Wifi", "Hide": "Skjul", "HideWatchedContentFromLatestMedia": "Skjul set indhold fra seneste medier", "Home": "Hjem", "Horizontal": "Horisontalt", "ImdbRating": "IMDb bedømmelse", "InterlacedVideoNotSupported": "Interlaced video ikke understøttet", - "InviteAnJellyfinConnectUser": "Tilføj en bruger ved at sende en email invitation.", "KeepDownload": "Behold hentning", "Label3DFormat": "3D format:", "LabelAlbum": "Album:", "LabelArtist": "Kunstner", "LabelAudio": "Lyd", "LabelBitrateMbps": "Bitrate (Mbps):", - "LabelBlockContentWithTags": "Blokér filer med mærkerne:", + "LabelBlockContentWithTags": "Blokér filer med etiketter:", "LabelBurnSubtitles": "Brænd undertekster:", "LabelCache": "Cache:", "LabelCertificatePassword": "Adgangskode til certifikat:", - "LabelCertificatePasswordHelp": "Hvis dit certifikat kræver en adgangskode, skriv det benligst her.", - "LabelConvertTo": "Konvertér til:", + "LabelCertificatePasswordHelp": "Hvis dit certifikat kræver en adgangskode, skriv det venligst her.", "LabelDashboardTheme": "Server dashboard tema:", "LabelDateTimeLocale": "Dato og tid område:", "LabelDefaultScreen": "Standard skærm:", "LabelDisplayLanguage": "Visningssprog:", "LabelDisplayLanguageHelp": "Oversættelse af Jellyfin er et vedvarende projekt.", - "LabelDisplayMode": "Visningsmodus:", + "LabelDisplayMode": "Visningstilstand:", "LabelDropShadow": "Drop skygge:", "LabelDynamicExternalId": "{0} ID:", "LabelEmail": "Email:", @@ -1297,7 +1280,7 @@ "LabelMaxBitrate": "Maks bitrate:", "LabelMaxChromecastBitrate": "Chromecast streaming kvalitet:", "LabelMetadata": "Metadata:", - "LabelModelUrl": "Model link", + "LabelModelUrl": "Model URL", "LabelPreferredSubtitleLanguage": "Foretrukket undertekst sprog:", "LabelProfileCodecs": "Codecs:", "LabelProfileContainer": "Beholder:", @@ -1314,7 +1297,6 @@ "LabelSoundEffects": "Lydeffekter:", "LabelStatus": "Status:", "LabelSubtitles": "Undertekster", - "LabelSyncNoTargetsHelp": "Det ser ud til at du ikke har nogen apps der understøtter offline hentning.", "LabelTVHomeScreen": "TV modus hjemmeskærm:", "LabelTag": "Mærke:", "LabelTagline": "Taglinje:", @@ -1333,7 +1315,6 @@ "LabelXDlnaCap": "X-DLNA begrænsning:", "LabelXDlnaDoc": "X-DLNA dokumentation:", "LabelYear": "År:", - "LabelffmpegVersion": "FFmpeg version:", "Large": "Stor", "LearnHowYouCanContribute": "Lær hvordan du kan bidrage.", "LeaveBlankToNotSetAPassword": "Du kan lade dette felt være tomt hvis du ikke ønsker adgangskode.", @@ -1365,7 +1346,6 @@ "MessageImageFileTypeAllowed": "Kun JPEG og PNG filer er understøttet.", "MessageImageTypeNotSelected": "Vælg venligst en type af billede i drop-down menuen.", "MessageNoDownloadsFound": "Ingen offline hentninger. Hent dine medier til offline brug ved at klikke Hent igennem app'en.", - "MessageNoSyncJobsFound": "Ingen hentninger fundet. Opret hent job ved at bruge Hent knapperne igennem app'en.", "MessagePlayAccessRestricted": "Afspilning af dette indhold er begrænset. Kontakt venligst server administratoren for mere information.", "Metadata": "Metadata", "Mobile": "Mobil", @@ -1378,11 +1358,8 @@ "Off": "Fra", "OnlyForcedSubtitles": "Kun tvungne undertekster", "OnlyForcedSubtitlesHelp": "Kun undertekster markeret som tvungne vil blive indlæst.", - "OnlyImageFormats": "Kun billedformater (VOBSUB, PGS, SUB, etc)", - "Option2Player": "2+", + "OnlyImageFormats": "Kun billedformater (VOBSUB, PGS, SUB)", "Option3D": "3D", - "Option3Player": "3+", - "Option4Player": "4+", "OptionAlbum": "Album", "OptionArtist": "Kunstner", "OptionAuto": "Automatisk", @@ -1420,13 +1397,12 @@ "PlayCount": "Afspilninger", "PlayNext": "Afspil næste", "PlayNextEpisodeAutomatically": "Afspil næste afsnit automatisk", - "PlaybackErrorNoCompatibleStream": "Ingen kompatible streams er tilgængelige. Prøv venligst igen senere eller kontakt din system administrator for detaljer.", + "PlaybackErrorNoCompatibleStream": "Denne klient er ikke kompatibel med medierne, og serveren sender ikke et kompatibelt medieformat.", "PlaybackErrorNotAllowed": "Du har ikke adgang til at afspille dette indhold. Kontakt venligst system administratoren for detaljer.", "PlaybackErrorPlaceHolder": "Indlæs venligst disken for at afspille denne video.", "PlaybackSettings": "Afspilningsindstillinger", "PlaybackSettingsIntro": "For at indstille standard afspilningsindstillingerne, stop video afspilning, herefter klik på dit bruger ikon i øverste højre sektion af denne app.", "Playlists": "Afspilningslister", - "PleaseSelectDeviceToSyncTo": "Vælg venligst en enhed at hente til.", "Previous": "Forrige", "Primary": "Primær", "PrivacyPolicy": "Privatlivs politik", @@ -1436,7 +1412,7 @@ "RefFramesNotSupported": "Antal af video reference billeder ikke understøttet", "RefreshMetadata": "Genopfrisk metadata", "RepeatAll": "Gentag alle", - "RepeatMode": "Gentagelsesmode", + "RepeatMode": "Gentagelses tilstand", "RepeatOne": "Gentag én", "RestartPleaseWaitMessage": "Vent venligst mens Jellyfin Server lukker og genstarter. Dette kan tage et minut eller to.", "RunAtStartup": "Kør ved opstart", @@ -1444,7 +1420,7 @@ "Schedule": "Tidsplan", "Screenshot": "Skærmbillede", "SecondaryAudioNotSupported": "Lydspor skift ikke understøttet", - "SeriesDisplayOrderHelp": "Sortér afsnit efter sende dato, dvd rækkefølge eller obsolut nummering.", + "SeriesDisplayOrderHelp": "Sortér episoder efter luftdato, DVD-orden eller absolut nummerering.", "ShowTitle": "Vis titel", "ShowYear": "Vis år", "Small": "Lille", @@ -1455,16 +1431,12 @@ "Sort": "Sortér", "SortByValue": "Sortér efter {0}", "Standard": "Standard", - "StatsForNerds": "Stats for nørder", "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Disse indstilinger bliver aktiveret på enhver Chromecast afspilning på denne enhed.", "SubtitleAppearanceSettingsDisclaimer": "Disse indstillinger bliver ikke aktiveret på grafiske undertekster (PGS, DVD, etc) eller ASS/SSA undertekster der har deres egen indbyggede stil.", "SubtitleCodecNotSupported": "Undertekst format ikke understøttet", "SubtitleSettings": "Undertekst indstillinger", "SubtitleSettingsIntro": "For at konfigurere standard undertekst udseende og sprog indstillinger, stop video afspilning, herefter klik på dit bruger ikon i øverste højre sektion af denne app.", "Suggestions": "Forslag", - "SyncUnwatchedVideosOnly": "Hent kun usete videoer", - "SyncUnwatchedVideosOnlyHelp": "Kun usete videoer vil blive hentet, og videoer vil bliver slettet fra enheden når de er set.", - "SyncingDots": "Synkroniserer...", "TV": "TV", "TabAlbums": "Albummer", "TabCodecs": "Codeks", @@ -1478,7 +1450,6 @@ "TabPlaylist": "Afspilningsliste", "TabServer": "Server", "TabStreaming": "Streamer", - "TabSync": "Sync", "TabTV": "TV", "Tags": "Mærker", "TagsValue": "Mærker: {0}", @@ -1489,7 +1460,6 @@ "TitleLiveTV": "Live TV", "TitleServer": "Server", "TitleSupport": "Support", - "TitleSync": "Sync", "Trailer": "Forfilm", "Trailers": "Forfilm", "Transcoding": "Omkodning", @@ -1519,7 +1489,6 @@ "ViewTypeTvShows": "TV", "Watched": "Set", "Whitelist": "Hvidliste", - "WifiRequiredToDownload": "En Wifi forbindelse er påkrævet for at forsætte hentning.", "Yes": "Ja", "HeaderFavoriteMovies": "Favoritfilm", "HeaderFavoriteShows": "Favoritserier", @@ -1532,5 +1501,114 @@ "LabelServerName": "Server navn:", "LabelUserLoginAttemptsBeforeLockout": "Fejlede loginforsøg før bruger lukkes ude:", "HeaderRestartingServer": "Genstarter Server", - "ButtonAddImage": "Tilføj billede" + "ButtonAddImage": "Tilføj billede", + "AllowFfmpegThrottlingHelp": "Når en omkodning eller remux kommer langt nok foran den nuværende afspildings position, pauses processen så der bruges færre resurser. Dette er mest brugbart når man ikke springer i filmen. Slå dette fra hvis du har problemer med playback.", + "AllowFfmpegThrottling": "Begræns Omkodning", + "AlbumArtist": "Album Artist", + "Album": "Album", + "EveryHour": "Hver time", + "EveryXMinutes": "Hvert {0} minut", + "OnWakeFromSleep": "Ved vækning fra dvale", + "WeeklyAt": "{0}s ved {1}", + "DailyAt": "Dagligt kl. {0}", + "LastSeen": "Sidst set {0}", + "PersonRole": "som {0}", + "ListPaging": "{0}-{1} af {2}", + "WriteAccessRequired": "Jellyfin Server kræver skriveadgang til denne mappe. Sørg for skriveadgang, og prøv igen.", + "PathNotFound": "Stien blev ikke fundet. Sørg for, at stien er gyldig, og prøv igen.", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "Track": "Spor", + "TabNetworking": "Netværk", + "SubtitleOffset": "Undertekst Offset", + "SelectAdminUsername": "Vælg et brugernavn til administrator kontoen.", + "Season": "Sæson", + "ReleaseGroup": "Release Group", + "Premiere": "Premiere", + "PreferEmbeddedEpisodeInfosOverFileNames": "Foretrækker integreret episode information frem for filnavne", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Dette bruger episode informationen fra de integrerede metadata, hvis den er tilgængelig.", + "PlaybackData": "Afspilningsdata", + "Person": "Person", + "PasswordResetProviderHelp": "Vælg en leverandør af nulstil adgangskode, der skal bruges, når denne bruger anmoder om en nulstilling af adgangskode", + "OtherArtist": "Anden kunstner", + "OptionThumbCard": "Thumb card", + "OptionThumb": "Thumb", + "OptionRandom": "Tilfældig", + "OptionPosterCard": "Plakatkort", + "OptionPoster": "Plakat", + "OptionLoginAttemptsBeforeLockoutHelp": "En værdi på nul betyder at arve standard for tre forsøg for normale brugere og fem for administratorer. Indstilling af dette til -1 vil deaktivere funktionen.", + "OptionLoginAttemptsBeforeLockout": "Bestemmer, hvor mange forkerte loginforsøg, der kan gøres, før lockout finder sted.", + "OptionList": "Liste", + "OptionForceRemoteSourceTranscoding": "Tving transcoding af eksterne mediekilder (som LiveTV)", + "NoCreatedLibraries": "Det ser ud til, at du ikke har oprettet nogen biblioteker endnu. {0} Vil du oprette en nu? {1}", + "MusicVideo": "Musik Video", + "MusicLibraryHelp": "Gennemgå {0} guide til navngivning af musik {1}.", + "MusicArtist": "Musik Artist", + "MusicAlbum": "Musik Album", + "Movie": "Film", + "MoreMediaInfo": "Medieinfo", + "MessageNoServersAvailable": "Der er ikke fundet nogen servere ved hjælp af den automatiske serveropdagelse.", + "MessageNoCollectionsAvailable": "Samlinger tillader dig at nyde personlige grupperinger af Film, Serier og Albums. Klik på + knappen for at skabe en samling.", + "MessageConfirmAppExit": "Vil du afslutte?", + "MediaInfoStreamTypeSubtitle": "Undertekst", + "MediaInfoStreamTypeEmbeddedImage": "Indlejret billede", + "MediaInfoStreamTypeAudio": "Lyd", + "LaunchWebAppOnStartupHelp": "Åben web klienten i den standard web browser når serveren starter første gang. Dette vil ikke ske når restart server funktionen benyttes.", + "LaunchWebAppOnStartup": "Åben webinterfacet når serveren startes", + "LabelWeb": "Web:", + "LabelVideoResolution": "Videoopløsning:", + "LabelVideoBitrate": "Video bitrate:", + "DashboardArchitecture": "Arkitektur: {0}", + "DashboardOperatingSystem": "Styresystem: {0}", + "DashboardServerName": "Server: {0}", + "DashboardVersionNumber": "Version: {0}", + "LabelTranscodingProgress": "Transcoding fremskridt:", + "LabelTranscodingFramerate": "Transcoding framerate:", + "LabelTranscodes": "Transcodes:", + "LabelTranscodePath": "Transcode sti:", + "LabelStreamType": "Stream type:", + "LabelSonyAggregationFlags": "Sony aggregering flag:", + "LabelSize": "Størrelse:", + "LabelPleaseRestart": "Ændringer vil træde i kraft efter web klienten er blevet genindlæst manuelt.", + "LabelPlayMethod": "Afspilnings metode:", + "LabelPlayerDimensions": "Afspillerdimensioner:", + "LabelPlayer": "Afspiller:", + "LabelPasswordResetProvider": "Udbyder til nulstilling as kodeord:", + "LabelLibraryPageSizeHelp": "Indstiller mængden af genstande, der skal vises på en bibliotekside. Indstil til 0 for at deaktivere.", + "LabelLibraryPageSize": "Biblioteks størrelse:", + "LabelFolder": "Mappe:", + "LabelBaseUrl": "Base URL:", + "LabelBaseUrlHelp": "Du kan tilføje en speciel undermappe her for, at have adgang til serveren fra en mere unik URL.", + "LabelDroppedFrames": "Tabte frames:", + "LabelDeinterlaceMethod": "Konventerings metode:", + "LabelCorruptedFrames": "Korrupte frames:", + "LabelBitrate": "Bitrate:", + "LabelAuthProvider": "Autentificeringsudbyder:", + "LabelAudioSampleRate": "Lydeksempelfrekvens:", + "LabelAudioCodec": "Lyd codec:", + "LabelAudioChannels": "Lyd kanaler:", + "LabelAudioBitrate": "Lyd bitrate:", + "LabelAudioBitDepth": "Lyd bitdybde:", + "HeaderFavoritePeople": "Foretrukne Personer", + "HeaderFavoriteBooks": "Foretrukne Bøger", + "FetchingData": "Henter yderligere data", + "Episode": "Afsnit", + "DeinterlaceMethodHelp": "Vælg hvilken konverteringsmulighed der skal bruges til transkodning af indhold.", + "CopyStreamURLError": "Der skete en fejl med at kopiere URL'en.", + "CopyStreamURLSuccess": "URL blev kopieret.", + "CopyStreamURL": "Kopiér stream URL", + "ClientSettings": "Klient Indstillinger", + "ButtonSplit": "Opdel", + "BoxSet": "Box Set", + "AuthProviderHelp": "Vælg en godkendelsesudbyder til at godkende denne brugers adgangskode.", + "AskAdminToCreateLibrary": "Spørg en administrator om at oprette et bibliotek.", + "Artist": "Artist", + "EveryXHours": "Hver {0} time", + "OnApplicationStartup": "Ved programstart", + "UnsupportedPlayback": "Jellyfin kan ikke dekryptere indhold, der er beskyttet af DRM, men alt indhold vil blive forsøgt afspillet uanset, inklusive beskyttede titler. Nogle filer kan eventuelt vises med sort skærm på grund af kryptering eller andre funktioner, der ikke understøttes, såsom interaktive titler.", + "MessageUnauthorizedUser": "Du har ikke tilladelse til at tilgå serveren på dette tidspunkt. Kontakt din serveradministrator for mere information.", + "Filter": "Filtrer", + "New": "Nye", + "ButtonTogglePlaylist": "Spilleliste", + "ButtonToggleContextMenu": "Mere" } diff --git a/src/strings/de.json b/src/strings/de.json index 03d1f81ca7..daad4b05bf 100644 --- a/src/strings/de.json +++ b/src/strings/de.json @@ -9,7 +9,6 @@ "AddToPlayQueue": "Zur Warteschlange hinzufügen", "AddToPlaylist": "Zur Wiedergabeliste hinzufügen", "AddUser": "Benutzer anlegen", - "AddUserByManually": "Lege einen lokalen User durch manuelle Eingabe der User-Informationen an.", "AddedOnValue": "{0} hinzugefügt", "AdditionalNotificationServices": "Durchsuche den Pluginkatalog, um weitere Benachrichtigungsdienste zu installieren.", "AirDate": "Erstausstrahlung", @@ -17,7 +16,7 @@ "Albums": "Alben", "All": "Alle", "AllChannels": "Alle Kanäle", - "AllComplexFormats": "Alle komplexen Formate (ASS, SSA, VOBSUB, PGS, SUB/IDX)", + "AllComplexFormats": "Alle komplexen Formate (ASS, SSA, VOBSUB, PGS, SUB/IDX, ...)", "AllEpisodes": "Alle Folgen", "AllLanguages": "Alle Sprachen", "AllLibraries": "Alle Bibliotheken", @@ -29,14 +28,12 @@ "AllowOnTheFlySubtitleExtractionHelp": "Eingebettete Untertitel können aus Videos extrahiert und in Reintext an Clients gesendet werden, um eine Videotranskodierung zu vermeiden. Auf manchen Systemen kann dieser Vorgang eine lange Zeit in Anspruch nehmen und deswegen währenddessen die Videowiedergabe stoppen. Deaktiviere diese Option, um eingebettete Untertitel während des Videotranskodierens einbrennen zu lassen, wenn sie nicht nativ vom Client unterstützt werden.", "AllowRemoteAccess": "Erlaube externe Verbindungen zu diesem Jellyfin Server.", "AllowRemoteAccessHelp": "Wenn deaktiviert werden alle externen Verbindungen blockiert.", - "AllowSeasonalThemes": "Erlaube automatische Jahreszeitenmotive", - "AllowSeasonalThemesHelp": "Wenn aktiviert, werden Jahreszeitenmotive von Zeit zu Zeit deine Motiveinstellungen überschreiben.", "AllowedRemoteAddressesHelp": "Kommagetrennte Liste von IP Adressen oder IP/Netzmasken für Netzwerke, für die externe Verbindungen erlaubt sind. Wenn leer, sind alle Adressen erlaubt.", "AlwaysPlaySubtitles": "Immer anzeigen", "AlwaysPlaySubtitlesHelp": "Untertitel die den Spracheinstellungen entsprechen werden unabhängig von der Tonspursprache geladen.", "AnyLanguage": "Jede Sprache", "Anytime": "Jederzeit", - "AroundTime": "Um {0}", + "AroundTime": "Um", "Artists": "Interpreten", "AsManyAsPossible": "So viele wie möglich", "Ascending": "Aufsteigend", @@ -55,12 +52,12 @@ "BirthLocation": "Geburtsort", "BirthPlaceValue": "Geburtsort: {0}", "BobAndWeaveWithHelp": "Bob & Weave (höhere Qualität, aber langsamer)", - "BookLibraryHelp": "Hörbücher und E-Books werden unterstützt. Schaue in den {0}Book Naming Guide {1}.", + "BookLibraryHelp": "Hörbücher und E-Books werden unterstützt. Schaue in den {0} Book Naming Guide {1}.", "Books": "Bücher", "BoxRear": "Box (Rückseite)", "Browse": "Blättern", "BrowsePluginCatalogMessage": "Durchsuche unsere Bibliothek, um alle verfügbaren Plugins anzuzeigen.", - "BurnSubtitlesHelp": "Legt fest, ob der Server die Untertitel während der Videotranskodierung einbrennen soll. Deaktivieren verbessert die Serverperformance immens. Wähle Auto, um bildbasierte Formate (z.B. VOBSUB, PGS, SUB, IDX) sowie bestimmte ASS- oder SSA-Untertitel einbrennen zu lassen.", + "BurnSubtitlesHelp": "Legt fest, ob der Server die Untertitel während der Videotranskodierung einbrennen soll. Deaktivieren verbessert die Serverperformance immens. Wähle Auto, um bildbasierte Formate (z.B. VOBSUB, PGS, SUB, IDX, ...) sowie bestimmte ASS- oder SSA-Untertitel einbrennen zu lassen.", "ButtonAdd": "Hinzufügen", "ButtonAddMediaLibrary": "Füge Medienbibliothek hinzu", "ButtonAddScheduledTaskTrigger": "Auslöser hinzufügen", @@ -230,7 +227,7 @@ "ExitFullscreen": "Vollbild verlassen", "ExtraLarge": "Extragroß", "ExtractChapterImagesHelp": "Das Extrahieren von Kapitel-Bildern ermöglicht es Jellyfin-Apps eine grafische Szenenauswahl anzubieten. Das Erstellen ist recht langsam, rechenintensiv und erfordert ggf. einige Gigabyte an freien Speicherplatz. Diese Aufgabe startet wenn neue Videos erkannt werden und ebenso als eine nächtliche Aufgabe. Es wird nicht empfohlen diese Aufgabe in Zeiten hoher Server-Auslastung zu starten.", - "FFmpegSavePathNotFound": "Wir konnten kein FFmpeg in dem von Dir erfassten Verzeichnis finden. FFprobe wird ebenso benötigt und muss sich im gleichen Verzeichnis befinden. Diese Komponenten sind normalerweise in einem Paket vorhanden um kommen zusammen mit einem Download. Bitte prüfe das Verzeichnis und probiere es erneut.", + "FFmpegSavePathNotFound": "Wir konnten kein FFmpeg in dem von Dir erfassten Verzeichnis finden. FFprobe wird ebenso benötigt und muss sich im gleichen Verzeichnis befinden. Diese Komponenten sind normalerweise in einem Paket vorhanden und kommen zusammen in einem Download. Bitte prüfe das Verzeichnis und probiere es erneut.", "FastForward": "Vorwärts spulen", "Favorite": "Favorit", "Favorites": "Favoriten", @@ -512,7 +509,7 @@ "LabelBlockContentWithTags": "Blockiere Inhalte mit Tags:", "LabelBurnSubtitles": "Untertitel einbrennen:", "LabelCachePath": "Cache Pfad:", - "LabelCachePathHelp": "Legen Sie ein eigenes Verzeichnis für den Server Zwischenspeicher fest. (z.B. für Bilder) Lassen Sie dieses Feld leer um die Standardeinstellung zu verwenden.", + "LabelCachePathHelp": "Legen Sie ein eigenes Verzeichnis für den Server Zwischenspeicher fest (z.B. für Bilder). Lassen Sie dieses Feld leer um die Standardeinstellung zu verwenden.", "LabelCancelled": "Abgebrochen", "LabelCertificatePassword": "Zertifikat Passwort:", "LabelCertificatePasswordHelp": "Wenn Dein Zertifikat ein Passwort benötigt, gib es hier ein.", @@ -551,7 +548,7 @@ "LabelDisplayOrder": "Anzeigereihenfolge:", "LabelDisplaySpecialsWithinSeasons": "Zeige Sonderinhalt innerhalb der Staffel in der er ausgestrahlt wurde", "LabelDownMixAudioScale": "Audio Verstärkung bei Downmixing:", - "LabelDownMixAudioScaleHelp": "Erhöhe die Audiolautstärke beim Heruntermischen. Setzte auf 1 um die original Lautstärke zu erhalten.", + "LabelDownMixAudioScaleHelp": "Erhöhe die Audiolautstärke beim Heruntermischen. Setze auf 1, um die ursprüngliche Lautstärke beizubehalten.", "LabelDownloadLanguages": "Herunterzuladende Sprachen:", "LabelDropImageHere": "Fotos hierher ziehen oder klicken im zu browsen.", "LabelDropShadow": "Schlagschatten:", @@ -559,7 +556,7 @@ "LabelEmbedAlbumArtDidl": "Integrierte Alben-Cover in Didl", "LabelEmbedAlbumArtDidlHelp": "Einige Geräte bevorzugen diese Methode um Album Art darstellen zu können. Andere wiederum können evtl. nichts abspielen, wenn diese Funktion aktiviert ist.", "LabelEnableAutomaticPortMap": "Aktiviere das automatische Port-Mapping", - "LabelEnableAutomaticPortMapHelp": "Versuche automatisch den öffentlichen Port dem lokalen Port mit Hilfe von UPnP zuzuordnen. Dies kann mit einigen Router-Modellen nicht funktionieren. Die Änderungen werden erst nach einem Neustart des Server aktiv.", + "LabelEnableAutomaticPortMapHelp": "Leitet automatisch die öffentlichen Ports des Routers an die lokalen Ports des Servers mit Hilfe von UPnP weiter. Dies kann mit einigen Router-Modellen nicht funktionieren. Die Änderungen werden erst nach einem Neustart des Server aktiv.", "LabelEnableBlastAliveMessages": "Erzeuge Alive Meldungen", "LabelEnableBlastAliveMessagesHelp": "Aktiviere dies, wenn der Server nicht zuverlässig von anderen UPnP Geräten in ihrem Netzwerk erkannt wird.", "LabelEnableDlnaClientDiscoveryInterval": "Client-Entdeckungs Intervall (Sekunden)", @@ -569,7 +566,7 @@ "LabelEnableDlnaPlayTo": "Aktiviere DLNA Play To", "LabelEnableDlnaPlayToHelp": "Geräte in deinem Netzwerk erkennen und deren Fernsteuerung ermöglichen.", "LabelEnableDlnaServer": "DLNA-Server aktivieren", - "LabelEnableDlnaServerHelp": "Erlaubt UPnP Geräten in Ihrem Netzwerk Zugriff und Wiedergabe von Jellyfin Inhalten.", + "LabelEnableDlnaServerHelp": "Erlaubt UPnP Geräten in Ihrem Netzwerk den Zugriff und die Wiedergabe von Inhalten.", "LabelEnableHardwareDecodingFor": "Aktiviere Hardware-Decoding für:", "LabelEnableRealtimeMonitor": "Erlaube Echtzeitüberwachung", "LabelEnableRealtimeMonitorHelp": "Änderungen werden auf unterstützten Dateisystemen sofort übernommen.", @@ -666,7 +663,7 @@ "LabelMoviePrefix": "Filmpräfix:", "LabelMoviePrefixHelp": "Wenn ein Präfix in Filmtiteln angewendet wird, gib es hier ein damit Jellyfin es korrekt behandeln kann.", "LabelMovieRecordingPath": "Film Aufnahmepfad (Optional):", - "LabelMusicStreamingTranscodingBitrate": "Musik Transkodier Bitrate:", + "LabelMusicStreamingTranscodingBitrate": "Musik-Transkodierung Bitrate:", "LabelMusicStreamingTranscodingBitrateHelp": "Wähle die maximale Bitrate für das streamen von Musik.", "LabelNewName": "Neuer Name:", "LabelNewPassword": "Neues Passwort:", @@ -678,7 +675,7 @@ "LabelNumberOfGuideDays": "Anzahl von Tagen für die Programminformationen geladen werden sollen:", "LabelNumberOfGuideDaysHelp": "Das laden von zusätzlichen Programmdaten bietet einen besseren Überblick und die Möglichkeit weiter in die Zukunft zu planen. Aber es wird länger dauern alles herunterzuladen. Auto wählt auf Grundlage der Kanalanzahl.", "LabelOptionalNetworkPath": "(Optionaler) Gemeinsamer Netzwerkordner:", - "LabelOptionalNetworkPathHelp": "Wenn dieser Ordner in deinem Netzwerk geteilt wird, kann die Weitergabe des Netzwerkpfades Jellyfin Apps auf anderen Geräten direkten Zugang zu den Mediendateien ermöglichen.", + "LabelOptionalNetworkPathHelp": "Wenn dieser Ordner in deinem Netzwerk geteilt wird, kann die Weitergabe des Netzwerkpfades Jellyfin Apps auf anderen Geräten direkten Zugang zu den Mediendateien ermöglichen. Beispielsweise {0} oder {1}.", "LabelOriginalAspectRatio": "Original Seitenverhältnis:", "LabelOriginalTitle": "Original Titel:", "LabelOverview": "Übersicht:", @@ -1000,7 +997,7 @@ "OptionLikes": "Mag ich", "OptionMissingEpisode": "Fehlende Episoden", "OptionMonday": "Montag", - "OptionNew": "Neu...", + "OptionNew": "Neu…", "OptionNone": "Keines", "OptionOnAppStartup": "Bei Anwendungsstart", "OptionOnInterval": "Nach einem Intervall", @@ -1097,7 +1094,7 @@ "RefreshMetadata": "Aktualisiere Metadaten", "RefreshQueued": "Aktualisierung eingereiht.", "ReleaseDate": "Veröffentlichungsdatum", - "RememberMe": "Erinnere mich", + "RememberMe": "Angemeldet bleiben", "RemoveFromCollection": "Aus Sammlung entfernen", "RemoveFromPlaylist": "Von Wiedergabeliste entfernen", "Repeat": "Wiederholen", @@ -1400,7 +1397,7 @@ "Thumb": "Miniaturansicht", "TitleSupport": "Hilfe", "Whitelist": "Erlaubt", - "AuthProviderHelp": "Authentifizierungsanbieter auswählen, der zur Authentifizierung des Benutzerpassworts verwendet werden soll.", + "AuthProviderHelp": "Wähle einen Authentifizierungsanbieter aus, der zur Authentifizierung des Passworts dieses Benutzers verwendet werden soll.", "Features": "Funktionen", "HeaderFavoriteBooks": "Lieblingsbücher", "HeaderFavoriteMovies": "Lieblingsfilme", @@ -1430,7 +1427,7 @@ "OptionLoginAttemptsBeforeLockoutHelp": "Null (0) bedeutet den Standardwert von drei Versuchen für normale, sowie fünf für Administrator-Benutzer zu übernehmen. Ein Wert von -1 deaktiviert die Funktion.", "PasswordResetProviderHelp": "Wählen Sie einen Password Reset Provider, der verwendet werden soll, wenn dieser Benutzer ein Passwort zurücksetzen möchte", "Box": "Box", - "HeaderHome": "Home", + "HeaderHome": "Startseite", "LabelAudioCodec": "Audiocodec:", "LabelAudioChannels": "Audiokanäle:", "HeaderTypeImageFetchers": "{0} Bildquellen", @@ -1446,7 +1443,7 @@ "LabelTranscodingFramerate": "Transcodierrate:", "LabelAudioSampleRate": "Audio-Abtastrate:", "LabelBaseUrl": "Basis URL:", - "LabelBaseUrlHelp": "Du kannst hier ein benutzerdefiniertes Unterverzeichnis hinzufügen, um über eine eindeutige URL auf den Server zuzugreifen.", + "LabelBaseUrlHelp": "Fügt ein benutzerdefiniertes Unterverzeichnis zur Server-URL hinzu, zum Beispiel: http://example.com/<baseurl>", "LabelFolder": "Ordner:", "LabelPasswordResetProvider": "Anbieter zum Zurücksetzen des Passwortes:", "LabelPlayMethod": "Spielmethode:", @@ -1474,15 +1471,13 @@ "OptionRandom": "Zufällig", "TabNetworking": "Netzwerk", "VideoRange": "Videobereich", - "ButtonSplit": "Teilen", + "ButtonSplit": "Aufteilen", "SelectAdminUsername": "Bitte einen Benutzernamen für das Administrator-Konto auswählen.", "HeaderNavigation": "Navigation", "CopyStreamURLError": "Beim Kopieren der URL ist ein Fehler aufgetreten.", "MessageConfirmAppExit": "Wirklich verlassen?", "LabelVideoResolution": "Videoauflösung:", "LabelStreamType": "Streamtyp:", - "EnableFastImageFadeInHelp": "Aktiviere schnellere Einblendeanimation für geladene Bilder", - "EnableFastImageFadeIn": "Schnelle Bildeinblendung", "LabelPlayerDimensions": "Playerabmessungen:", "LabelDroppedFrames": "Verlorene Frames:", "LabelCorruptedFrames": "Fehlerhafte Frames:", @@ -1490,7 +1485,7 @@ "AskAdminToCreateLibrary": "Bitten Sie einen Administrator, eine Bibliothek zu erstellen.", "NoCreatedLibraries": "Sieht so aus als hättest du bis jetzt keine Bibliothek erstellt. {0}Möchtest du jetzt eine Bibliothek erstellen?{1}", "AllowFfmpegThrottling": "Transkodierung drosseln", - "PlaybackErrorNoCompatibleStream": "Es gab ein Problem bei der Erkennung des Wiedergabeprofils des Clients und der Server sendet kein kompatibles Format.", + "PlaybackErrorNoCompatibleStream": "Dieser Client ist nicht mit den Medien kompatibel und der Server sendet kein kompatibles Medienformat.", "AllowFfmpegThrottlingHelp": "Wenn eine Transkodierung oder ein Remux weit genug über die aktuelle Abspielposition fortgeschritten ist, pausiere sie sodass weniger Ressourcen verbraucht werden. Dies ist am nützlichsten, wenn wenig geskippt wird. Bei Wiedergabeproblemen sollte diese Option deaktiviert werden.", "ClientSettings": "Client Einstellungen", "OnApplicationStartup": "Beim Starten der Applikation", @@ -1515,5 +1510,66 @@ "Artist": "Künstler", "AlbumArtist": "Album Künstler", "Album": "Album", - "BoxSet": "Box Set" + "BoxSet": "Box Set", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "LabelLibraryPageSizeHelp": "Setzt die Anzahl der auf einer Seite angezeigten Objekte. Setze auf 0, um alle Elemente auf einer Seite anzuzeigen.", + "LabelLibraryPageSize": "Bibliothek Seiten Größe:", + "DeinterlaceMethodHelp": "Wähle die Deinterlacing-Methode zum Transkodieren von Inhalten im Zeilensprungverfahren (Interlace).", + "LabelDeinterlaceMethod": "Deinterlacing-Methode:", + "UnsupportedPlayback": "Jellyfin kann keine DRM-geschützten Inhalte entschlüsseln, aber es wird versucht, alle Inhalte unabhängig davon zu entschlüsseln, einschließlich geschützter Titel. Einige Dateien können aufgrund der Verschlüsselung oder anderer nicht unterstützter Funktionen, wie z.B. interaktive Titel, komplett schwarz erscheinen.", + "Filter": "Filter", + "New": "Neu", + "MessageUnauthorizedUser": "Sie sind im Moment nicht berechtigt, auf den Server zuzugreifen. Bitte kontaktieren Sie Ihren Server-Administrator für weitere Informationen.", + "HeaderFavoritePlaylists": "Lieblings-Wiedergabeliste", + "ButtonTogglePlaylist": "Wiedergabeliste", + "ButtonToggleContextMenu": "Mehr", + "ApiKeysCaption": "Liste der aktuell aktivierten API-Schlüssel", + "LabelNightly": "Nightly", + "LabelStable": "Stable", + "LabelChromecastVersion": "Chromecast Version", + "HeaderDVR": "DVR", + "TabDVR": "DVR", + "SaveChanges": "Änderungen speichern", + "LabelRequireHttpsHelp": "Wenn dies ausgewählt ist, leitet der Server alle Anfragen über HTTP an HTTPS weiter. Dies hat keinen Effekt, falls der Server nicht auf HTTPS hört.", + "LabelRequireHttps": "Erfordere HTTPS", + "LabelEnableHttpsHelp": "Erlaubt es dem Server, den konfigurierten HTTPS-Port zu beobachten. Damit dies geschehen kann, muss ein gültiges Zertifikat konfiguriert sein.", + "LabelEnableHttps": "Aktiviere HTTPS", + "HeaderServerAddressSettings": "Server-Adresseinstellungen", + "HeaderRemoteAccessSettings": "Fernzugriffs-Einstellungen", + "HeaderHttpsSettings": "HTTPS-Einstellungen", + "SyncPlayAccessHelp": "Wähle die Berechtigungsstufe, die dieser Benutzer auf das SyncPlay-Feature hat. SyncPlay ermöglicht die Synchronisierung der Wiedergabe mit anderen Geräten.", + "MessageSyncPlayErrorMedia": "SyncPlay konnte nicht aktiviert werden! Medienfehler.", + "MessageSyncPlayErrorMissingSession": "SyncPlay konnte nicht aktiviert werden! Fehlende Sitzung.", + "MessageSyncPlayErrorNoActivePlayer": "Keine aktive Wiedergabe gefunden. SyncPlay wurde deaktiviert.", + "MessageSyncPlayErrorAccessingGroups": "Beim Zugriff auf die Gruppen ist ein Fehler aufgetreten.", + "MessageSyncPlayLibraryAccessDenied": "Der Zugang zu diesem Inhalt ist beschränkt.", + "MessageSyncPlayJoinGroupDenied": "Eine Berechtigung ist erforderlich um SyncPlay zu benutzen.", + "MessageSyncPlayCreateGroupDenied": "Zum Erstellen einer Gruppe ist eine Genehmigung erforderlich.", + "MessageSyncPlayGroupDoesNotExist": "Konnte der Gruppe nicht beitreten, da sie nicht existiert.", + "MessageSyncPlayPlaybackPermissionRequired": "Wiedergabegenehmigung erforderlich.", + "MessageSyncPlayNoGroupsAvailable": "Keine Gruppen verfügbar. Fange an, etwas abzuspielen.", + "MessageSyncPlayGroupWait": "{0} ist am laden...", + "MessageSyncPlayUserLeft": "{0} hat die Gruppe verlassen.", + "MessageSyncPlayUserJoined": "{0} ist der Gruppe beigetreten.", + "MessageSyncPlayDisabled": "SyncPlay deaktiviert.", + "MessageSyncPlayEnabled": "SyncPlay aktiviert.", + "LabelSyncPlayAccess": "SyncPlay-Zugriff", + "LabelSyncPlayAccessNone": "Deaktiviert für diesen Benutzer", + "LabelSyncPlayAccessJoinGroups": "Erlaube dem Benutzer, Gruppen beizutreten", + "LabelSyncPlayAccessCreateAndJoinGroups": "Erlaube dem Benutzer, Gruppen zu erstellen und beizutreten", + "LabelSyncPlayLeaveGroupDescription": "Deaktiviere SyncPlay", + "LabelSyncPlayLeaveGroup": "Gruppe verlassen", + "LabelSyncPlayNewGroupDescription": "Erstelle eine neue Gruppe", + "LabelSyncPlayNewGroup": "Neue Gruppe", + "LabelSyncPlaySyncMethod": "Sync-Methode:", + "LabelSyncPlayPlaybackDiff": "Zeitversatz bei der Wiedergabe:", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Zeitversatz mit dem Server:", + "HeaderSyncPlayEnabled": "SyncPlay aktiviert", + "HeaderSyncPlaySelectGroup": "Tritt einer Gruppe bei", + "EnableDetailsBannerHelp": "Zeigt ein Bannerbild im oberen Bereich der Seite Item-Details.", + "EnableDetailsBanner": "Detailbanner", + "ShowMore": "Mehr anzeigen", + "ShowLess": "Weniger anzeigen" } diff --git a/src/strings/el.json b/src/strings/el.json index 3856480fd0..f4021436c7 100644 --- a/src/strings/el.json +++ b/src/strings/el.json @@ -10,7 +10,7 @@ "AdditionalNotificationServices": "Περιηγηθείτε στον κατάλογο plugin για να εγκαταστήσετε πρόσθετες υπηρεσίες ειδοποίησης.", "AirDate": "Ημερομηνία προβολής", "Aired": "Προβλήθηκε", - "Albums": "Άλμπουμ", + "Albums": "Άλμπουμς", "All": "Όλα", "AllChannels": "Όλα τα κανάλια", "AllComplexFormats": "Όλες οι σύνθετες μορφές (ASS, SSA, VOBSUB, PGS, SUB / IDX κ.λπ.)", @@ -19,7 +19,7 @@ "AllLibraries": "Όλες οι βιβλιοθήκες", "AllowRemoteAccess": "Να επιτρέπονται οι απομακρυσμένες συνδέσεις σε αυτόν το διακομιστή Jellyfin.", "AllowRemoteAccessHelp": "Εάν δεν επιλεχθεί, όλες οι απομακρυσμένες συνδέσεις θα αποκλειστούν.", - "AlwaysPlaySubtitles": "Πάντα αναπαραγωγή Υποτίτλων", + "AlwaysPlaySubtitles": "Παίξτε πάντα", "AlwaysPlaySubtitlesHelp": "Οι υπότιτλοι που ταιριάζουν με τις προτιμήσεις γλώσσας θα φορτωθούν ανεξάρτητα από τη γλώσσα του ήχου.", "AnyLanguage": "Οποιαδήποτε γλώσσα", "Anytime": "Οποτεδήποτε", @@ -28,7 +28,7 @@ "Artists": "Καλλιτέχνες", "AsManyAsPossible": "Οσο το δυνατον περισσοτερα", "Ascending": "Αύξουσα", - "AspectRatio": "Αρχικός λόγος διαστάσεων", + "AspectRatio": "Αναλογία απεικόνισης", "AttributeNew": "Νέο", "Audio": "Ήχος", "Auto": "Αυτόματο", @@ -1224,6 +1224,17 @@ "LabelServerName": "Όνομα Διακομιστή:", "ButtonAddImage": "Προσθήκη Εικόνας", "BoxRear": "Κουτί(πίσω)", - "BookLibraryHelp": "Ήχος και βιβλία υποστηρίζονται.Ελέγξτε τον {0}ονομαστικό οδηγό βιβλίων{1}.", - "AuthProviderHelp": "Επιλέξτε ένα Πάροχο Επαλήθευσης για να επαληθεύσετε το κωδικό αυτού του χρήστη." + "BookLibraryHelp": "Υποστήριξη ήχου και βιβλίων κειμένου. Εξετάστε τον {0}οδηγό ονομάτων βιβλίου{1}.", + "AuthProviderHelp": "Επιλέξτε ένα Πάροχο Επαλήθευσης για να επαληθεύσετε το κωδικό αυτού του χρήστη.", + "AllowFfmpegThrottling": "Επιτάχυνση Διακωδικοποιησής", + "AlbumArtist": "Άλμπουμ Καλλιτέχνη", + "Album": "Άλμπουμ", + "BoxSet": "Σετ Κουτιού", + "AskAdminToCreateLibrary": "Ζητήστε από έναν διαχειριστή να δημιουργήσει μια βιβλιοθήκη.", + "Artist": "Καλλιτέχνης", + "AllowedRemoteAddressesHelp": "Λίστα διαχωρισμένων διευθύνσεων IP ή καταχωρίσεων IP / netmask για δίκτυα που θα επιτρέπεται η σύνδεση εξ αποστάσεως. Εάν αφεθεί κενό, όλες οι απομακρυσμένες διευθύνσεις θα επιτρέπονται.", + "AllowFfmpegThrottlingHelp": "Όταν ένας διακωδικοποιητής ή remux φτάσει αρκετά μπροστά από την τρέχουσα θέση αναπαραγωγής, διακόψτε τη διαδικασία ώστε να καταναλώσει λιγότερους πόρους. Αυτό είναι πιο χρήσιμο όταν παρακολουθείτε χωρίς να αναζητάτε συχνά. Απενεργοποιήστε το εάν αντιμετωπίζετε προβλήματα αναπαραγωγής.", + "ButtonTogglePlaylist": "Λίστα αναπαραγωγής", + "ButtonToggleContextMenu": "Περισσότερα", + "ButtonSplit": "Διαχωρισμός" } diff --git a/src/strings/en-gb.json b/src/strings/en-gb.json index 757ea55266..3ce9a10476 100644 --- a/src/strings/en-gb.json +++ b/src/strings/en-gb.json @@ -75,9 +75,9 @@ "AllowRemoteAccess": "Allow remote connections to this Jellyfin Server.", "AllowRemoteAccessHelp": "If unchecked, all remote connections will be blocked.", "AllowedRemoteAddressesHelp": "Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. If left blank, all remote addresses will be allowed.", - "AlwaysPlaySubtitles": "Always play subtitles", + "AlwaysPlaySubtitles": "Always Play", "AlwaysPlaySubtitlesHelp": "Subtitles matching the language preference will be loaded regardless of the audio language.", - "AnyLanguage": "Any language", + "AnyLanguage": "Any Language", "Anytime": "Anytime", "AroundTime": "Around {0}", "Art": "Art", @@ -100,7 +100,7 @@ "Box": "Box", "BoxRear": "Box (rear)", "Browse": "Browse", - "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when converting video depending on the subtitle format. Avoiding burning in subtitles will improve server performance. Select Auto to burn image based formats (VOBSUB, PGS, SUB/IDX, etc) and certain ASS/SSA subtitles.", + "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when transcoding videos. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VOBSUB, PGS, SUB, IDX) and certain ASS or SSA subtitles.", "ButtonAdd": "Add", "ButtonAddMediaLibrary": "Add Media Library", "ButtonAddScheduledTaskTrigger": "Add Trigger", @@ -233,7 +233,7 @@ "DisplayInOtherHomeScreenSections": "Display in home screen sections such as latest media and continue watching", "DisplayMissingEpisodesWithinSeasons": "Display missing episodes within seasons", "DisplayMissingEpisodesWithinSeasonsHelp": "This must also be enabled for TV libraries in the server configuration.", - "DisplayModeHelp": "Select the type of screen you're running Jellyfin on.", + "DisplayModeHelp": "Select the layout style you want for the interface.", "DoNotRecord": "Do not record", "Down": "Down", "Download": "Download", @@ -385,6 +385,7 @@ "HeaderFavoriteArtists": "Favourite Artists", "HeaderFavoriteSongs": "Favourite Songs", "HeaderFavoriteVideos": "Favourite Videos", + "HeaderFavoritePlaylists": "Favourite Playlists", "HeaderFeatureAccess": "Feature Access", "HeaderFeatures": "Features", "HeaderFetchImages": "Fetch Images:", @@ -527,6 +528,8 @@ "Smart": "Smart", "SimultaneousConnectionLimitHelp": "The maximum number of allowed simultaneous streams. Enter 0 for no limit.", "Shuffle": "Shuffle", + "New": "New", + "Filter": "Filter", "ShowYear": "Show year", "ShowIndicatorsFor": "Show indicators for:", "ShowAdvancedSettings": "Show advanced settings", @@ -569,7 +572,7 @@ "Repeat": "Repeat", "RemoveFromPlaylist": "Remove from playlist", "RemoveFromCollection": "Remove from collection", - "RememberMe": "Remember me", + "RememberMe": "Remember Me", "ReleaseDate": "Release date", "RefreshMetadata": "Refresh metadata", "RefreshDialogHelp": "Metadata is refreshed based on settings and internet services that are enabled in the Jellyfin Server dashboard.", @@ -617,9 +620,9 @@ "PasswordResetComplete": "The password has been reset.", "PasswordMatchError": "Password and password confirmation must match.", "ParentalRating": "Parental rating", - "PackageInstallFailed": "{0} installation failed.", - "PackageInstallCompleted": "{0} installation completed.", - "PackageInstallCancelled": "{0} installation cancelled.", + "PackageInstallFailed": "{0} (version {1}) installation failed.", + "PackageInstallCompleted": "{0} (version {1}) installation completed.", + "PackageInstallCancelled": "{0} (version {1}) installation cancelled.", "OriginalAirDateValue": "Original air date: {0}", "OptionWeekly": "Weekly", "OptionWeekends": "Weekends", @@ -657,7 +660,7 @@ "OptionOnInterval": "On an interval", "OptionOnAppStartup": "On application startup", "OptionNone": "None", - "OptionNew": "New...", + "OptionNew": "New…", "OptionMissingEpisode": "Missing Episodes", "OptionMax": "Max", "OptionLoginAttemptsBeforeLockoutHelp": "A value of zero means inheriting the default of three attempts for normal users and five for administrators. Setting this to -1 will disable the feature.", @@ -690,12 +693,12 @@ "OptionAlbumArtist": "Album Artist", "OptionAlbum": "Album", "Option3D": "3D", - "OnlyImageFormats": "Only image formats (VOBSUB, PGS, SUB, etc)", + "OnlyImageFormats": "Only Image Formats (VOBSUB, PGS, SUB)", "OnlyForcedSubtitlesHelp": "Only subtitles marked as forced will be loaded.", "Normal": "Normal", "None": "None", "NoSubtitlesHelp": "Subtitles will not be loaded by default. They can still be turned on manually during playback.", - "NoSubtitles": "No subtitles", + "NoSubtitles": "None", "NoPluginConfigurationMessage": "This plugin has no settings to configure.", "NoNextUpItemsMessage": "None found. Start watching your shows!", "No": "No", @@ -834,8 +837,8 @@ "LabelSecureConnectionsMode": "Secure connection mode:", "LabelSeasonNumber": "Season number:", "LabelScreensaver": "Screensaver:", - "EnableFastImageFadeIn": "Fast image fade-in", - "EnableFastImageFadeInHelp": "Enable faster fade-in animation for loaded images", + "EnableFasterAnimations": "Faster animations", + "EnableFasterAnimationsHelp": "Use faster animations and transitions", "LabelScheduledTaskLastRan": "Last ran {0}, taking {1}.", "LabelSaveLocalMetadataHelp": "Saving artwork into media folders will put them in a place where they can be easily edited.", "LabelRuntimeMinutes": "Run time (minutes):", @@ -871,7 +874,7 @@ "MessageConfirmProfileDeletion": "Are you sure you wish to delete this profile?", "LaunchWebAppOnStartup": "Launch the web interface when starting the server", "LabelYourFirstName": "Your first name:", - "OnlyForcedSubtitles": "Only forced subtitles", + "OnlyForcedSubtitles": "Only Forced", "Off": "Off", "NumLocationsValue": "{0} folders", "Name": "Name", @@ -1029,8 +1032,8 @@ "LabelEnableDlnaServerHelp": "Allows UPnP devices on your network to browse and play content.", "LabelEnableDlnaDebugLoggingHelp": "Create large log files and should only be used as needed for troubleshooting purposes.", "LabelEnableDlnaClientDiscoveryIntervalHelp": "Determines the duration in seconds between SSDP searches performed by Jellyfin.", - "LabelEnableAutomaticPortMapHelp": "Attempt to automatically map the public port to the local port via UPnP. This may not work with some router models. Changes will not apply until after a server restart.", - "InstallingPackage": "Installing {0}", + "LabelEnableAutomaticPortMapHelp": "Automatically forward public ports on your router to local ports on your server via UPnP. This may not work with some router models or network configurations. Changes will not apply until after a server restart.", + "InstallingPackage": "Installing {0} (version {1})", "ImportMissingEpisodesHelp": "If enabled, information about missing episodes will be imported into your Jellyfin database and displayed within seasons and series. This may cause significantly longer library scans.", "HeaderSubtitleAppearance": "Subtitle Appearance", "LabelProtocol": "Protocol:", @@ -1115,7 +1118,7 @@ "LabelForgotPasswordUsernameHelp": "Enter your username, if you remember it.", "LabelFont": "Font:", "LabelExtractChaptersDuringLibraryScanHelp": "Generate chapter images when videos are imported during the library scan. Otherwise, they will be extracted during the chapter images scheduled task, allowing the regular library scan to complete faster.", - "LabelBaseUrlHelp": "You can add a custom subdirectory here to access the server from a more unique URL.", + "LabelBaseUrlHelp": "Adds a custom subdirectory to the server URL. For example: http://example.com/<baseurl>", "LabelEveryXMinutes": "Every:", "LabelEvent": "Event:", "LabelEpisodeNumber": "Episode number:", @@ -1472,7 +1475,45 @@ "CopyStreamURLError": "There was an error copying the URL.", "NoCreatedLibraries": "Seems like you haven't created any libraries yet. {0}Would you like to create one now?{1}", "AskAdminToCreateLibrary": "Ask an administrator to create a library.", - "PlaybackErrorNoCompatibleStream": "There was an issue with the client profiling and the server isn't sending a compatible media format.", + "PlaybackErrorNoCompatibleStream": "This client isn't compatible with the media and the server isn't sending a compatible media format.", "AllowFfmpegThrottlingHelp": "When a transcode or remux gets far enough ahead from the current playback position, pause the process so it will consume less resources. This is most useful when watching without seeking often. Turn this off if you experience playback issues.", - "AllowFfmpegThrottling": "Throttle Transcodes" + "AllowFfmpegThrottling": "Throttle Transcodes", + "OnApplicationStartup": "On application startup", + "EveryXHours": "Every {0} hours", + "EveryHour": "Every hour", + "EveryXMinutes": "Every {0} minutes", + "OnWakeFromSleep": "On wake from sleep", + "WeeklyAt": "{0}s at {1}", + "DailyAt": "Daily at {0}", + "LastSeen": "Last seen {0}", + "PersonRole": "as {0}", + "ListPaging": "{0}-{1} of {2}", + "WriteAccessRequired": "Jellyfin Server requires write access to this folder. Please ensure write access and try again.", + "PathNotFound": "The path could not be found. Please ensure the path is valid and try again.", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "Track": "Track", + "Season": "Season", + "ReleaseGroup": "Release Group", + "PreferEmbeddedEpisodeInfosOverFileNames": "Prefer embedded episode information over filenames", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "This uses the episode information from the embedded metadata if available.", + "Person": "Person", + "OtherArtist": "Other Artist", + "Movie": "Movie", + "LabelLibraryPageSizeHelp": "Sets the amount of items to show on a library page. Set to 0 in order to disable paging.", + "LabelLibraryPageSize": "Library page size:", + "LabelDeinterlaceMethod": "Deinterlacing method:", + "Episode": "Episode", + "DeinterlaceMethodHelp": "Select the deinterlacing method to use when transcoding interlaced content.", + "ClientSettings": "Client Settings", + "BoxSet": "Box Set", + "Artist": "Artist", + "AlbumArtist": "Album Artist", + "Album": "Album", + "UnsupportedPlayback": "Jellyfin cannot decrypt content protected by DRM but all content will be attempted regardless, including protected titles. Some files may appear completely black due to encryption or other unsupported features, such as interactive titles.", + "MessageUnauthorizedUser": "You are not authorized to access the server at this time. Please contact your server administrator for more information.", + "ButtonTogglePlaylist": "Playlist", + "ButtonToggleContextMenu": "More", + "HeaderDVR": "DVR", + "ApiKeysCaption": "List of the currently enabled API keys" } diff --git a/src/strings/en-us.json b/src/strings/en-us.json index a3b4257ae8..102d7d65b1 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -17,7 +17,7 @@ "Alerts": "Alerts", "All": "All", "AllChannels": "All channels", - "AllComplexFormats": "All Complex Formats (ASS, SSA, VOBSUB, PGS, SUB, IDX)", + "AllComplexFormats": "All Complex Formats (ASS, SSA, VOBSUB, PGS, SUB, IDX, …)", "AllEpisodes": "All episodes", "AllLanguages": "All languages", "AllLibraries": "All libraries", @@ -25,7 +25,7 @@ "AllowMediaConversion": "Allow media conversion", "AllowMediaConversionHelp": "Grant or deny access to the convert media feature.", "AllowOnTheFlySubtitleExtraction": "Allow subtitle extraction on the fly", - "AllowOnTheFlySubtitleExtractionHelp": "Embedded subtitles can be extracted from videos and delivered to clients in plain text in order to help prevent video transcoding. On some systems this can take a long time and cause video playback to stall during the extraction process. Disable this to have embedded subtitles burned in with video transcoding when they are not natively supported by the client device.", + "AllowOnTheFlySubtitleExtractionHelp": "Embedded subtitles can be extracted from videos and delivered to clients in plain text, in order to help prevent video transcoding. On some systems this can take a long time and cause video playback to stall during the extraction process. Disable this to have embedded subtitles burned in with video transcoding when they are not natively supported by the client device.", "AllowFfmpegThrottling": "Throttle Transcodes", "AllowFfmpegThrottlingHelp": "When a transcode or remux gets far enough ahead from the current playback position, pause the process so it will consume less resources. This is most useful when watching without seeking often. Turn this off if you experience playback issues.", "AllowRemoteAccess": "Allow remote connections to this Jellyfin Server.", @@ -35,7 +35,7 @@ "AlwaysPlaySubtitlesHelp": "Subtitles matching the language preference will be loaded regardless of the audio language.", "AnyLanguage": "Any Language", "Anytime": "Anytime", - "AroundTime": "Around {0}", + "AroundTime": "Around", "Art": "Art", "Artist": "Artist", "Artists": "Artists", @@ -55,14 +55,14 @@ "BirthLocation": "Birth location", "BirthPlaceValue": "Birth place: {0}", "Blacklist": "Blacklist", - "BookLibraryHelp": "Audio and text books are supported. Review the {0}book naming guide{1}.", + "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.", - "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when transcoding videos. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VOBSUB, PGS, SUB, IDX) and certain ASS or SSA subtitles.", + "BurnSubtitlesHelp": "Determines if the server should burn in subtitles when transcoding videos. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VOBSUB, PGS, SUB, IDX, …) and certain ASS or SSA subtitles.", "ButtonAdd": "Add", "ButtonAddImage": "Add Image", "ButtonAddMediaLibrary": "Add Media Library", @@ -137,6 +137,8 @@ "ButtonSplit": "Split", "ButtonSubmit": "Submit", "ButtonSubtitles": "Subtitles", + "ButtonToggleContextMenu": "More", + "ButtonTogglePlaylist": "Playlist", "ButtonTrailer": "Trailer", "ButtonUninstall": "Uninstall", "ButtonUp": "Up", @@ -237,6 +239,8 @@ "EnableThemeSongsHelp": "Play theme songs in the background while browsing the library.", "EnableThemeVideos": "Theme videos", "EnableThemeVideosHelp": "Play theme videos in the background while browsing the library.", + "EnableDetailsBanner": "Details Banner", + "EnableDetailsBannerHelp": "Display a banner image at the top of the item details page.", "Ended": "Ended", "EndsAtValue": "Ends at {0}", "Episode": "Episode", @@ -288,7 +292,6 @@ "H264CrfHelp": "The Constant Rate Factor (CRF) is the default quality setting for the x264 encoder. You can set the values between 0 and 51, where lower values would result in better quality (at the expense of higher file sizes). Sane values are between 18 and 28. The default for x264 is 23, so you can use this as a starting point.", "EncoderPresetHelp": "Choose a faster value to improve performance, or a slower value to improve quality.", "HDPrograms": "HD programs", - "HandledByProxy": "Handled by reverse proxy", "HardwareAccelerationWarning": "Enabling hardware acceleration may cause instability in some environments. Ensure that your operating system and video drivers are fully up to date. If you have difficulty playing video after enabling this, you'll need to change the setting back to None.", "HeaderAccessSchedule": "Access Schedule", "HeaderAccessScheduleHelp": "Create an access schedule to limit access to certain hours.", @@ -309,6 +312,7 @@ "HeaderApiKey": "API Key", "HeaderApiKeys": "API Keys", "HeaderApiKeysHelp": "External applications are required to have an API key in order to communicate with Jellyfin Server. Keys are issued by logging in with a Jellyfin account, or by manually granting the application a key.", + "ApiKeysCaption": "List of the currently enabled API keys", "HeaderApp": "App", "HeaderAppearsOn": "Appears On", "HeaderAudioBooks": "Audio Books", @@ -352,6 +356,7 @@ "HeaderDirectPlayProfileHelp": "Add direct play profiles to indicate which formats the device can handle natively.", "HeaderDisplay": "Display", "HeaderDownloadSync": "Download & Sync", + "HeaderDVR": "DVR", "HeaderEasyPinCode": "Easy Pin Code", "HeaderEditImages": "Edit Images", "HeaderEnabledFields": "Enabled Fields", @@ -368,6 +373,7 @@ "HeaderFavoriteArtists": "Favorite Artists", "HeaderFavoriteSongs": "Favorite Songs", "HeaderFavoriteVideos": "Favorite Videos", + "HeaderFavoritePlaylists": "Favorite Playlists", "HeaderFeatureAccess": "Feature Access", "HeaderFeatures": "Features", "HeaderFetchImages": "Fetch Images:", @@ -380,6 +386,7 @@ "HeaderGuideProviders": "TV Guide Data Providers", "HeaderHome": "Home", "HeaderHttpHeaders": "HTTP Headers", + "HeaderHttpsSettings": "HTTPS Settings", "HeaderIdentification": "Identification", "HeaderIdentificationCriteriaHelp": "Enter at least one identification criteria.", "HeaderIdentificationHeader": "Identification Header", @@ -446,6 +453,7 @@ "HeaderRecentlyPlayed": "Recently Played", "HeaderRecordingOptions": "Recording Options", "HeaderRecordingPostProcessing": "Recording Post Processing", + "HeaderRemoteAccessSettings": "Remote Access Settings", "HeaderRemoteControl": "Remote Control", "HeaderRemoveMediaFolder": "Remove Media Folder", "HeaderRemoveMediaLocation": "Remove Media Location", @@ -472,6 +480,7 @@ "HeaderSeries": "Series", "HeaderSeriesOptions": "Series Options", "HeaderSeriesStatus": "Series Status", + "HeaderServerAddressSettings": "Server Address Settings", "HeaderServerSettings": "Server Settings", "HeaderSettings": "Settings", "HeaderSetupLibrary": "Setup your media libraries", @@ -488,6 +497,8 @@ "HeaderSubtitleProfile": "Subtitle Profile", "HeaderSubtitleProfiles": "Subtitle Profiles", "HeaderSubtitleProfilesHelp": "Subtitle profiles describe the subtitle formats supported by the device.", + "HeaderSyncPlaySelectGroup": "Join a group", + "HeaderSyncPlayEnabled": "SyncPlay enabled", "HeaderSystemDlnaProfiles": "System Profiles", "HeaderTags": "Tags", "HeaderTaskTriggers": "Task Triggers", @@ -626,7 +637,7 @@ "LabelEmbedAlbumArtDidl": "Embed album art in Didl", "LabelEmbedAlbumArtDidlHelp": "Some devices prefer this method for obtaining album art. Others may fail to play with this option enabled.", "LabelEnableAutomaticPortMap": "Enable automatic port mapping", - "LabelEnableAutomaticPortMapHelp": "Attempt to automatically map the public port to the local port via UPnP. This may not work with some router models. Changes will not apply until after a server restart.", + "LabelEnableAutomaticPortMapHelp": "Automatically forward public ports on your router to local ports on your server via UPnP. This may not work with some router models or network configurations. Changes will not apply until after a server restart.", "LabelEnableBlastAliveMessages": "Blast alive messages", "LabelEnableBlastAliveMessagesHelp": "Enable this if the server is not detected reliably by other UPnP devices on your network.", "LabelEnableDlnaClientDiscoveryInterval": "Client discovery interval (seconds)", @@ -638,6 +649,8 @@ "LabelEnableDlnaServer": "Enable DLNA server", "LabelEnableDlnaServerHelp": "Allows UPnP devices on your network to browse and play content.", "LabelEnableHardwareDecodingFor": "Enable hardware decoding for:", + "LabelEnableHttps": "Enable HTTPS", + "LabelEnableHttpsHelp": "Enables the server to listen on the configured HTTPS port. A valid certificate must also be configured in order for this to take effect.", "LabelEnableRealtimeMonitor": "Enable real time monitoring", "LabelEnableRealtimeMonitorHelp": "Changes to files will be processed immediately, on supported file systems.", "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", @@ -647,7 +660,7 @@ "LabelEvent": "Event:", "LabelEveryXMinutes": "Every:", "LabelBaseUrl": "Base URL:", - "LabelBaseUrlHelp": "You can add a custom subdirectory here to access the server from a more unique URL.", + "LabelBaseUrlHelp": "Adds a custom subdirectory to the server URL. For example: http://example.com/<baseurl>", "LabelExtractChaptersDuringLibraryScan": "Extract chapter images during the library scan", "LabelExtractChaptersDuringLibraryScanHelp": "Generate chapter images when videos are imported during the library scan. Otherwise, they will be extracted during the chapter images scheduled task, allowing the regular library scan to complete faster.", "LabelFailed": "Failed", @@ -694,6 +707,8 @@ "LabelKodiMetadataUserHelp": "Save watch data to NFO files for other applications to utilize.", "LabelLanNetworks": "LAN networks:", "LabelLanguage": "Language:", + "LabelLibraryPageSize": "Library page size:", + "LabelLibraryPageSizeHelp": "Sets the amount of items to show on a library page. Set to 0 in order to disable paging.", "LabelLineup": "Lineup:", "LabelLocalHttpServerPortNumber": "Local HTTP port number:", "LabelLocalHttpServerPortNumberHelp": "The TCP port number that Jellyfin's HTTP server should bind to.", @@ -742,6 +757,9 @@ "LabelMusicStreamingTranscodingBitrate": "Music transcoding bitrate:", "LabelMusicStreamingTranscodingBitrateHelp": "Specify a max bitrate when streaming music.", "LabelName": "Name:", + "LabelChromecastVersion": "Chromecast Version", + "LabelStable": "Stable", + "LabelNightly": "Nightly", "LabelNewName": "New name:", "LabelNewPassword": "New password:", "LabelNewPasswordConfirm": "New password confirm:", @@ -752,7 +770,7 @@ "LabelNumberOfGuideDays": "Number of days of guide data to download:", "LabelNumberOfGuideDaysHelp": "Downloading more days worth of guide data provides the ability to schedule out further in advance and view more listings, but it will also take longer to download. Auto will choose based on the number of channels.", "LabelOptionalNetworkPath": "(Optional) Shared network folder:", - "LabelOptionalNetworkPathHelp": "If this folder is shared on your network, supplying the network share path can allow Jellyfin apps on other devices to access media files directly.", + "LabelOptionalNetworkPathHelp": "If this folder is shared on your network, supplying the network share path can allow Jellyfin apps on other devices to access media files directly. For example, {0} or {1}.", "LabelOriginalAspectRatio": "Original aspect ratio:", "LabelOriginalTitle": "Original title:", "LabelOverview": "Overview:", @@ -801,15 +819,16 @@ "LabelReleaseDate": "Release date:", "LabelRemoteClientBitrateLimit": "Internet streaming bitrate limit (Mbps):", "LabelRemoteClientBitrateLimitHelp": "An optional per-stream bitrate limit for all out of network devices. This is useful to prevent devices from requesting a higher bitrate than your internet connection can handle. This may result in increased CPU load on your server in order to transcode videos on the fly to a lower bitrate.", + "LabelRequireHttps": "Require HTTPS", + "LabelRequireHttpsHelp": "If checked, the server will automatically redirect all requests over HTTP to HTTPS. This has no effect if the server is not listening on HTTPS.", "LabelRuntimeMinutes": "Run time (minutes):", "LabelSaveLocalMetadata": "Save artwork into media folders", "LabelSaveLocalMetadataHelp": "Saving artwork into media folders will put them in a place where they can be easily edited.", "LabelScheduledTaskLastRan": "Last ran {0}, taking {1}.", "LabelScreensaver": "Screensaver:", - "EnableFastImageFadeIn": "Fast image fade-in", - "EnableFastImageFadeInHelp": "Enable faster fade-in animation for loaded images", + "EnableFasterAnimations": "Faster animations", + "EnableFasterAnimationsHelp": "Use faster animations and transitions", "LabelSeasonNumber": "Season number:", - "LabelSecureConnectionsMode": "Secure connection mode:", "LabelSelectFolderGroups": "Automatically group content from the following folders into views such as Movies, Music and TV:", "LabelSelectFolderGroupsHelp": "Folders that are unchecked will be displayed by themselves in their own view.", "LabelSelectUsers": "Select users:", @@ -848,6 +867,18 @@ "LabelSubtitlePlaybackMode": "Subtitle mode:", "LabelSubtitles": "Subtitles", "LabelSupportedMediaTypes": "Supported Media Types:", + "LabelSyncPlayTimeOffset": "Time offset with the server:", + "MillisecondsUnit": "ms", + "LabelSyncPlayPlaybackDiff": "Playback time difference:", + "LabelSyncPlaySyncMethod": "Sync method:", + "LabelSyncPlayNewGroup": "New group", + "LabelSyncPlayNewGroupDescription": "Create a new group", + "LabelSyncPlayLeaveGroup": "Leave group", + "LabelSyncPlayLeaveGroupDescription": "Disable SyncPlay", + "LabelSyncPlayAccessCreateAndJoinGroups": "Allow user to create and join groups", + "LabelSyncPlayAccessJoinGroups": "Allow user to join groups", + "LabelSyncPlayAccessNone": "Disabled for this user", + "LabelSyncPlayAccess": "SyncPlay access", "LabelTVHomeScreen": "TV mode home screen:", "LabelTag": "Tag:", "LabelTagline": "Tagline:", @@ -976,7 +1007,6 @@ "MessageCreateAccountAt": "Create an account at {0}", "MessageDeleteTaskTrigger": "Are you sure you wish to delete this task trigger?", "MessageDirectoryPickerBSDInstruction": "For BSD, you may need to configure storage within your FreeNAS Jail in order to allow Jellyfin to access it.", - "MessageDirectoryPickerInstruction": "Network paths can be entered manually in the event the Network button fails to locate your devices. For example, {0} or {1}.", "MessageDirectoryPickerLinuxInstruction": "For Linux on Arch Linux, CentOS, Debian, Fedora, openSUSE, or Ubuntu, you must grant the service user at least read access to your storage locations.", "MessageDownloadQueued": "Download queued.", "MessageEnablingOptionLongerScans": "Enabling this option may result in significantly longer library scans.", @@ -988,6 +1018,7 @@ "MessageInstallPluginFromApp": "This plugin must be installed from within the app you intend to use it in.", "MessageInvalidForgotPasswordPin": "An invalid or expired pin code was entered. Please try again.", "MessageInvalidUser": "Invalid username or password. Please try again.", + "MessageUnauthorizedUser": "You are not authorized to access the server at this time. Please contact your server administrator for more information.", "MessageItemSaved": "Item saved.", "MessageItemsAdded": "Items added.", "MessageLeaveEmptyToInherit": "Leave empty to inherit settings from a parent item or the global default value.", @@ -1010,6 +1041,21 @@ "MessageUnableToConnectToServer": "We're unable to connect to the selected server right now. Please ensure it is running and try again.", "MessageUnsetContentHelp": "Content will be displayed as plain folders. For best results use the metadata manager to set the content types of sub-folders.", "MessageYouHaveVersionInstalled": "You currently have version {0} installed.", + "MessageSyncPlayEnabled": "SyncPlay enabled.", + "MessageSyncPlayDisabled": "SyncPlay disabled.", + "MessageSyncPlayUserJoined": "{0} has joined the group.", + "MessageSyncPlayUserLeft": "{0} has left the group.", + "MessageSyncPlayGroupWait": "{0} is buffering...", + "MessageSyncPlayNoGroupsAvailable": "No groups available. Start playing something first.", + "MessageSyncPlayPlaybackPermissionRequired": "Playback permission required.", + "MessageSyncPlayGroupDoesNotExist": "Failed to join group because it does not exist.", + "MessageSyncPlayCreateGroupDenied": "Permission required to create a group.", + "MessageSyncPlayJoinGroupDenied": "Permission required to use SyncPlay.", + "MessageSyncPlayLibraryAccessDenied": "Access to this content is restricted.", + "MessageSyncPlayErrorAccessingGroups": "An error occurred while accessing groups list.", + "MessageSyncPlayErrorNoActivePlayer": "No active player found. SyncPlay has been disabled.", + "MessageSyncPlayErrorMissingSession": "Failed to enable SyncPlay! Missing session.", + "MessageSyncPlayErrorMedia": "Failed to enable SyncPlay! Media error.", "Metadata": "Metadata", "MetadataManager": "Metadata Manager", "MetadataSettingChangeHelp": "Changing metadata settings will affect new content that is added going forward. To refresh existing content, open the detail screen and click the refresh button, or perform bulk refreshes using the metadata manager.", @@ -1160,7 +1206,7 @@ "OptionMissingEpisode": "Missing Episodes", "OptionMonday": "Monday", "OptionNameSort": "Name", - "OptionNew": "New...", + "OptionNew": "New…", "OptionNone": "None", "OptionOnAppStartup": "On application startup", "OptionOnInterval": "On an interval", @@ -1248,9 +1294,8 @@ "PluginInstalledMessage": "The plugin has been successfully installed. Jellyfin Server will need to be restarted for changes to take effect.", "PreferEmbeddedTitlesOverFileNames": "Prefer embedded titles over filenames", "PreferEmbeddedTitlesOverFileNamesHelp": "This determines the default display title when no internet metadata or local metadata is available.", - "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "This uses the episode informations from the embedded metadata if available.", - "PreferEmbeddedEpisodeInfosOverFileNames": "Prefer embedded episode informations over filenames", - "PreferredNotRequired": "Preferred, but not required", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "This uses the episode information from the embedded metadata if available.", + "PreferEmbeddedEpisodeInfosOverFileNames": "Prefer embedded episode information over filenames", "Premiere": "Premiere", "Premieres": "Premieres", "Previous": "Previous", @@ -1279,7 +1324,7 @@ "RefreshQueued": "Refresh queued.", "ReleaseDate": "Release date", "ReleaseGroup": "Release Group", - "RememberMe": "Remember me", + "RememberMe": "Remember Me", "RemoveFromCollection": "Remove from collection", "RemoveFromPlaylist": "Remove from playlist", "Repeat": "Repeat", @@ -1289,7 +1334,6 @@ "RepeatOne": "Repeat one", "ReplaceAllMetadata": "Replace all metadata", "ReplaceExistingImages": "Replace existing images", - "RequiredForAllRemoteConnections": "Required for all remote connections", "RestartPleaseWaitMessage": "Please wait while Jellyfin Server shuts down and restarts. This may take a minute or two.", "ResumeAt": "Resume from {0}", "Rewind": "Rewind", @@ -1297,6 +1341,7 @@ "Runtime": "Runtime", "Saturday": "Saturday", "Save": "Save", + "SaveChanges": "Save changes", "SaveSubtitlesIntoMediaFolders": "Save subtitles into media folders", "SaveSubtitlesIntoMediaFoldersHelp": "Storing subtitles next to video files will allow them to be more easily managed.", "ScanForNewAndUpdatedFiles": "Scan for new and updated files", @@ -1328,10 +1373,14 @@ "Share": "Share", "ShowAdvancedSettings": "Show advanced settings", "ShowIndicatorsFor": "Show indicators for:", + "ShowLess": "Show less", + "ShowMore": "Show more", "ShowTitle": "Show title", "ShowYear": "Show year", "Shows": "Shows", "Shuffle": "Shuffle", + "New": "New", + "Filter": "Filter", "SimultaneousConnectionLimitHelp": "The maximum number of allowed simultaneous streams. Enter 0 for no limit.", "SkipEpisodesAlreadyInMyLibrary": "Don't record episodes that are already in my library", "SkipEpisodesAlreadyInMyLibraryHelp": "Episodes will be compared using season and episode numbers, when available.", @@ -1356,6 +1405,7 @@ "Suggestions": "Suggestions", "Sunday": "Sunday", "Sync": "Sync", + "SyncPlayAccessHelp": "Select the level of access this user has to the SyncPlay feature. SyncPlay enables to sync playback with other devices.", "SystemDlnaProfilesHelp": "System profiles are read-only. Changes to a system profile will be saved to a new custom profile.", "TV": "TV", "TabAccess": "Access", @@ -1372,6 +1422,7 @@ "TabDevices": "Devices", "TabDirectPlay": "Direct Play", "TabDisplay": "Display", + "TabDVR": "DVR", "TabEpisodes": "Episodes", "TabFavorites": "Favorites", "TabGenres": "Genres", @@ -1481,8 +1532,8 @@ "XmlTvNewsCategoriesHelp": "Programs with these categories will be displayed as news programs. Separate multiple with '|'.", "XmlTvPathHelp": "A path to a XMLTV file. Jellyfin will read this file and periodically check it for updates. You are responsible for creating and updating the file.", "XmlTvSportsCategoriesHelp": "Programs with these categories will be displayed as sports programs. Separate multiple with '|'.", - "Yadif": "Yadif", - "YadifBob": "Yadif Bob", + "Yadif": "YADIF", + "YadifBob": "YADIF Bob", "Yes": "Yes", "Yesterday": "Yesterday", "PathNotFound": "The path could not be found. Please ensure the path is valid and try again.", @@ -1497,6 +1548,10 @@ "EveryHour": "Every hour", "EveryXHours": "Every {0} hours", "OnApplicationStartup": "On application startup", + "UnsupportedPlayback": "Jellyfin cannot decrypt content protected by DRM but all content will be attempted regardless, including protected titles. Some files may appear completely black due to encryption or other unsupported features, such as interactive titles.", + "EnableBlurhash": "Enable blurred placeholders for images", + "EnableBlurhashHelp": "Images that are still being loaded will be displayed with a blurred placeholder", + "EnableDecodingColorDepth10": "Enable 10-Bit hardware decoding", "EnableDecodingColorDepth10Help" : "Enable 10-Bit hardware decoding on supported hardware. Only works for HEVC and VP9 formats. Turn this off if you experience playback issues." } diff --git a/src/strings/eo.json b/src/strings/eo.json new file mode 100644 index 0000000000..5f0e658efa --- /dev/null +++ b/src/strings/eo.json @@ -0,0 +1,5 @@ +{ + "AddToCollection": "Aldoni al kolekto", + "Actor": "Aktoro", + "Absolute": "Absoluta" +} diff --git a/src/strings/es-ar.json b/src/strings/es-ar.json index 52b4d02cf2..2fc290c66c 100644 --- a/src/strings/es-ar.json +++ b/src/strings/es-ar.json @@ -1,6 +1,6 @@ { "ButtonQuickStartGuide": "Guía de inicio rápido", - "ButtonSignOut": "Sign out", + "ButtonSignOut": "Cerrar sesión", "EnableHardwareEncoding": "Habilitar la codificación de hardware", "FolderTypeTvShows": "TV", "HeaderAddUser": "Agregar Usuario", @@ -38,8 +38,8 @@ "Folders": "Carpetas", "Genres": "Géneros", "HeaderAlbumArtists": "Artistas de álbum", - "HeaderContinueWatching": "Continuar viendo", - "HeaderNextUp": "A Continuación", + "HeaderContinueWatching": "Seguir viendo", + "HeaderNextUp": "Siguiente", "Movies": "Películas", "Photos": "Fotos", "Playlists": "Listas de reproducción", @@ -47,21 +47,21 @@ "Sync": "Sincronizar", "ValueSpecialEpisodeName": "Especial - {0}", "Absolute": "Absoluto", - "AccessRestrictedTryAgainLater": "El acceso está actualmente restringido. Por favor intente nuevamente mas tarde.", + "AccessRestrictedTryAgainLater": "El acceso está actualmente restringido. Por favor intente nuevamente más tarde.", "Actor": "Actor", "Add": "Agregar", - "AddItemToCollectionHelp": "Agregue items a las colecciones buscándolas y usando el click derecho o tocando el menu para añadirlas a una colección.", + "AddItemToCollectionHelp": "Agregue elementos a las colecciones buscándolos y usando el click derecho o tocando el menú para añadirlos a una colección.", "AddToCollection": "Añadir a la colección", - "AddToPlayQueue": "Añadir a la lista de reproducción", + "AddToPlayQueue": "Añadir a la cola de reproducción", "AddToPlaylist": "Añadir a la lista de reproducción", "AddedOnValue": "Añadidos {0}", - "AdditionalNotificationServices": "Navegue el catálogo de plugins para instalar servicios de notificación adicionales.", + "AdditionalNotificationServices": "Navegue el catálogo de complementos para instalar servicios de notificación adicionales.", "AirDate": "Fecha de emisión", "Aired": "Emitido", "Alerts": "Alertas", - "All": "Todo", + "All": "Todos", "AllChannels": "Todos los canales", - "AllComplexFormats": "Todos los formatos complejos (ASS, SSA, VOBSUB, PGS, SUB, IDX)", + "AllComplexFormats": "Todos los formatos complejos (ASS, SSA, VOBSUB, PGS, SUB, IDX, ...)", "AllEpisodes": "Todos los capítulos", "AllLanguages": "Todos los idiomas", "AllLibraries": "Todas las bibliotecas", @@ -69,9 +69,9 @@ "AllowMediaConversionHelp": "Permitir o denegar acceso a la opción de convertir medios.", "AllowOnTheFlySubtitleExtraction": "Permitir extracción de subtítulos al vuelo", "AllowOnTheFlySubtitleExtractionHelp": "Los subtítulos incrustados pueden extraerse de los videos y entregarse a los reproductores en texto plano para ayudar a evitar la transcodificación de video. En algunos sistemas, esto puede tardar mucho tiempo y provocar que la reproducción de video se detenga durante el proceso de extracción. Deshabilite esta opción para que los subtítulos incrustados se graben con transcodificación de video cuando no estén soportados de forma nativa por el dispositivo cliente.", - "AllowRemoteAccess": "Permitir conexiones remotas a este Servidor Jellyfin.", + "AllowRemoteAccess": "Permitir conexiones remotas a este servidor Jellyfin.", "AllowRemoteAccessHelp": "Si no está tildado, todas las conexiones remotas serán bloqueadas.", - "AlwaysPlaySubtitles": "Siempre mostrar subtítulos", + "AlwaysPlaySubtitles": "Reproducir siempre", "AnyLanguage": "Cualquier idioma", "Anytime": "Cualquier fecha", "Ascending": "Ascendente", @@ -79,21 +79,16 @@ "Audio": "Audio", "Auto": "Auto", "AutoBasedOnLanguageSetting": "Auto (basado en configuración de idioma)", - "AutomaticallyConvertNewContent": "Convertir contenido nuevo automáticamente", "Backdrop": "Fondo", - "AllowHWTranscodingHelp": "Permite que el sintonizador transcodifique las transmisiones sobre la marcha. Esto puede ayudar a reducir la transcodificación requerida por el servidor.", - "AllowedRemoteAddressesHelp": "Lista separada por comas de direcciones IP o IP/máscara-de-red para redes a las que se les permitirá conectarse de forma remota. Si se deja vacía, todas las direcciones remotas serán permitidas.", + "AllowHWTranscodingHelp": "Permita que el sintonizador transcodifique transmisiones sobre la marcha. Esto puede ayudar a reducir la transcodificación requerida por el servidor.", + "AllowedRemoteAddressesHelp": "Lista separada por comas de direcciones IP o IP/máscara de red para redes a las que se les permitirá conectarse de forma remota. Si se deja vacía, todas las direcciones remotas serán permitidas.", "AlwaysPlaySubtitlesHelp": "Los subtítulos que concuerden con la preferencia de idioma se cargarán independientemente del idioma del audio.", - "AndroidUnlockRestoreHelp": "Para recuperar tu compra anterior, por favor asegurate que iniciaste sesión en el dispositivo con la misma cuenta de Google (o Amazon) que hizo la compra originalmente. Asegurate de que la tienda de aplicaciones esté habilitada y no posea control parental alguno, y que tiene una conexión a Internet activa. Solo tendrás que hacer esto una sola vez para recuperar tu compra anterior.", "AroundTime": "Alrededor de {0}", "Art": "Arte", "AsManyAsPossible": "Tantos como sea posible", "AspectRatio": "Relación de aspecto", "AudioBitrateNotSupported": "Tasa de bits de audio no soportado", "AudioSampleRateNotSupported": "Frecuencia de muestreo de audio no soportada", - "AutomaticallyConvertNewContentHelp": "El contenido nuevo que se agregue a esta carpeta va a convertirse automáticamente.", - "AutomaticallySyncNewContent": "Descargar contenido nuevo automáticamente", - "AutomaticallySyncNewContentHelp": "El contenido nuevo agregado a esta carpeta va a ser descargado automáticamente al dispositivo.", "Backdrops": "Imágenes de fondo", "Banner": "Cartel", "BestFit": "Mejor ajuste", @@ -128,7 +123,6 @@ "ButtonConfigurePassword": "Configurar contraseña", "ButtonConfigurePinCode": "Configurar clave PIN", "ButtonConnect": "Conectar", - "ButtonConvertMedia": "Convertir medios", "ButtonCreate": "Crear", "ButtonDelete": "Eliminar", "ButtonDeleteImage": "Eliminar imagen", @@ -169,7 +163,6 @@ "ButtonParentalControl": "Control parental", "ButtonPause": "Pausar", "ButtonPlay": "Reproducir", - "ButtonPlayOneMinute": "Reproducir un minuto", "ButtonPlayTrailer": "Tráiler", "ButtonPlaylist": "Lista de reproducción", "ButtonPreferences": "Preferencias", @@ -178,10 +171,8 @@ "ButtonPrivacyPolicy": "Política de privacidad", "ButtonProfile": "Perfil", "ButtonProfileHelp": "Definir imagen de perfil y contraseña", - "ButtonPurchase": "Comprar", "ButtonQuality": "Calidad", "ButtonRecord": "Grabar", - "ButtonReenable": "Reactivar", "ButtonRefresh": "Actualizar", "ButtonRefreshGuideData": "Actualizar datos de la guía", "ButtonReject": "Rechazar", @@ -192,12 +183,11 @@ "ButtonRepeat": "Repetir", "ButtonReports": "Reportes", "ButtonReset": "Restablecer", - "ButtonResetEasyPassword": "Restablecer codigo PIN", + "ButtonResetEasyPassword": "Restablecer código PIN", "ButtonResetPassword": "Restablecer contraseña", "ButtonResetTuner": "Restablecer sintonizador", "ButtonRestart": "Reiniciar", "ButtonRestartNow": "Reiniciar ahora", - "ButtonRestorePreviousPurchase": "Recuperar compra", "ButtonResume": "Resumir", "ButtonRevoke": "Revocar", "ButtonSave": "Guardar", @@ -213,12 +203,11 @@ "ButtonSendInvitation": "Enviar invitación", "ButtonServer": "Servidor", "ButtonServerDashboard": "Panel del servidor", - "ButtonSettings": "Ajustes", + "ButtonSettings": "Configuraciones", "ButtonShare": "Compartir", "ButtonShuffle": "Aleatorio", "ButtonShutdown": "Apagar", "ButtonSignIn": "Iniciar sesión", - "ButtonSignUp": "Registrarse", "ButtonSkip": "Saltear", "ButtonSort": "Ordenar", "ButtonStart": "Iniciar", @@ -230,8 +219,6 @@ "ButtonTrailer": "Tráiler", "ButtonTryAgain": "Intentar de nuevo", "ButtonUninstall": "Desinstalar", - "ButtonUnlockPrice": "Desbloquear {0}", - "ButtonUnlockWithPurchase": "Desbloquear con compra", "ButtonUnmute": "Desilenciar", "ButtonUp": "Arriba", "ButtonUpdateNow": "Actualizar ahora", @@ -251,11 +238,10 @@ "CategorySystem": "Sistema", "CategoryUser": "Usuario", "ChangingMetadataImageSettingsNewContent": "Cambiar la configuración de descarga de metadatos o arte solo aplicará al contenido nuevo que se agregue a tu biblioteca. Para aplicar los cambios a los títulos existentes, tendrás que actualizar sus metadatos manualmente.", - "ChannelAccessHelp": "Selecciona los canales a compartir con este usuario. Los administradores podrán editar todos los canales con el administrador de metadatos", + "ChannelAccessHelp": "Selecciona los canales a compartir con este usuario. Los administradores podrán editar todos los canales con el administrador de metadatos.", "ChannelNameOnly": "Sólo canal {0}", "ChannelNumber": "Número del canal", "CinemaModeConfigurationHelp": "El modo cine trae la experiencia del cine directamente a tu living, con la posibilidad de reproducir tráilers e introducciones personalizadas antes de la función principal.", - "CinemaModeConfigurationHelp2": "Las aplicaciones de Jellyfin tienen la opcion de habilitar o deshabilitar el modo cine. Las aplicaciones de TV activan el modo cine de forma predeterminada.", "CinemaModeFeatureDescription": "El modo cine te da la verdadera experiencia del cine con tráilers e introducciones personalizadas antes de la función principal.", "CloudSyncFeatureDescription": "Sincroniza tus medios a la nube para respaldos, archivados y conversiones más fáciles.", "ColorPrimaries": "Colores primarios", @@ -265,8 +251,8 @@ "Composer": "Compositor", "ConfigureDateAdded": "Configura cómo se va a determinar las fechas de adición en la pestaña Bibliotecas del panel del servidor Jellyfin", "ConfirmDeleteImage": "¿Eliminar imagen?", - "ConfirmDeleteItem": "Eliminar este elemento lo eliminara tanto del sistema de archivos como de la biblioteca de medios. ¿Está seguro que desea continuar?", - "ConfirmDeleteItems": "Eliminar estos elementos los eliminara tanto del sistema de archivos como de la biblioteca de medios. ¿Está seguro que desea continuar?", + "ConfirmDeleteItem": "Eliminar este elemento lo eliminará tanto del sistema de archivos como de la biblioteca de medios. ¿Está seguro que desea continuar?", + "ConfirmDeleteItems": "Eliminar estos elementos los eliminará tanto del sistema de archivos como de la biblioteca de medios. ¿Está seguro que desea continuar?", "ConfirmDeletion": "Confirmar eliminación", "ConfirmEndPlayerSession": "¿Desea apagar Jellyfin en {0}?", "ConfirmRemoveDownload": "¿Quitar descarga?", @@ -286,20 +272,20 @@ "CoverArt": "Arte de portada", "CustomDlnaProfilesHelp": "Crear un perfil personalizado para apuntar un nuevo dispositivo o sobre-escribir el perfil del sistema.", "DateAdded": "Fecha agregada", - "DatePlayed": "Fecha reproducido", + "DatePlayed": "Fecha de reproducción", "Days": "Dias", "DeathDateValue": "Muerte: {0}", "Default": "Predeterminado", "DefaultCameraUploadPathHelp": "Selecciona un directorio personalizado. Si queda en blanco, una carpeta predeterminada va a ser usada. Si es una carpeta personalizada, esta va a necesitar ser agregada en la biblioteca, en la configuración de bibliotecas de Jellyfin.", "DefaultErrorMessage": "Hubo un error procesando la solicitud. Por favor intentalo nuevamente mas tarde.", - "DefaultMetadataLangaugeDescription": "Estos son tus predeterminados y pueden ser personalizados por librería unicamente.", + "DefaultMetadataLangaugeDescription": "Estos son tus predeterminados y pueden ser personalizados por librería únicamente.", "Delete": "Borrar", "DeleteDeviceConfirmation": "Estas seguro que quieres borrar este dispositivo? Este va a volver a aparecer cuando una persona se registre.", "DeleteImage": "Borrar Imagen", - "DeleteImageConfirmation": "Estas seguro que quieres borrar esta imagen?", + "DeleteImageConfirmation": "Estás seguro que quieres borrar esta imagen?", "DeleteMedia": "Borrar medio", - "DeleteUser": "Borrar Usuario", - "DeleteUserConfirmation": "Estas seguro que quieres borrar este usuario?", + "DeleteUser": "Borrar usuario", + "DeleteUserConfirmation": "Estás seguro que quieres borrar este usuario?", "Depressed": "Deprimido", "Descending": "Descendiente", "Desktop": "Escritorio", @@ -308,16 +294,16 @@ "DeviceLastUsedByUserName": "Usado ultima vez por {0}", "DirectPlayError": "Error en la reproducción directa", "DirectPlaying": "Reproducción directa", - "DirectStreamHelp1": "El medio es compatible con el dispositivo en cuanto a la resolución y tipo de medio (H.264, AC3, etc.), pero está en un contenedor de archivo incompatible (mkv, avi, wmv, etc.). El video sera reempaquetado en el acto antes de transmitirlo al dispositivo.", - "DirectStreamHelp2": "Transmitir directamente un archivo usa muy poco procesamiento, esto sin perdida en la calidad de video.", - "DirectStreaming": "Transmisión en directo", + "DirectStreamHelp1": "El medio es compatible con el dispositivo con respecto a la resolución y el tipo de medio (H.264, AC3, etc.), pero está en un contenedor de archivos incompatible (mkv, avi, wmv, etc.). El video se volverá a empaquetar sobre la marcha antes de transmitirlo al dispositivo.", + "DirectStreamHelp2": "La transmisión directa de un archivo utiliza muy poca potencia de procesamiento sin pérdida de calidad de video.", + "DirectStreaming": "Transmisión directa", "Director": "Director", "Directors": "Directores", "Disabled": "Deshabilitado", "Disc": "Disco", "Disconnect": "Desconectar", "Display": "Pantalla", - "DisplayInMyMedia": "Mostrar en pantalla principal", + "DisplayInMyMedia": "Mostrar en la pantalla de inicio", "DisplayInOtherHomeScreenSections": "Mostrar en las secciones de la pantalla principal, como últimos medios y continuar viendo", "DisplayMissingEpisodesWithinSeasons": "Mostrar episodios faltantes entre temporadas", "DisplayMissingEpisodesWithinSeasonsHelp": "Esto también debe estar habilitado para las bibliotecas de TV en la configuración del servidor.", @@ -345,8 +331,8 @@ "EnableCinemaMode": "Modo cine", "EnableColorCodedBackgrounds": "Habilitar colores en el fondo del código", "AuthProviderHelp": "Seleccione un proveedor de autenticación que se utilizará para autenticar la contraseña de este usuario.", - "CriticRating": "Calificación de la crítica", - "DefaultSubtitlesHelp": "Los subtítulos se cargan en base a los indicadores por defecto y los indicadores forzados en los metadatos embebidos. Las preferencias de idioma son consideradas cuando existe más de una opción.", + "CriticRating": "Valoración crítica", + "DefaultSubtitlesHelp": "Los subtítulos se cargan según los indicadores predeterminados y forzados en los metadatos incrustados. Las preferencias de idioma se consideran cuando hay varias opciones disponibles.", "Dislike": "No me gusta", "EnableDebugLoggingHelp": "El registro de depuración debería activarse solo a fin de solucionar problemas. El incremento en el acceso al sistema de archivos podría prevenir que el servidor entre en modo de suspensión en algunos entornos.", "EnableDisplayMirroring": "Habilitar duplicación de la pantalla", @@ -361,23 +347,23 @@ "EnableThemeSongs": "Canciones temáticas", "EnableThemeSongsHelp": "Reproducir canciones temáticas en el fondo mientras se navega por la biblioteca.", "EnableThemeVideos": "Videos temáticos", - "EnableThemeVideosHelp": "Al habilitarse, los videos de tema se reproducirán de fondo mientras navegues por la biblioteca.", + "EnableThemeVideosHelp": "Al habilitarse, los vídeos de tema se reproducirán de fondo mientras navegues por la biblioteca.", "Ended": "Finalizado", "EndsAtValue": "Termina en {0}", "Episodes": "Episodios", "ErrorAddingListingsToSchedulesDirect": "Ocurrió un error al añadir el alineamiento a tu cuenta de Schedules Direct. Schedules Direct solo permite una cantidad limitada de alineamientos por cuenta. Quizás necesites ingresar al sitio de Schedules Direct y eliminar otros alineamientos de tu cuenta antes de continuar.", "ErrorAddingMediaPathToVirtualFolder": "Ocurrió un error al agregar la ruta de medios. Por favor, asegurate que la ruta es válida y que el proceso que sirve Jellyfin tiene acceso a esa ubicación.", "ErrorAddingTunerDevice": "Ocurrió un error al añadir el dispositivo sintonizador. Por favor asegurate que está disponible e intenta de nuevo.", - "ErrorAddingXmlTvFile": "Ocurrió un error al acceder al archivo de XmlTV. Por favor asegurate de que el archivo existe e intenta de nuevo.", + "ErrorAddingXmlTvFile": "Ocurrió un error al acceder al archivo de XmlTV. Por favor asegúrate de que el archivo existe e intenta de nuevo.", "ErrorDeletingItem": "Ocurrió un error al eliminar el ítem del servidor Jellyfin. Por favor verifica que el servidor Jellyfin tiene permiso de escritura a la carpeta de medios e intenta de nuevo.", - "ErrorGettingTvLineups": "Ocurrió un error al descargar los alineamientos de TV. Por favor asegúrate que tu información es correcta e intenta de nuevo.", + "ErrorGettingTvLineups": "Ocurrió un error al descargar la guía de programación de TV. Por favor asegúrate que tu información es correcta e intenta de nuevo.", "ErrorMessageStartHourGreaterThanEnd": "La hora de fin tiene que ser mayor que la de inicio.", "ErrorPleaseSelectLineup": "Por favor selecciona un alineamiento e intenta de nuevo. Si no existen alineamientos disponibles, asegúrate de que tu nombre de usuario, contraseña y código postal son correctos.", "ErrorSavingTvProvider": "Ocurrió un error al guardar el proveedor de TV. Por favor asegúrate de que está disponible e intenta de nuevo.", "EveryNDays": "Cada {0} días", "ExitFullscreen": "Salir de pantalla completa", "ExtraLarge": "Extra grande", - "ExtractChapterImagesHelp": "Extraer imágenes de los capítulos permitirá a las aplicaciones de Jellyfin mostrar menúes gráficos de selección de escena. El proceso puede ser lento, ocupar mucho tiempo de cpu y puede ocupar varios gigabytes de almacenamiento. El proceso corre cuando los videos son descubiertos, y también como un proceso periódico por la noche. El horario del proceso periódico se puede configurar en el área de procesos periódicos. No se recomienda correr este proceso durante horas de alto uso.", + "ExtractChapterImagesHelp": "Extraer imágenes de los capítulos permitirá a las aplicaciones de Jellyfin mostrar menús gráficos de selección de escena. El proceso puede ser lento, ocupar mucho tiempo de cpu y puede ocupar varios gigabytes de almacenamiento. El proceso corre cuando los vídeos son descubiertos, y también como un proceso periódico por la noche. El horario del proceso periódico se puede configurar en el área de procesos periódicos. No se recomienda correr este proceso durante horas de alto uso.", "Extras": "Extras", "FFmpegSavePathNotFound": "No pudimos localizar FFmpeg usando la ruta que has ingresado. FFprobe también es necesario y debe existir en la misma carpeta. Éstos componentes normalmente están incluidos en la misma descarga. Por favor, revisa la ruta e intenta de nuevo.", "FastForward": "Avanzar Rápido", @@ -391,8 +377,8 @@ "FolderTypeBooks": "Libros", "FolderTypeMovies": "Películas", "FolderTypeMusic": "Música", - "FolderTypeMusicVideos": "Videos Musicales", - "FolderTypeUnset": "Sin especificar (Contenido Mixto)", + "FolderTypeMusicVideos": "Vídeos Musicales", + "FolderTypeUnset": "Contenido Mixto", "FormatValue": "Formato: {0}", "Friday": "Viernes", "Fullscreen": "Pantalla Completa", @@ -409,10 +395,10 @@ "HeaderFavoriteEpisodes": "Episodios favoritos", "HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteAlbums": "Álbumes favoritos", - "Shows": "Series", + "Shows": "Programas", "CopyStreamURLError": "Hubo un error copiando la URL.", "CopyStreamURLSuccess": "URL copiada con éxito.", - "CopyStreamURL": "Copiar la URL de la transmisión", + "CopyStreamURL": "Copiar URL de transmisión", "ButtonSplit": "Dividir", "ButtonAddImage": "Agregar imagen", "AskAdminToCreateLibrary": "Preguntar al administrador para crear una biblioteca.", @@ -421,7 +407,7 @@ "HeaderCancelRecording": "Cancelar Grabación", "HeaderBranding": "Marca", "HeaderBooks": "Libros", - "HeaderBlockItemsWithNoRating": "Bloquear elementos con rating de información no reconocible:", + "HeaderBlockItemsWithNoRating": "Bloquear elementos con rating de información vacía o no reconocible:", "HeaderAutomaticUpdates": "Actualizaciones Automáticas", "HeaderAudioSettings": "Configuración del Audio", "HeaderAudioBooks": "Audiolibros", @@ -429,7 +415,7 @@ "HeaderApp": "Aplicación", "HeaderApiKeysHelp": "Las aplicaciones externas requieren una llave API para poder comunicarse con el servidor Jellyfin. Las llaves se emiten iniciando sesión con una cuenta Jellyfin u otorgando manualmente una clave a la aplicación.", "HeaderApiKeys": "Llaves API", - "HeaderApiKey": "Llave API", + "HeaderApiKey": "Contraseña API", "HeaderAllowMediaDeletionFrom": "Permitir el borrado de medios desde", "HeaderAlert": "Alerta", "HeaderAlbums": "Albumes", @@ -444,16 +430,233 @@ "HeaderActiveDevices": "Dispositivos activos", "HeaderAccessScheduleHelp": "Crear un calendario de acceso, para limitar el acceso en determinadas horas.", "HeaderAccessSchedule": "Acceder al Calendario", - "HardwareAccelerationWarning": "Habilitar la aceleración de hardware puede causar inestabilidad en algunos entornos. Asegúrese de que su sistema operativo y los controladores de video estén completamente actualizados. Si tiene dificultades para reproducir el video después de habilitarlo, deberá volver a cambiar la configuración a \"Nada\".", + "HardwareAccelerationWarning": "Habilitar la aceleración de hardware puede causar inestabilidad en algunos entornos. Asegúrese de que su sistema operativo y los controladores de vídeo estén completamente actualizados. Si tiene dificultades para reproducir el vídeo después de habilitarlo, deberá volver a cambiar la configuración a \"Nada\".", "HandledByProxy": "Manejado por un proxy reverso", "HDPrograms": "Programas en HD", - "EncoderPresetHelp": "Elige un valor más rápido para mejorar la performance, o elige un valor más lento para mejorar la calidad.", + "EncoderPresetHelp": "Elige un valor más rápido para mejorar el desempeño, o elige un valor más lento para mejorar la calidad.", "FetchingData": "Obteniendo información adicional", "Episode": "Episodio", "Yesterday": "Ayer", "ClientSettings": "Configuración de cliente", "BoxSet": "Colección", "Artist": "Artista", - "AlbumArtist": "Artista del Album", - "Album": "Album" + "AlbumArtist": "Artista del álbum", + "Album": "Álbum", + "HeaderDateIssued": "Fecha de Emisión", + "HeaderCustomDlnaProfiles": "Perfiles personalizados", + "HeaderContinueListening": "Seguir escuchando", + "HeaderContainerProfileHelp": "Los perfiles de contenedor indican las limitaciones de un dispositivo cuando se reproducen formatos específicos. Si se aplica una limitación, los medios se transcodificarán, incluso si el formato está configurado para reproducción directa.", + "HeaderContainerProfile": "Perfil de contenedor", + "HeaderConnectionFailure": "Conexión fallida", + "HeaderConnectToServer": "Conectar al servidor", + "HeaderConfirmRevokeApiKey": "Revocar llave de API", + "HeaderConfirmProfileDeletion": "Confirmar borrado de perfil", + "HeaderConfirmPluginInstallation": "Confirmar instalación del complemento", + "HeaderConfigureRemoteAccess": "Configurar acceso remoto", + "HeaderCodecProfileHelp": "Los perfiles de códec indican las limitaciones de un dispositivo cuando se reproducen códecs específicos. Si se aplica una limitación, los medios se transcodificarán, incluso si el códec está configurado para reproducción directa.", + "HeaderCodecProfile": "Perfil del códec", + "HeaderChapterImages": "Imágenes del capitulo", + "HeaderChannels": "Canales", + "HeaderChannelAccess": "Acceso al canal", + "HeaderCastCrew": "Reparto", + "HeaderCastAndCrew": "Reparto", + "HeaderCancelSeries": "Cancelar serie", + "H264CrfHelp": "El Factor de velocidad constante (CRF) es la configuración de calidad predeterminada para el codificador x264. Puede establecer los valores entre 0 y 51, donde valores más bajos resultarían en una mejor calidad (a expensas de tamaños de archivo más altos). Los valores correctos están entre 18 y 28. El valor predeterminado para x264 es 23, por lo que puede usar esto como punto de partida.", + "DeinterlaceMethodHelp": "Seleccione el método de desentrelazado que se usará al transcodificar contenido entrelazado.", + "HeaderFavoriteVideos": "Videos favoritos", + "HeaderFavoritePeople": "Gente favorita", + "HeaderFavoriteMovies": "Películas Favoritas", + "HeaderFavoriteBooks": "Libros favoritos", + "HeaderExternalIds": "IDs externos:", + "HeaderError": "Error", + "HeaderEpisodes": "Episodios", + "HeaderEnabledFields": "Campos habilitados", + "HeaderEditImages": "Editar imágenes", + "HeaderEasyPinCode": "Pin sencillo", + "HeaderDownloadSync": "Descargar y sincronizar", + "HeaderDisplay": "Pantalla", + "HeaderDirectPlayProfile": "Perfil Direct Play", + "HeaderDevices": "Dispositivos", + "HeaderDeviceAccess": "Acceso al dispositivo", + "HeaderDeveloperInfo": "Información para desarrolladores", + "HeaderDetectMyDevices": "Detectar mis dispositivos", + "HeaderDeleteTaskTrigger": "Elminar disparador de acciones", + "HeaderDeleteProvider": "Eliminar proveedor", + "HeaderDeleteItems": "Eliminar ítems", + "HeaderDeleteItem": "Eliminar ítem", + "HeaderDeleteDevice": "Eliminar dispositivo", + "HeaderDefaultRecordingSettings": "Ajustes de grabación por defecto", + "UnsupportedPlayback": "Jellyfin no puede descifrar contenido protegido por DRM pero a pesar de esto lo intentará con todo el contenido, incluyendo títulos protegidos. Algunos archivos pueden aparecer completamente en negro por estar cifrados o por otras características no soportadas, como títulos interactivos.", + "HeaderMediaFolders": "Carpetas de medios", + "HeaderMedia": "Medios", + "HeaderLoginFailure": "Error al iniciar sesión", + "HeaderLiveTvTunerSetup": "Configuración del sintonizador de TV en vivo", + "HeaderLiveTv": "TV en vivo", + "HeaderLibrarySettings": "Configuraciones de biblioteca", + "HeaderLibraryOrder": "Orden de biblioteca", + "HeaderLibraryFolders": "Carpetas de biblioteca", + "HeaderLibraryAccess": "Acceso a la biblioteca", + "HeaderLibraries": "Bibliotecas", + "HeaderLatestRecordings": "Grabaciones recientes", + "HeaderLatestMusic": "Música reciente", + "HeaderLatestMovies": "Películas recientes", + "HeaderLatestMedia": "Medios recientes", + "HeaderKeepSeries": "Mantener serie", + "HeaderKeepRecording": "Mantener grabación", + "HeaderItems": "Artículos", + "HeaderInstantMix": "Mezcla instantánea", + "HeaderInstall": "Instalar", + "HeaderImageSettings": "Configuraciones de imagen", + "HeaderImageOptions": "Opciones de imagen", + "HeaderIdentifyItemHelp": "Ingrese uno o más criterios de búsqueda. Eliminar criterios para aumentar los resultados de búsqueda.", + "HeaderIdentificationHeader": "Encabezado de identificación", + "HeaderIdentificationCriteriaHelp": "Ingrese al menos un criterio de identificación.", + "HeaderIdentification": "Identificación", + "HeaderHttpHeaders": "Encabezados HTTP", + "HeaderHome": "Inicio", + "HeaderGuideProviders": "Proveedores de datos de guías de TV", + "HeaderGenres": "Géneros", + "HeaderFrequentlyPlayed": "Reproducido con frecuencia", + "HeaderForgotPassword": "Olvidé la contraseña", + "HeaderForKids": "Para niños", + "HeaderFilters": "Filtros", + "HeaderFetcherSettings": "Configuración del recuperador", + "HeaderFetchImages": "Obtener imágenes:", + "HeaderFeatures": "Caracteristicas", + "HeaderFeatureAccess": "Acceso a características", + "HeaderFavoritePlaylists": "Listas de reproducción favoritas", + "ButtonTogglePlaylist": "Lista de reproducción", + "ButtonToggleContextMenu": "Más", + "HeaderPlaybackError": "Error de reproducción", + "HeaderPlayback": "Reproducción de medios", + "HeaderPlayOn": "Reproducir en", + "HeaderPlayAll": "Reproducir todo", + "HeaderPinCodeReset": "Restablecer código PIN", + "HeaderPhotoAlbums": "Álbumes de fotos", + "HeaderPeople": "Personas", + "HeaderPendingInvitations": "Invitaciones pendientes", + "HeaderPaths": "Rutas", + "HeaderPasswordReset": "Restablecer contraseña", + "HeaderPassword": "Contraseña", + "HeaderParentalRatings": "Calificaciones parentales", + "HeaderOtherItems": "Otros elementos", + "HeaderOnNow": "Ahora", + "HeaderNewDevices": "Nuevos dispositivos", + "HeaderNewApiKey": "Nueva clave de API", + "HeaderNavigation": "Navegación", + "HeaderMyMediaSmall": "Mis medios (pequeño)", + "HeaderMyMedia": "Mis medios", + "HeaderMyDevice": "Mi dispositivo", + "HeaderMusicVideos": "Videos musicales", + "HeaderMusicQuality": "Calidad de música", + "HeaderMovies": "Películas", + "LabelAccessDay": "Día de la semana:", + "LabelAbortedByServerShutdown": "(Abortado por el apagado del servidor)", + "Label3DFormat": "Formato 3D:", + "Kids": "Niños", + "Items": "Artículos", + "ItemCount": "{0} artículos", + "InstantMix": "Mezcla instantánea", + "InstallingPackage": "Instalando {0} (versión {1})", + "ImportFavoriteChannelsHelp": "Si está habilitado, solo se importarán los canales que estén marcados como favoritos en el dispositivo sintonizador.", + "Images": "Imágenes", + "Identify": "Identificar", + "HttpsRequiresCert": "Para habilitar conexiones seguras, deberá proporcionar un certificado SSL confiable, como Let's Encrypt. Proporcione un certificado o desactive las conexiones seguras.", + "Horizontal": "Horizontal", + "Home": "Inicio", + "HideWatchedContentFromLatestMedia": "Ocultar contenido visto de los últimos medios", + "Hide": "Ocultar", + "Help": "Ayuda", + "HeadersFolders": "Carpetas", + "HeaderYears": "Años", + "HeaderXmlSettings": "Configuraciones Xml", + "HeaderXmlDocumentAttributes": "Atributos del documento Xml", + "HeaderXmlDocumentAttribute": "Atributo de documento Xml", + "HeaderVideos": "Videos", + "HeaderVideoTypes": "Tipos de video", + "HeaderVideoType": "Tipo de video", + "HeaderVideoQuality": "Calidad de video", + "HeaderUsers": "Usuarios", + "HeaderUser": "Usuario", + "HeaderUploadImage": "Subir imagen", + "HeaderUpcomingOnTV": "Próximamente en TV", + "HeaderTypeText": "Ingrese texto", + "HeaderTuners": "Sintonizadores", + "HeaderTunerDevices": "Dispositivos sintonizadores", + "HeaderTranscodingProfileHelp": "Agregue perfiles de transcodificación para indicar qué formatos deben usarse cuando se requiere transcodificación.", + "HeaderTranscodingProfile": "Perfil de transcodificación", + "HeaderTracks": "Pistas", + "HeaderThisUserIsCurrentlyDisabled": "Este usuario está actualmente deshabilitado", + "HeaderTaskTriggers": "Desencadenantes de tareas", + "HeaderTags": "Etiquetas", + "HeaderSystemDlnaProfiles": "Perfiles del sistema", + "HeaderSubtitleProfilesHelp": "Los perfiles de subtítulos describen los formatos de subtítulos compatibles con el dispositivo.", + "HeaderSubtitleProfiles": "Perfiles de subtítulos", + "HeaderSubtitleProfile": "Perfil de subtítulos", + "HeaderSubtitleDownloads": "Descargas de subtítulos", + "HeaderSubtitleAppearance": "Apariencia de subtítulos", + "HeaderStopRecording": "Detener grabación", + "HeaderStatus": "Estado", + "HeaderStartNow": "Empezar ahora", + "HeaderSpecialFeatures": "Características especiales", + "HeaderSpecialEpisodeInfo": "Información especial del episodio", + "HeaderSortOrder": "Orden de clasificación", + "HeaderSortBy": "Ordenar por", + "HeaderShutdown": "Apagar", + "HeaderSetupLibrary": "Configura tus bibliotecas de medios", + "HeaderSettings": "Configuraciones", + "HeaderServerSettings": "Configuración del servidor", + "HeaderServerAddressSettings": "Configuración de la dirección del servidor", + "HeaderSeriesStatus": "Estado de la serie", + "HeaderSeriesOptions": "Opciones de serie", + "HeaderSendMessage": "Enviar mensaje", + "HeaderSelectTranscodingPathHelp": "Examine o ingrese la ruta a utilizar para transcodificar archivos temporales. La carpeta debe ser grabable.", + "HeaderSelectTranscodingPath": "Seleccionar ruta temporal de transcodificación", + "HeaderSelectServerCachePathHelp": "Examine o ingrese la ruta a utilizar para los archivos de caché del servidor. La carpeta debe ser grabable.", + "HeaderSelectServerCachePath": "Seleccionar ruta de caché del servidor", + "HeaderSelectServer": "Seleccionar servidor", + "HeaderSelectPath": "Seleccionar ruta", + "HeaderSelectMetadataPathHelp": "Examine o ingrese la ruta en la que desea almacenar metadatos. La carpeta debe ser grabable.", + "HeaderSelectMetadataPath": "Seleccionar ruta de metadatos", + "HeaderSelectCertificatePath": "Seleccionar ruta del certificado", + "HeaderSecondsValue": "{0} segundos", + "HeaderSeasons": "Temporadas", + "HeaderSchedule": "Programación", + "HeaderScenes": "Escenas", + "HeaderRunningTasks": "Ejecución de tareas", + "HeaderRevisionHistory": "Revisión histórica", + "HeaderRestartingServer": "Reiniciando servidor", + "HeaderRestart": "Reiniciar", + "HeaderResponseProfile": "Perfil de respuesta", + "HeaderRemoveMediaLocation": "Eliminar ubicación de medios", + "HeaderRemoveMediaFolder": "Eliminar carpeta de medios", + "HeaderRemoteControl": "Control remoto", + "HeaderRemoteAccessSettings": "Configuración de acceso remoto", + "HeaderRecordingPostProcessing": "Grabación posterior al procesamiento", + "HeaderRecordingOptions": "Opciones de grabación", + "HeaderRecentlyPlayed": "Recientemente reproducido", + "HeaderProfileServerSettingsHelp": "Estos valores controlan cómo el servidor Jellyfin se presentará al dispositivo.", + "HeaderProfileInformation": "Información del perfil", + "HeaderProfile": "Perfil", + "HeaderPreferredMetadataLanguage": "Lenguaje de metadatos preferido", + "HeaderPluginInstallation": "Instalación de complementos", + "HeaderPleaseSignIn": "Por favor, inicie sesión", + "HeaderNextVideoPlayingInValue": "Reproducción del siguiente video en {0}", + "HeaderNextEpisodePlayingInValue": "Reproducción del siguiente episodio en {0}", + "HeaderMoreLikeThis": "Más como esto", + "HeaderMetadataSettings": "Configuraciones de metadatos", + "HeaderMediaInfo": "Información de medios", + "HeaderHttpsSettings": "Configuraciones HTTPS", + "HeaderEnabledFieldsHelp": "Desmarque un campo para bloquearlo y evitar que se modifiquen sus datos.", + "HeaderDirectPlayProfileHelp": "Agregue perfiles de reproducción directa para indicar qué formatos puede manejar el dispositivo de forma nativa.", + "ApiKeysCaption": "Lista de las claves de API habilitadas actualmente", + "LabelArtists": "Artistas:", + "LabelAppName": "Nombre de la aplicación", + "LabelAllowedRemoteAddressesMode": "Modo de filtro de dirección IP remota:", + "LabelAlbum": "Álbum:", + "LabelAirTime": "Tiempo al aire:", + "LabelAirDays": "Días al aire:", + "LabelAccessStart": "Hora de inicio:", + "LabelAccessEnd": "Hora de finalización:", + "HeaderDVR": "DVR" } diff --git a/src/strings/es-mx.json b/src/strings/es-mx.json index d8c313fd7a..b41385feb5 100644 --- a/src/strings/es-mx.json +++ b/src/strings/es-mx.json @@ -1,82 +1,81 @@ { "Absolute": "Absoluto", "Accept": "Aceptar", - "AccessRestrictedTryAgainLater": "El acceso está restringido actualmente. Por favor, inténtelo más tarde.", + "AccessRestrictedTryAgainLater": "El acceso está restringido actualmente. Por favor, inténtalo más tarde.", "Add": "Agregar", - "AddItemToCollectionHelp": "Agregue elementos a las colecciones buscándolos y utilizando sus menús al hacer clic derecho o al tocarlos para agregarlos a una colección.", + "AddItemToCollectionHelp": "Agrega elementos a las colecciones buscándolos y utilizando sus menúes al hacer clic derecho o al tocarlos para agregarlos a una colección.", "AddToCollection": "Agregar a colección", "AddToPlayQueue": "Agregar a la cola de reproducción", "AddToPlaylist": "Agregar a lista de reproducción", "AddedOnValue": "Agregado {0}", - "AdditionalNotificationServices": "Explore el catálogo de complementos para instalar servicios de notificaciones adicionales.", + "AdditionalNotificationServices": "Explora el catálogo de complementos para instalar servicios de notificaciones adicionales.", "AirDate": "Fecha de emisión", "Aired": "Transmitido", "Albums": "Álbumes", "Alerts": "Alertas", "All": "Todo", "AllChannels": "Todos los canales", - "AllComplexFormats": "Todos los formatos complejos (ASS, SSA, VOBSUB, PGS, SUB, IDX)", + "AllComplexFormats": "Todos los formatos complejos (ASS, SSA, VOBSUB, PGS, SUB, IDX...)", "AllEpisodes": "Todos los episodios", "AllLanguages": "Todos los idiomas", "AllLibraries": "Todas las bibliotecas", - "AllowHWTranscodingHelp": "Permite que el sintonizador transcodifique las transmisiones sobre la marcha. Esto puede ayudar a reducir la transcodificación requerida por el servidor.", + "AllowHWTranscodingHelp": "Permite al sintonizador transcodificar las transmisiones sobre la marcha. Esto puede ayudar a reducir la transcodificación requerida por el servidor.", "AllowMediaConversion": "Permitir conversión de medios", "AllowMediaConversionHelp": "Permitir o denegar acceso a la función de convertir medios.", "AllowOnTheFlySubtitleExtraction": "Permitir la extracción de subtítulos sobre la marcha", "AllowOnTheFlySubtitleExtractionHelp": "Los subtítulos incrustados pueden extraerse de los videos y entregarse a los clientes en texto plano para ayudar a evitar la transcodificación de video. En algunos sistemas, esto puede tardar mucho tiempo y provocar que la reproducción de video se detenga durante el proceso de extracción. Deshabilite esta opción para que los subtítulos incrustados se graben con transcodificación de video cuando no estén soportados de forma nativa por el dispositivo cliente.", - "AllowRemoteAccess": "Permitir conexiones remotas a este Servidor Jellyfin.", - "AllowRemoteAccessHelp": "Si se deshabilita, todas las conexiones remotas serán bloqueadas.", - "AllowedRemoteAddressesHelp": "Lista separada por comas de direcciones IP/mascaras de subred para las redes a las que se les permitirá conectarse remotamente. Si se deja en blanco, todas las IP remotas serán permitidas.", - "AlwaysPlaySubtitles": "Siempre mostrar subtítulos", - "AlwaysPlaySubtitlesHelp": "Los subtítulos que coincidan con el lenguaje preferido serán cargados independientemente del lenguaje del audio.", + "AllowRemoteAccess": "Permitir conexiones remotas a este servidor Jellyfin.", + "AllowRemoteAccessHelp": "Si no se marca, se bloquearán todas las conexiones remotas.", + "AllowedRemoteAddressesHelp": "Lista separada por comas de direcciones IP/máscaras de red para las redes a las que se les permitirá conectarse remotamente. Si se deja en blanco, se les permitirá a todas las direcciones remotas.", + "AlwaysPlaySubtitles": "Siempre reproducir", + "AlwaysPlaySubtitlesHelp": "Los subtítulos que coincidan con el idioma preferido serán cargados independientemente del idioma del audio.", "AnamorphicVideoNotSupported": "Video anamorfico no soportado", - "AndroidUnlockRestoreHelp": "Para restaurar su compra previa, por favor asegúrese de que se encuentra registrado en el dispositivo con la misma cuenta de Google (o Amazon) con que hizo la compra. Asegúrese que la tienda de aplicaciones esta habilitada, no esta restringida por cualquier control parental y que tiene una conexión de internet activa. Esto se tiene que hacer solo una vez para restaurar su compra previa.", "AnyLanguage": "Cualquier idioma", "Anytime": "En cualquier momento", - "AroundTime": "Alrededor de {0}", + "AroundTime": "Alrededor de", "Art": "Arte", "Artists": "Artistas", "AsManyAsPossible": "Tantos como sea posible", "Ascending": "Ascendente", "AspectRatio": "Relación de aspecto", "AttributeNew": "Nuevo", - "AutoBasedOnLanguageSetting": "Auto (basado en la configuración del lenguaje)", - "Backdrop": "Imagen de Fondo", + "AutoBasedOnLanguageSetting": "Auto (basado en la configuración del idioma)", + "Backdrop": "Imagen de fondo", "Backdrops": "Imágenes de fondo", - "Banner": "Cartel", + "Banner": "Banner", "BestFit": "Mejor ajuste", "BirthDateValue": "Nacimiento: {0}", "BirthLocation": "Lugar de nacimiento", "BirthPlaceValue": "Lugar de nacimiento: {0}", - "Blacklist": "Bloqueados", - "BookLibraryHelp": "Los libros de texto y audio libros son soportados. Revise la {0}Guía para Nomenclatura de Libros{1}.", + "Blacklist": "Lista negra", + "BookLibraryHelp": "Los libros de texto y audiolibros están soportados. Revisa la {0} guía de nombrado de libros {1}.", "Books": "Libros", "Box": "Caja", - "BoxRear": "Reverso de caja", - "Browse": "Navegar", - "BrowsePluginCatalogMessage": "Explorar el catalogo de complementos para ver los complementos disponibles.", - "BurnSubtitlesHelp": "Determina si el servidor debe grabar subtítulos al transcodificar videos. Evitar esto mejorará altamente el rendimiento del servidor. Seleccione Auto para grabar formatos basados en imágenes (VOBSUB, PGS, SUB/IDX) y ciertos subtítulos ASS o SSA.", + "BoxRear": "Caja (parte trasera)", + "Browse": "Explorar", + "BrowsePluginCatalogMessage": "Explora nuestro catálogo de complementos para ver los complementos disponibles.", + "BurnSubtitlesHelp": "Determina si el servidor debería quemar los subtítulos al transcodificar videos. Evitar esto mejorará altamente el rendimiento del servidor. Seleccione Auto para grabar formatos basados en imágenes (VOBSUB, PGS, SUB, IDX...) y ciertos subtítulos ASS o SSA.", "ButtonAdd": "Agregar", - "ButtonAddMediaLibrary": "Agregar Biblioteca de Medios", - "ButtonAddScheduledTaskTrigger": "Agregar Disparador", - "ButtonAddServer": "Agregar Servidor", - "ButtonAddUser": "Agregar Usuario", + "ButtonAddMediaLibrary": "Agregar biblioteca de medios", + "ButtonAddScheduledTaskTrigger": "Agregar disparador", + "ButtonAddServer": "Agregar servidor", + "ButtonAddUser": "Agregar usuario", "ButtonArrowDown": "Abajo", "ButtonArrowLeft": "Izquierda", "ButtonArrowRight": "Derecha", "ButtonArrowUp": "Arriba", - "ButtonAudioTracks": "Pistas de Audio", + "ButtonAudioTracks": "Pistas de audio", "ButtonBack": "Atrás", "ButtonCancel": "Cancelar", - "ButtonChangeServer": "Cambiar Servidor", + "ButtonChangeServer": "Cambiar servidor", "ButtonConnect": "Conectar", "ButtonDelete": "Eliminar", - "ButtonDeleteImage": "Eliminar Imagen", + "ButtonDeleteImage": "Eliminar imagen", "ButtonDown": "Abajo", "ButtonDownload": "Descargar", "ButtonEdit": "Editar", "ButtonEditImages": "Editar imágenes", - "ButtonEditOtherUserPreferences": "Editar el perfíl de este usuario. imágen y preferencias personales.", + "ButtonEditOtherUserPreferences": "Editar el perfil, la imagen y las preferencias personales de este usuario.", "ButtonFilter": "Filtro", "ButtonForgotPassword": "Olvidé mi contraseña", "ButtonFullscreen": "Pantalla completa", @@ -84,9 +83,9 @@ "ButtonGuide": "Guía", "ButtonHelp": "Ayuda", "ButtonHome": "Inicio", - "ButtonLearnMore": "Aprenda más", - "ButtonLibraryAccess": "Acceso a biblioteca", - "ButtonManualLogin": "Inicio de Sesión Manual", + "ButtonLearnMore": "Aprender más", + "ButtonLibraryAccess": "Acceso a biblioteca(s)", + "ButtonManualLogin": "Inicio de sesión manual", "ButtonMore": "Más", "ButtonNetwork": "Red", "ButtonNew": "Nuevo", @@ -97,29 +96,29 @@ "ButtonPause": "Pausar", "ButtonPlay": "Reproducir", "ButtonPreviousTrack": "Pista anterior", - "ButtonProfile": "Perfíl", - "ButtonQuickStartGuide": "Guía de Inicio Rápido", + "ButtonProfile": "Perfil", + "ButtonQuickStartGuide": "Guía de inicio rápido", "ButtonRefresh": "Actualizar", - "ButtonRefreshGuideData": "Actualizar Datos de la Guía", - "ButtonRemove": "Eliminar", + "ButtonRefreshGuideData": "Actualizar datos de la guía", + "ButtonRemove": "Remover", "ButtonRename": "Renombrar", "ButtonRepeat": "Repetir", - "ButtonResetEasyPassword": "Restaurar código pin sencillo", - "ButtonResetPassword": "Restablecer Contraseña", + "ButtonResetEasyPassword": "Restablecer código PIN sencillo", + "ButtonResetPassword": "Restablecer contraseña", "ButtonRestart": "Reiniciar", "ButtonResume": "Continuar", "ButtonRevoke": "Revocar", "ButtonSave": "Guardar", - "ButtonScanAllLibraries": "Escanear Todas las Bibliotecas", + "ButtonScanAllLibraries": "Escanear todas las bibliotecas", "ButtonSearch": "Búsqueda", - "ButtonSelectDirectory": "Seleccionar Carpeta", - "ButtonSelectServer": "Seleccionar Servidor", + "ButtonSelectDirectory": "Seleccionar directorio", + "ButtonSelectServer": "Seleccionar servidor", "ButtonSelectView": "Seleccionar vista", "ButtonSend": "Enviar", "ButtonSettings": "Configuración", "ButtonShuffle": "Aleatorio", "ButtonShutdown": "Apagar", - "ButtonSignIn": "Iniciar Sesión", + "ButtonSignIn": "Iniciar sesión", "ButtonSignOut": "Cerrar sesión", "ButtonSort": "Ordenar", "ButtonStart": "Iniciar", @@ -133,70 +132,70 @@ "CancelRecording": "Cancelar grabación", "CancelSeries": "Cancelar serie", "Categories": "Categorías", - "ChangingMetadataImageSettingsNewContent": "Cambios en las configuraciones de descarga de metadatos o arte solo se aplicaran a contenido nuevo agregado a su biblioteca. Para aplicar los cambios a los títulos existentes, necesita actualizar los metadatos manualmente.", - "ChannelAccessHelp": "Seleccione los canales a compartir con este usuario. Los administradores podrán editar todos los canales empleando el administrador de metadatos.", + "ChangingMetadataImageSettingsNewContent": "Cambios en las configuraciones de descarga de metadatos o arte solo se aplicarán a contenido nuevo agregado a tu biblioteca. Para aplicar los cambios a los títulos existentes, necesitarás actualizar sus metadatos manualmente.", + "ChannelAccessHelp": "Selecciona los canales a compartir con este usuario. Los administradores podrán editar todos los canales empleando el administrador de metadatos.", "ChannelNameOnly": "Canal {0} solamente", - "ChannelNumber": "Numero de canal", + "ChannelNumber": "Número de canal", "Channels": "Canales", - "CinemaModeConfigurationHelp": "El modo cine trae la experiencia del cine directo al la sala de TV con la habilidad de reproducir tráilers e intros personalizados antes de la presentación estelar.", + "CinemaModeConfigurationHelp": "El modo cine lleva la experiencia del cine directamente a tu sala de estar con la capacidad de reproducir trailers e introducciones personalizadas antes de la función principal.", "Collections": "Colecciones", "ColorPrimaries": "Colores primarios", "ColorSpace": "Espacio de color", "ColorTransfer": "Transferencia de color", "CommunityRating": "Calificación de la comunidad", "Composer": "Compositor", - "ConfigureDateAdded": "Configure como la fecha de adición es determinada en el Panel de Control del Servidor Jellyfin bajo la configuración de Bibliotecas", + "ConfigureDateAdded": "Configura cómo se determina la fecha de adición en el panel de control del servidor Jellyfin en la configuración de la biblioteca", "ConfirmDeleteImage": "¿Eliminar imagen?", - "ConfirmDeleteItem": "Al eliminar este ítem se eliminará tanto del sistema de archivos como de su biblioteca de medios. ¿Esta seguro de querer continuar?", - "ConfirmDeleteItems": "Al borrar estos items serán eliminados tanto del sistema de archivos como de la biblioteca de medios. ¿Esta seguro que desea continuar?", + "ConfirmDeleteItem": "Eliminar este elemento lo eliminará tanto del sistema como de tu biblioteca de medios. ¿Estás seguro de querer continuar?", + "ConfirmDeleteItems": "Eliminar estos elementos los eliminará tanto del sistema como de tu biblioteca de medios. ¿Estás seguro de querer continuar?", "ConfirmDeletion": "Confirmar eliminación", - "ConfirmEndPlayerSession": "¿Desea cerrar Jellyfin en {0}?", + "ConfirmEndPlayerSession": "¿Deseas apagar Jellyfin en {0}?", "Connect": "Conectar", "ContinueWatching": "Continuar viendo", "Continuing": "Continuando", - "CriticRating": "Calificación de la crítica", + "CriticRating": "Calificación de los críticos", "CustomDlnaProfilesHelp": "Crear un perfil personalizado para un nuevo dispositivo o reemplazar un perfil del sistema.", "DateAdded": "Fecha de adición", "DatePlayed": "Fecha de reproducción", - "DeathDateValue": "Fallcimiento: {0}", + "DeathDateValue": "Falleció: {0}", "Default": "Por defecto", - "DefaultErrorMessage": "Ha ocurrido un error al procesar la solicitud. Por favor inténtelo de nuevo mas tarde.", - "DefaultMetadataLangaugeDescription": "Estas son sus configuraciones por defecto y puedes ser personalizadas independientemente en cada biblioteca.", - "DefaultSubtitlesHelp": "Los subtitulos son cargados basados en los indicadores \"por defecto\" y \"forzado\" incluidos en los metadatos. Las preferencias de idioma son consideradas cuando hay múltiples opciones disponibles.", + "DefaultErrorMessage": "Ha ocurrido un error al procesar la solicitud. Por favor, inténtalo de nuevo más tarde.", + "DefaultMetadataLangaugeDescription": "Estos son sus valores por defecto y pueden ser personalizados en cada biblioteca.", + "DefaultSubtitlesHelp": "Los subtítulos se cargan basados en los indicadores «por defecto» y «forzado» incluidos en los metadatos. Las preferencias de idioma son consideradas cuando hay múltiples opciones disponibles.", "Delete": "Eliminar", - "DeleteDeviceConfirmation": "¿Está seguro de querer eliminar este dispositivo? Volverá a aparecer la siguiente vez que un usuario inicie sesión en él.", + "DeleteDeviceConfirmation": "¿Estás seguro de querer eliminar este dispositivo? Volverá a aparecer la siguiente vez que un usuario inicie sesión con él.", "DeleteImage": "Eliminar imagen", - "DeleteImageConfirmation": "¿Está seguro de querer eliminar esta imagen?", + "DeleteImageConfirmation": "¿Estás seguro de querer eliminar esta imagen?", "DeleteMedia": "Eliminar medios", - "DeleteUser": "Eliminar Usuario", - "DeleteUserConfirmation": "¿Está seguro de querer eliminar este usuario?", - "Depressed": "Depresión", + "DeleteUser": "Eliminar usuario", + "DeleteUserConfirmation": "¿Estás seguro de querer eliminar este usuario?", + "Depressed": "Presionado", "Descending": "Descendente", "Desktop": "Escritorio", "DetectingDevices": "Detectando dispositivos", - "DeviceAccessHelp": "Esto solo aplica a dispositivos que pueden ser identificados de manera individual y no evitará acceso al navegador. Al filtrar el acceso de usuarios a dispositivos se impedirá que utilicen nuevos dispositivos hasta que hayan sido aprobados aquí.", + "DeviceAccessHelp": "Esto solo se aplica a los dispositivos que pueden ser identificados de manera única y no impedirá el acceso desde navegadores. Filtrar el acceso a los dispositivos de los usuarios les impedirá usar nuevos dispositivos hasta que hayan sido aprobados aquí.", "DirectPlaying": "Reproducción directa", - "DirectStreamHelp1": "El medio es compatible con el dispositivo en cuanto a la resolución y tipo de medio (H.264, AC3, etc.), pero está en un contenedor de archivo incompatible (mkv, avi, wmv, etc.). El video sera reempaquetado en el acto antes de transmitirlo al dispositivo.", - "DirectStreamHelp2": "La Transmisión Directa de un archivo usa muy poco poder de procesamiento sin ninguna perdida en la calidad de video.", - "DirectStreaming": "Transmisión Directa", + "DirectStreamHelp1": "El medio es compatible con el dispositivo en cuanto a la resolución y tipo de medio (H.264, AC3, etc.), pero está en un contenedor de archivo incompatible (mkv, avi, wmv, etc.). El video será reempaquetado sobre la marcha antes de transmitirlo al dispositivo.", + "DirectStreamHelp2": "Transmitir directamente un archivo usa muy poco poder de procesamiento sin ninguna perdida en la calidad de video.", + "DirectStreaming": "Transmisión directa", "Directors": "Directores", "Disabled": "Desactivado", "Disc": "DIsco", "Disconnect": "Desconectar", "Dislike": "No me gusta", "Display": "Pantalla", - "DisplayInMyMedia": "Mostrar en pantalla de inicio", - "DisplayInOtherHomeScreenSections": "Mostrar en las secciones de la pantalla de inicio como Recientes o Continua Viendo", + "DisplayInMyMedia": "Mostrar en la pantalla de inicio", + "DisplayInOtherHomeScreenSections": "Mostrar en las secciones de la pantalla de inicio como recientes o continuar viendo", "DisplayMissingEpisodesWithinSeasons": "Mostrar episodios faltantes en las temporadas", "DisplayMissingEpisodesWithinSeasonsHelp": "Esto también debe estar habilitado para las bibliotecas de TV en la configuración del servidor.", - "DisplayModeHelp": "Seleccione el estilo de diseño que desea en la Interfaz.", + "DisplayModeHelp": "Selecciona el estilo de diseño que desea para la interfaz.", "DoNotRecord": "No grabar", "Down": "Abajo", "Download": "Descargar", "DownloadsValue": "{0} descargas", "DrmChannelsNotImported": "Los canales con DRM no serán importados.", - "DropShadow": "Sombra Paralela", - "EasyPasswordHelp": "Su código PIN fácil se utiliza para el acceso sin conexión en los clientes compatibles y también puede utilizarse para acceder fácilmente cuando se está en la misma red.", + "DropShadow": "Sombra paralela", + "EasyPasswordHelp": "Tu código PIN fácil se utiliza para el acceso sin conexión en los clientes soportados y también puede utilizarse para acceder fácilmente cuando se está en la misma red.", "Edit": "Editar", "EditImages": "Editar imágenes", "EditMetadata": "Editar metadatos", @@ -204,17 +203,17 @@ "EnableBackdrops": "Imágenes de fondo", "EnableBackdropsHelp": "Muestra imágenes de fondo en el fondo de algunas páginas mientras se navega por la biblioteca.", "EnableCinemaMode": "Modo cine", - "EnableColorCodedBackgrounds": "Fondos con código de color", + "EnableColorCodedBackgrounds": "Fondos de colores codificados", "EnableDisplayMirroring": "Duplicado de pantalla", "EnableExternalVideoPlayers": "Reproductores de video externos", "EnableExternalVideoPlayersHelp": "Un menú de reproductor externo se mostrara cuando inicie la reproducción de un video.", "EnableHardwareEncoding": "Habilitar codificación por hardware", "EnableNextVideoInfoOverlay": "Mostrar la información del siguiente video durante la reproducción", - "EnableNextVideoInfoOverlayHelp": "Al finalizar un video, mostrar información sobre el siguiente video a reproducir en la lista de reproducción.", + "EnableNextVideoInfoOverlayHelp": "Al finalizar un video, mostrar información sobre el siguiente video a reproducir en la lista de reproducción actual.", "EnablePhotos": "Mostrar fotografías", "EnablePhotosHelp": "Las imágenes serán detectadas y mostradas junto con otros archivos multimedia.", - "EnableStreamLooping": "Repetir automáticamente transmisiones en vivo", - "EnableStreamLoopingHelp": "Habilite esta opción si las transmisiones en vivo contienen solo unos pocos segundos de datos y necesitan ser solicitadas continuamente. Habilitarlo cuando no es requerido puede causar problemas.", + "EnableStreamLooping": "Repetir automáticamente las transmisiones en vivo", + "EnableStreamLoopingHelp": "Habilita esta opción si las transmisiones en vivo contienen solo unos pocos segundos de datos y necesitan ser solicitadas continuamente. Habilitar esto cuando no es requerido puede causar problemas.", "EnableThemeSongs": "Canciones temáticas", "EnableThemeSongsHelp": "Reproducir canciones temáticas en el fondo mientras se navega por la biblioteca.", "EnableThemeVideos": "Videos temáticos", @@ -222,21 +221,21 @@ "Ended": "Finalizado", "EndsAtValue": "Termina a las {0}", "Episodes": "Episodios", - "ErrorAddingListingsToSchedulesDirect": "Hubo un error agregando la programación de su cuenta de Schedules Direct. Schedules Direct solo permite un numero limitado de programaciones por cuenta. Tal vez necesite acceder al sitio web de Schedules Direct y eliminar otras programaciones de su cuenta antes de continuar.", - "ErrorAddingMediaPathToVirtualFolder": "Hubo un error agregando la ruta de medios. Por favor asegúrese de que la ruta es valida y que el proceso del Servidor Jellyfin tenga acceso a ese destino.", - "ErrorAddingTunerDevice": "Hubo un error al agregar el dispositivo sintonizador. Por favor asegúrese de que este disponible e intente de nuevo.", - "ErrorAddingXmlTvFile": "Hubo un error accediendo al archivo XMLTV. Por favor asegúrese de que el archivo existe e intente de nuevo.", - "ErrorDeletingItem": "Hubo un error eliminando el ítem del Servidor Jellyfin. Por favor verifique tenga permisos de escritura en la carpeta de medios e intente de nuevo.", - "ErrorGettingTvLineups": "Hubo un error al descargar la programación de TV. Por favor, asegúrese de que su información este correcta e inténtelo de nuevo.", - "ErrorMessageStartHourGreaterThanEnd": "El horario de fin debe ser mayor al de comienzo.", - "ErrorPleaseSelectLineup": "Por favor seleccione una programación e intente de nuevo. Si no hay disponible ninguna, entonces por favor verifique que su nombre de usuario, contraseña, y código postal sean correctos.", - "ErrorSavingTvProvider": "Hubo un error al salvar el proveedor de TV. Por favor asegúrese de que este disponible e intente de nuevo.", + "ErrorAddingListingsToSchedulesDirect": "Hubo un error agregando la programación de tu cuenta de Schedules Direct. Schedules Direct solo permite un numero limitado de programaciones por cuenta. Tal vez necesites acceder al sitio web de Schedules Direct y eliminar otras programaciones de tu cuenta antes de continuar.", + "ErrorAddingMediaPathToVirtualFolder": "Hubo un error agregando la ruta de medios. Por favor, asegúrate de que la ruta es válida y que el proceso del servidor Jellyfin tiene acceso a ese destino.", + "ErrorAddingTunerDevice": "Hubo un error al agregar el dispositivo sintonizador. Por favor, asegúrate de que esté disponible e inténtalo de nuevo.", + "ErrorAddingXmlTvFile": "Hubo un error accediendo al archivo XMLTV. Por favor, asegúrate de que el archivo existe e inténtalo de nuevo.", + "ErrorDeletingItem": "Hubo un error eliminando el elemento del servidor Jellyfin. Por favor, verifica que el servidor Jellyfin tiene permisos de escritura en la carpeta del medio e inténtalo de nuevo.", + "ErrorGettingTvLineups": "Hubo un error al descargar la programación de TV. Por favor, asegúrate de que tu información sea correcta e inténtalo de nuevo.", + "ErrorMessageStartHourGreaterThanEnd": "La hora de finalización debe ser mayor que la hora de inicio.", + "ErrorPleaseSelectLineup": "Por favor, selecciona una programación e inténtalo de nuevo. Si no hay disponible ninguna, entonces, por favor, verifica que tu nombre de usuario, contraseña, y código postal sean correctos.", + "ErrorSavingTvProvider": "Hubo un error al guardar el proveedor de TV. Por favor, asegúrate de que sea accesible e inténtalo de nuevo.", "EveryNDays": "Cada {0} días", - "ExitFullscreen": "Salir de Pantalla Completa", + "ExitFullscreen": "Salir de la pantalla completa", "ExtraLarge": "Extra grande", "ExtractChapterImagesHelp": "La extracción de imágenes de capítulos permitirá a los clientes mostrar menús gráficos de selección de escenas. El proceso puede ser lento, intensivo en recursos y puede requerir varios gigabytes de espacio. Se ejecuta cuando se descubren los videos y también como una tarea programada cada noche. La programación es configurable en el área de tareas programadas. No se recomienda ejecutar esta tarea durante las horas de mayor uso.", - "FFmpegSavePathNotFound": "No fue posible localizar FFmpeg usando la ruta que introdujo. FFprobe también es requerido y debe de estar en la misma carpeta. Estos componentes normalmente están empaquetados en la misma descarga. Por favor verifique la ruta e intente de nuevo.", - "FastForward": "Avance Rápido", + "FFmpegSavePathNotFound": "No fue posible localizar FFmpeg usando la ruta que has introducido. FFprobe también es requerido y debe de estar en la misma carpeta. Estos componentes normalmente están empaquetados en la misma descarga. Por favor, verifica la ruta e inténtalo de nuevo.", + "FastForward": "Avance rápido", "Favorite": "Favorito", "Favorites": "Favoritos", "Features": "Características", @@ -254,349 +253,349 @@ "Folders": "Carpetas", "FormatValue": "Formato: {0}", "Friday": "Viernes", - "Fullscreen": "Pantalla Completa", - "Genre": "Genero", + "Fullscreen": "Pantalla completa", + "Genre": "Género", "Genres": "Géneros", "GroupBySeries": "Agrupar por series", "GroupVersions": "Agrupar versiones", "GuestStar": "Estrella invitada", "Guide": "Guía", - "GuideProviderLogin": "Iniciar Sesión", - "GuideProviderSelectListings": "Elegir Listados", - "H264CrfHelp": "El \"Factor de Transferencia Constante\" o \"Constant Rate Factor\" (CFR) es la configuración por defecto para el codificador x264. Puede poner valores entre 0 y 51, donde los valores mas bajos dan como resultado mejor calidad (a expensas de archivos mas grandes). Los valores comunes son entre 18 y 28. El valor por defecto para x264 es 23, puede usar este valor como punto de referencia.", - "EncoderPresetHelp": "Elija un valor mas rápido para mejorar el rendimiento, o uno mas lento para mejorar la calidad.", + "GuideProviderLogin": "Iniciar sesión", + "GuideProviderSelectListings": "Elegir listados", + "H264CrfHelp": "El «Factor de transferencia constante» (CRF) es la configuración de calidad por defecto para el codificador x264. Puedes establecer valores entre 0 y 51, donde los valores más bajos dan como resultado mejor calidad (a expensas de archivos más grandes). Los valores comunes son entre 18 y 28. El valor por defecto para x264 es 23, así que puedes usar este valor como punto de referencia.", + "EncoderPresetHelp": "Elige un valor más rápido para mejorar el rendimiento, o uno más lento para mejorar la calidad.", "HDPrograms": "Programas en HD", "HandledByProxy": "Manejado por un proxy inverso", - "HardwareAccelerationWarning": "Habilitar la aceleración por hardware podría causar inestabilidad en algunos entornos. Asegúrese de que su sistema operativo y controladores de video están actualizados. Si tiene dificultades reproduciendo vides después de habilitar esto, necesita volver a cambiar la configuración a NO.", - "HeaderAccessSchedule": "Acceder Programación", - "HeaderAccessScheduleHelp": "Crear programación de acceso para limitar el acceso a ciertos horarios.", - "HeaderActiveDevices": "Dispositivos Activos", - "HeaderActiveRecordings": "Grabaciones Activas", + "HardwareAccelerationWarning": "Habilitar la aceleración por hardware podría causar inestabilidad en algunos entornos. Asegúrate de que tu sistema operativo y controladores de video están actualizados. Si tienes dificultades reproduciendo videos después de habilitar esto, necesitarás volver a cambiar la configuración a Ninguno.", + "HeaderAccessSchedule": "Programación de acceso", + "HeaderAccessScheduleHelp": "Crea una programación de acceso para limitar el acceso a ciertos horarios.", + "HeaderActiveDevices": "Dispositivos activos", + "HeaderActiveRecordings": "Grabaciones activas", "HeaderActivity": "Actividad", - "HeaderAddScheduledTaskTrigger": "Agregar Disparador", - "HeaderAddToCollection": "Agregar a Colección", - "HeaderAddToPlaylist": "Agregar a Lista de Reproducción", + "HeaderAddScheduledTaskTrigger": "Agregar disparador", + "HeaderAddToCollection": "Agregar a colección", + "HeaderAddToPlaylist": "Agregar a lista de reproducción", "HeaderAddUpdateImage": "Agregar/Actualizar Imagen", - "HeaderAddUser": "Agregar Usuario", - "HeaderAdditionalParts": "Partes Adicionales", + "HeaderAddUser": "Agregar usuario", + "HeaderAdditionalParts": "Partes adicionales", "HeaderAlbumArtists": "Artistas del álbum", "HeaderAlbums": "Álbumes", "HeaderAlert": "Alerta", - "HeaderAllowMediaDeletionFrom": "Permitir Eliminacion de Medios De", + "HeaderAllowMediaDeletionFrom": "Permitir eliminación de medios de", "HeaderApiKey": "Clave API", - "HeaderApiKeys": "Claves de API", - "HeaderApiKeysHelp": "Son necesarias aplicaciones externas para obtener una clave API para comunicarse con el Servidor Jellyfin. Las clave son emitidas accediendo con una cuenta Jellyfin, o bien concediéndole manualmente una clave a la aplicación.", - "HeaderAppearsOn": "Aparece En", - "HeaderAudioBooks": "Audio Libros", - "HeaderAudioSettings": "Configuración de Audio", - "HeaderAutomaticUpdates": "Actualizaciones Automáticas", - "HeaderBlockItemsWithNoRating": "Bloquear ítems sin clasificación o con información de clasificación desconocida:", + "HeaderApiKeys": "Claves API", + "HeaderApiKeysHelp": "Las aplicaciones externas deben tener una clave API para poder comunicarse con el servidor Jellyfin. Las claves se emiten al iniciar sesión con una cuenta Jellyfin, o al otorgar manualmente una clave a la aplicación.", + "HeaderAppearsOn": "Aparece en", + "HeaderAudioBooks": "Audiolibros", + "HeaderAudioSettings": "Configuración de audio", + "HeaderAutomaticUpdates": "Actualizaciones automáticas", + "HeaderBlockItemsWithNoRating": "Bloquear elementos sin clasificación o con información de clasificación desconocida:", "HeaderBooks": "Libros", - "HeaderBranding": "Establecer Marca", - "HeaderCancelRecording": "Cancelar Grabación", - "HeaderCancelSeries": "Cancelar Serie", - "HeaderCastAndCrew": "Reparto & Personal", - "HeaderCastCrew": "Reparto y Personal", - "HeaderChannelAccess": "Acceso a los Canales", + "HeaderBranding": "Establecer marca", + "HeaderCancelRecording": "Cancelar grabación", + "HeaderCancelSeries": "Cancelar serie", + "HeaderCastAndCrew": "Reparto y equipo", + "HeaderCastCrew": "Reparto y equipo", + "HeaderChannelAccess": "Acceso a los canales", "HeaderChannels": "Canales", - "HeaderChapterImages": "Imagenes de Capitulo", - "HeaderCodecProfile": "Perfil de Codecs", - "HeaderCodecProfileHelp": "Los perfiles de codificación indican las limitaciones de un dispositivo al reproducir con codecs específicos. Si aplica una limitación el medio será transcodificado, aún si el codec ha sido configurado para reprodución directa.", - "HeaderConfigureRemoteAccess": "Configurar Acceso Remoto", - "HeaderConfirmPluginInstallation": "Confirmar Instalación de Complemento", - "HeaderConfirmProfileDeletion": "Confirmar Eliminación del Perfil", - "HeaderConfirmRevokeApiKey": "Revocar llave de API", + "HeaderChapterImages": "Imágenes de los capítulos", + "HeaderCodecProfile": "Perfil de códec", + "HeaderCodecProfileHelp": "Los perfiles de códecs indican las limitaciones de un dispositivo al reproducir códecs específicos. Si una limitación se aplica entonces el medio será transcodificado, incluso si el códec ha sido configurado para reproducción directa.", + "HeaderConfigureRemoteAccess": "Configurar acceso remoto", + "HeaderConfirmPluginInstallation": "Confirmar instalación de complemento", + "HeaderConfirmProfileDeletion": "Confirmar eliminación de perfil", + "HeaderConfirmRevokeApiKey": "Revocar clave API", "HeaderConnectToServer": "Conectarse al servidor", - "HeaderConnectionFailure": "Falla de Conexión", - "HeaderContainerProfile": "Perfil del Contenedor", - "HeaderContainerProfileHelp": "Los perfiles de contenedor indican las limitaciones de un dispositivo al reproducir formatos específicos. Si aplica una limitación el medio será transcodificado, aún si el formato ha sifo configurado para reproducción directa.", - "HeaderContinueListening": "Continuar Escuchando", - "HeaderContinueWatching": "Continuar Viendo", - "HeaderCustomDlnaProfiles": "Perfiles Personalizados", - "HeaderDateIssued": "Fecha de Emisión", - "HeaderDefaultRecordingSettings": "Configuración Predeterminada de Grabaciones", - "HeaderDeleteDevice": "Eliminar Dispositivo", - "HeaderDeleteItem": "Eliminar Ítem", - "HeaderDeleteItems": "Borrar items", - "HeaderDeleteProvider": "Eliminar Proveedor", - "HeaderDeleteTaskTrigger": "Borrar Disparador de Tarea", - "HeaderDetectMyDevices": "Detectar Mis Dispositivos", + "HeaderConnectionFailure": "Falla de conexión", + "HeaderContainerProfile": "Perfil del contenedor", + "HeaderContainerProfileHelp": "Los perfiles de contenedor indican las limitaciones de un dispositivo al reproducir formatos específicos. Si una limitación se aplica entonces el medio será transcodificado, incluso si el formato ha sido configurado para reproducción directa.", + "HeaderContinueListening": "Continuar escuchando", + "HeaderContinueWatching": "Continuar viendo", + "HeaderCustomDlnaProfiles": "Perfiles personalizados", + "HeaderDateIssued": "Fecha de emisión", + "HeaderDefaultRecordingSettings": "Configuración predeterminada de grabaciones", + "HeaderDeleteDevice": "Eliminar dispositivo", + "HeaderDeleteItem": "Eliminar elemento", + "HeaderDeleteItems": "Eliminar elementos", + "HeaderDeleteProvider": "Eliminar proveedor", + "HeaderDeleteTaskTrigger": "Borrar disparador de tarea", + "HeaderDetectMyDevices": "Detectar mis dispositivos", "HeaderDeveloperInfo": "Info del desarrollador", - "HeaderDeviceAccess": "Acceso a Dispositivos", + "HeaderDeviceAccess": "Acceso a dispositivos", "HeaderDevices": "Dispositivos", - "HeaderDirectPlayProfile": "Perfil de Reproducción Directa", - "HeaderDirectPlayProfileHelp": "Agregue perfiles de reproducción directa para indicar que formatos puede manejar el dispositivo de manera nativa.", + "HeaderDirectPlayProfile": "Perfil de reproducción directa", + "HeaderDirectPlayProfileHelp": "Agrega perfiles de reproducción directa para indicar qué formatos puede manejar el dispositivo de forma nativa.", "HeaderDisplay": "Pantalla", - "HeaderDownloadSync": "Descargar y Sincronizar", - "HeaderEasyPinCode": "Código Pin Sencillo", + "HeaderDownloadSync": "Descargar y sincronizar", + "HeaderEasyPinCode": "Código PIN sencillo", "HeaderEditImages": "Editar imágenes", - "HeaderEnabledFields": "Campos Habilitados", - "HeaderEnabledFieldsHelp": "Desmarcar un campo para bloquearlo y prevenir que sus datos cambien.", + "HeaderEnabledFields": "Campos habilitados", + "HeaderEnabledFieldsHelp": "Desmarca un campo para bloquearlo y prevenir que sus datos sean cambiados.", "HeaderEpisodes": "Episodios", - "HeaderExternalIds": "IDs Externos:", - "HeaderFeatureAccess": "Permisos de acceso", + "HeaderExternalIds": "IDs externos:", + "HeaderFeatureAccess": "Acceso a características", "HeaderFeatures": "Características", - "HeaderFetchImages": "Buscar imágenes:", - "HeaderFetcherSettings": "Configuración de Recolectores", + "HeaderFetchImages": "Obtener imágenes:", + "HeaderFetcherSettings": "Configuración del recolector", "HeaderFilters": "Filtros", - "HeaderForKids": "Para Niños", - "HeaderForgotPassword": "Contraseña Olvidada", - "HeaderFrequentlyPlayed": "Reproducido Frecuentemente", + "HeaderForKids": "Para niños", + "HeaderForgotPassword": "Olvidé mi contraseña", + "HeaderFrequentlyPlayed": "Reproducido frecuentemente", "HeaderGenres": "Géneros", "HeaderGuideProviders": "Proveedores de Guías de TV", "HeaderHttpHeaders": "Encabezados HTTP", "HeaderIdentification": "Identificación", "HeaderIdentificationCriteriaHelp": "Introduzca, al menos, un criterio de identificación.", - "HeaderIdentificationHeader": "Encabezado de Identificación", - "HeaderIdentifyItemHelp": "Introduzca uno o más criterios de búsqueda. Elimine criterios para expandir los resultados.", - "HeaderImageOptions": "Opciones de Imagen", - "HeaderImageSettings": "Configuración de Imágenes", + "HeaderIdentificationHeader": "Encabezado de identificación", + "HeaderIdentifyItemHelp": "Introduce uno o más criterios de búsqueda. Elimina criterios para expandir los resultados.", + "HeaderImageOptions": "Opciones de imagen", + "HeaderImageSettings": "Configuración de imagen", "HeaderInstall": "Instalar", - "HeaderInstantMix": "Mezcla Instantánea", - "HeaderItems": "Ítems", - "HeaderKeepRecording": "Conservar Grabaciones", - "HeaderKeepSeries": "Conservar Serie", - "HeaderKodiMetadataHelp": "Para habilitar o deshabilitar los metadatos Nfo, edite una biblioteca en la configuracion de bibliotecas de Jellyfin y busque la sección de grabadores de metadatos.", - "HeaderLatestEpisodes": "Episodios Recientes", - "HeaderLatestMedia": "Agregadas Recientemente", - "HeaderLatestMovies": "Películas Recientes", - "HeaderLatestMusic": "Música Reciente", - "HeaderLatestRecordings": "Grabaciones Recientes", + "HeaderInstantMix": "Mix instantáneo", + "HeaderItems": "Elementos", + "HeaderKeepRecording": "Conservar grabación", + "HeaderKeepSeries": "Conservar serie", + "HeaderKodiMetadataHelp": "Para habilitar o deshabilitar los metadatos NFO, edite una biblioteca en la configuración de bibliotecas de Jellyfin y ubica la sección grabadores de metadatos.", + "HeaderLatestEpisodes": "Últimos episodios", + "HeaderLatestMedia": "Últimos medios", + "HeaderLatestMovies": "Últimas películas", + "HeaderLatestMusic": "Última música", + "HeaderLatestRecordings": "Últimas grabaciones", "HeaderLibraries": "Bibliotecas", - "HeaderLibraryAccess": "Acceso a la Biblioteca", - "HeaderLibraryFolders": "Carpetas de Biblioteca", - "HeaderLibraryOrder": "Orden de Bibliotecas", - "HeaderLibrarySettings": "Configuraciones de Biblioteca", - "HeaderLiveTV": "TV en Vivo", - "HeaderLiveTv": "TV en Vivo", + "HeaderLibraryAccess": "Acceso a bibliotecas", + "HeaderLibraryFolders": "Carpetas de bibliotecas", + "HeaderLibraryOrder": "Orden de las bibliotecas", + "HeaderLibrarySettings": "Configuraciones de biblioteca", + "HeaderLiveTV": "TV en vivo", + "HeaderLiveTv": "TV en vivo", "HeaderLiveTvTunerSetup": "Configuración del sintonizador de TV", - "HeaderLoginFailure": "Falló el Inicio de Sesión", + "HeaderLoginFailure": "Falló el inicio de sesión", "HeaderMedia": "Medios", - "HeaderMediaFolders": "Carpetas de Medios", - "HeaderMediaInfo": "Info del Medio", + "HeaderMediaFolders": "Carpetas de medios", + "HeaderMediaInfo": "Info del medio", "HeaderMetadataSettings": "Configuración de metadatos", - "HeaderMoreLikeThis": "Mas Como Esto", + "HeaderMoreLikeThis": "Más como esto", "HeaderMovies": "Películas", - "HeaderMusicQuality": "Calidad de Musica", - "HeaderMusicVideos": "Videos Musicales", - "HeaderMyDevice": "Mi Dispositivo", - "HeaderMyMedia": "Mis Medios", + "HeaderMusicQuality": "Calidad de la música", + "HeaderMusicVideos": "Videos musicales", + "HeaderMyDevice": "Mi dispositivo", + "HeaderMyMedia": "Mis medios", "HeaderMyMediaSmall": "Mis medios (pequeño)", "HeaderNewApiKey": "Nueva clave API", - "HeaderNewDevices": "Nuevos Dispositivos", - "HeaderNextEpisodePlayingInValue": "El Siguiente Episodio se Reproducirá en {0}", - "HeaderNextUp": "A Continuación", - "HeaderNextVideoPlayingInValue": "El Siguiente Video se Reproducirá en {0}", - "HeaderOnNow": "Transmitiéndo Ahora", - "HeaderOtherItems": "Otros Ítems", - "HeaderParentalRatings": "Clasificación Parental", + "HeaderNewDevices": "Nuevos dispositivos", + "HeaderNextEpisodePlayingInValue": "El siguiente episodio se reproducirá en {0}", + "HeaderNextUp": "A continuación", + "HeaderNextVideoPlayingInValue": "El siguiente video se reproducirá en {0}", + "HeaderOnNow": "Transmitiendo ahora", + "HeaderOtherItems": "Otros elementos", + "HeaderParentalRatings": "Clasificación parental", "HeaderPassword": "Contraseña", - "HeaderPasswordReset": "Restablecer Contraseña", + "HeaderPasswordReset": "Restablecer contraseña", "HeaderPaths": "Rutas", - "HeaderPendingInvitations": "Invitaciones Pendientes", + "HeaderPendingInvitations": "Invitaciones pendientes", "HeaderPeople": "Personas", - "HeaderPhotoAlbums": "Álbumes de Fotos", - "HeaderPinCodeReset": "Restablecer Código Pin", - "HeaderPlayAll": "Reproducir Todo", - "HeaderPlayOn": "Reproducir En", - "HeaderPlayback": "Reproducción de Medios", - "HeaderPlaybackError": "Error de Reproducción", - "HeaderPleaseSignIn": "Por favor inicie sesión", + "HeaderPhotoAlbums": "Álbumes de fotos", + "HeaderPinCodeReset": "Restablecer código PIN", + "HeaderPlayAll": "Reproducir todo", + "HeaderPlayOn": "Reproducir en", + "HeaderPlayback": "Reproducción de medios", + "HeaderPlaybackError": "Error de reproducción", + "HeaderPleaseSignIn": "Por favor, inicia sesión", "HeaderPluginInstallation": "Instalación de complemento", - "HeaderPreferredMetadataLanguage": "Idioma Preferido para Metadatos", - "HeaderProfile": "Perfíl", - "HeaderProfileInformation": "Información de Perfil", - "HeaderProfileServerSettingsHelp": "Estos valores controlan como el Servidor Jellyfin se presentara al dispositivo.", - "HeaderRecentlyPlayed": "Reproducido Recientemente", - "HeaderRecordingOptions": "Opciones de Grabación", - "HeaderRecordingPostProcessing": "Post Procesado de las Grabaciones", - "HeaderRemoteControl": "Control Remoto", - "HeaderRemoveMediaFolder": "Eliminar Carpteta de Medios", - "HeaderRemoveMediaLocation": "Eliminar Ubicación de Medios", - "HeaderResponseProfile": "Perfil de Respuesta", - "HeaderResponseProfileHelp": "Los perfiles de respuesta proporcionan un medio para personalizar la información enviada a un dispositivo cuando se reproducen ciertos tipos de medios.", + "HeaderPreferredMetadataLanguage": "Idioma preferido para los metadatos", + "HeaderProfile": "Perfil", + "HeaderProfileInformation": "Información del perfil", + "HeaderProfileServerSettingsHelp": "Estos valores controlan como el servidor Jellyfin se presentará al dispositivo.", + "HeaderRecentlyPlayed": "Reproducido recientemente", + "HeaderRecordingOptions": "Opciones de grabación", + "HeaderRecordingPostProcessing": "Post procesado de las grabaciones", + "HeaderRemoteControl": "Control remoto", + "HeaderRemoveMediaFolder": "Remover carpeta de medios", + "HeaderRemoveMediaLocation": "Remover ubicación de medios", + "HeaderResponseProfile": "Perfil de respuesta", + "HeaderResponseProfileHelp": "Los perfiles de respuesta proporcionan un medio para personalizar la información enviada al dispositivo cuando se reproducen ciertos tipos de medios.", "HeaderRestart": "Reiniciar", - "HeaderRevisionHistory": "Historial de Versiones", - "HeaderRunningTasks": "Tareas en Ejecución", + "HeaderRevisionHistory": "Historial de versiones", + "HeaderRunningTasks": "Tareas en ejecución", "HeaderScenes": "Escenas", "HeaderSchedule": "Programación", "HeaderSeasons": "Temporadas", - "HeaderSecondsValue": "{0} Segundos", - "HeaderSelectCertificatePath": "Seleccione la Ruta del Certificado", - "HeaderSelectMetadataPath": "Seleccionar Ruta para Metadatos", - "HeaderSelectMetadataPathHelp": "Explore o introduzca la ruta donde desea almacenar los metadatos. La carpeta debe tener permisos de escritura.", - "HeaderSelectPath": "Seleccionar Ruta", - "HeaderSelectServer": "Seleccionar Servidor", - "HeaderSelectServerCachePath": "Seleccionar ruta para Caché del Servidor", - "HeaderSelectServerCachePathHelp": "Explore o introduzca la ruta a utilizar para los archivos del caché del servidor. La carpeta debe tener permisos de escritura.", - "HeaderSelectTranscodingPath": "Seleccionar Ruta para Transcodificación Temporal", - "HeaderSelectTranscodingPathHelp": "Explore o introduzca la ruta a utilizar para los archivos temporales de transcodificación. La carpeta debe tener permisos de escritura.", - "HeaderSendMessage": "Enviar Mensaje", + "HeaderSecondsValue": "{0} segundos", + "HeaderSelectCertificatePath": "Selecciona la ruta del certificado", + "HeaderSelectMetadataPath": "Selecciona la ruta para los metadatos", + "HeaderSelectMetadataPathHelp": "Explora o introduce la ruta donde deseas almacenar los metadatos. Se debe tener permisos de escritura en dicha carpeta.", + "HeaderSelectPath": "Seleccionar ruta", + "HeaderSelectServer": "Seleccionar servidor", + "HeaderSelectServerCachePath": "Seleccionar ruta para la caché del servidor", + "HeaderSelectServerCachePathHelp": "Explora o introduce la ruta a utilizar para los archivos caché del servidor. Se debe tener permisos de escritura en dicha carpeta.", + "HeaderSelectTranscodingPath": "Selecciona la ruta para los archivos temporales de transcodificación", + "HeaderSelectTranscodingPathHelp": "Explora o introduce la ruta a utilizar para los archivos temporales de transcodificación. Se debe tener permisos de escritura en dicha carpeta.", + "HeaderSendMessage": "Enviar mensaje", "HeaderSeries": "Series", - "HeaderSeriesOptions": "Opciones de Serie", - "HeaderSeriesStatus": "Estado de la Serie", - "HeaderServerSettings": "Configuración del Servidor", + "HeaderSeriesOptions": "Opciones de serie", + "HeaderSeriesStatus": "Estado de la serie", + "HeaderServerSettings": "Configuración del servidor", "HeaderSettings": "Configuración", - "HeaderSetupLibrary": "Configurar sus bibliotecas de medios", + "HeaderSetupLibrary": "Configura tus bibliotecas de medios", "HeaderShutdown": "Apagar", - "HeaderSortBy": "Ordenar Por", - "HeaderSortOrder": "Ordenado Por", - "HeaderSpecialEpisodeInfo": "Información del Episodio Especial", - "HeaderSpecialFeatures": "Características Especiales", - "HeaderStartNow": "Iniciar Ahora", + "HeaderSortBy": "Ordenar por", + "HeaderSortOrder": "Clasificar ordenado", + "HeaderSpecialEpisodeInfo": "Información del episodio especial", + "HeaderSpecialFeatures": "Características especiales", + "HeaderStartNow": "Iniciar ahora", "HeaderStatus": "Estado", - "HeaderStopRecording": "Detener Grabación", - "HeaderSubtitleAppearance": "Apariencia de Subtitulos", - "HeaderSubtitleDownloads": "Descarga de Subtitulos", - "HeaderSubtitleProfile": "Perfíl de Subtítulo", - "HeaderSubtitleProfiles": "Perfiles de Subtítulos", - "HeaderSubtitleProfilesHelp": "Los perfiles de subtítulos describen el formato del subtítulo soportado por el dispositivo.", - "HeaderSystemDlnaProfiles": "Perfiles del Sistema", + "HeaderStopRecording": "Detener grabación", + "HeaderSubtitleAppearance": "Apariencia de subtítulos", + "HeaderSubtitleDownloads": "Descarga de subtítulos", + "HeaderSubtitleProfile": "Perfil de subtítulo", + "HeaderSubtitleProfiles": "Perfiles de subtítulo", + "HeaderSubtitleProfilesHelp": "Los perfiles de subtítulo describen los formatos de subtítulo soportados por el dispositivo.", + "HeaderSystemDlnaProfiles": "Perfiles del sistema", "HeaderTags": "Etiquetas", - "HeaderTaskTriggers": "Disparadores de Tarea", + "HeaderTaskTriggers": "Disparadores de tarea", "HeaderThisUserIsCurrentlyDisabled": "Este usuario se encuentra actualmente deshabilitado", "HeaderTracks": "Pistas", - "HeaderTranscodingProfile": "Perfil de Transcodificación", - "HeaderTranscodingProfileHelp": "Agruegue perfiles de transcodificación para indicar que formatos deben ser usados cuando se requiera transcodificar.", + "HeaderTranscodingProfile": "Perfil de transcodificación", + "HeaderTranscodingProfileHelp": "Agrega perfiles de transcodificación para indicar qué formatos deben ser usados cuando se requiere transcodificar.", "HeaderTunerDevices": "Sintonizadores", "HeaderTuners": "Sintonizador", - "HeaderTypeImageFetchers": "{0} Recolectores de Imágenes", - "HeaderTypeText": "Introduzca Texto", + "HeaderTypeImageFetchers": "Recolectores de imágenes para {0}", + "HeaderTypeText": "Introducir texto", "HeaderUpcomingOnTV": "Próximamente en TV", - "HeaderUploadImage": "Subir Imagen", + "HeaderUploadImage": "Subir imagen", "HeaderUser": "Usuario", "HeaderUsers": "Usuarios", - "HeaderVideoQuality": "Calidad de Video", - "HeaderVideoType": "Tipo de Video", - "HeaderVideoTypes": "Tipos de Video", - "HeaderXmlDocumentAttribute": "Atributo del Documento XML", - "HeaderXmlDocumentAttributes": "Atributos del Documento XML", + "HeaderVideoQuality": "Calidad de video", + "HeaderVideoType": "Tipo de video", + "HeaderVideoTypes": "Tipos de video", + "HeaderXmlDocumentAttribute": "Atributo del documento XML", + "HeaderXmlDocumentAttributes": "Atributos del documento XML", "HeaderXmlSettings": "Configuración XML", "HeaderYears": "Años", "HeadersFolders": "Carpetas", "Help": "Ayuda", "Hide": "Ocultar", - "HideWatchedContentFromLatestMedia": "Ocultar contenido ya visto de Agregadas Recientemente", + "HideWatchedContentFromLatestMedia": "Ocultar contenido ya visto de últimos medios", "Home": "Inicio", - "HttpsRequiresCert": "Para habilitar las conexiones seguras, es necesario proporcionar un certificado SSL de confianza, como el de \"Let's Encrypt\". Por favor proporcione un certificado, o desactive las conexiones seguras.", + "HttpsRequiresCert": "Para habilitar las conexiones seguras, necesitarás proporcionar un certificado SSL de confianza, como el de Let's Encrypt. Por favor, proporciona un certificado o desactiva las conexiones seguras.", "Identify": "Identificar", "Images": "Imágenes", "ImportFavoriteChannelsHelp": "Si se habilita, solo los canales marcados como favoritos en el dispositivo sintonizador serán importados.", - "ImportMissingEpisodesHelp": "Si se habilita, se importara a su base de datos de Jellyfin información sobre episodios faltantes y se mostrara dentro de las temporadas y series. Esto podría ocasionar escaneos de biblioteca significativamente mas largos.", + "ImportMissingEpisodesHelp": "Si se habilita, la información sobre los episodios faltantes se importará a la base de datos de Jellyfin y se mostrarán dentro de las temporadas y series. Esto puede causar escaneos de biblioteca significativamente más largos.", "InstallingPackage": "Instalando {0} (versión {1})", "InstantMix": "Mix instantáneo", - "ItemCount": "{0} ítems", - "Items": "Ítems", + "ItemCount": "{0} elementos", + "Items": "Elementos", "Kids": "Niños", "Label3DFormat": "Formato 3D:", - "LabelAbortedByServerShutdown": "(Abortada por apagado del servidor)", + "LabelAbortedByServerShutdown": "(Abortado por apagado del servidor)", "LabelAccessDay": "Día de la semana:", - "LabelAccessEnd": "Horario de fin:", - "LabelAccessStart": "Horario de comienzo:", + "LabelAccessEnd": "Hora de finalización:", + "LabelAccessStart": "Hora de inicio:", "LabelAirDays": "Se emite los días:", "LabelAirTime": "Duración:", "LabelAirsAfterSeason": "Transmisión después de la temporada:", "LabelAirsBeforeEpisode": "Transmisión antes del episodio:", "LabelAirsBeforeSeason": "Transmisión antes de la temporada:", "LabelAlbum": "Álbum:", - "LabelAlbumArtHelp": "PN usado para arte del álbum, dentro del atributo dlna:profileID en upnp:albumArtURI. Algunos dispositivos requieren valores específicos, independientemente del tamaño de la imagen.", - "LabelAlbumArtMaxHeight": "Altura máxima para arte del álbum:", - "LabelAlbumArtMaxHeightHelp": "Máxima resolución para arte del album expuesta via upnp:albumArtURI.", - "LabelAlbumArtMaxWidth": "Ancho máximo para arte del álbum:", - "LabelAlbumArtMaxWidthHelp": "Máxima resolución para arte del album expuesta via upnp:albumArtURI.", - "LabelAlbumArtPN": "PN para arte del álbum:", + "LabelAlbumArtHelp": "PN usado para el arte del álbum, dentro del atributo dlna:profileID en upnp:albumArtURI. Algunos dispositivos requieren valores específicos, independientemente del tamaño de la imagen.", + "LabelAlbumArtMaxHeight": "Altura máxima del arte del álbum:", + "LabelAlbumArtMaxHeightHelp": "Resolución máxima del arte del álbum expuesta vía upnp:albumArtURI.", + "LabelAlbumArtMaxWidth": "Ancho máximo del arte del álbum:", + "LabelAlbumArtMaxWidthHelp": "Resolución máxima del arte del álbum expuesta vía upnp:albumArtURI.", + "LabelAlbumArtPN": "PN del arte del álbum:", "LabelAlbumArtists": "Artistas del álbum:", "LabelAll": "Todos", - "LabelAllowHWTranscoding": "Permitir transcodificacion de hardware", - "LabelAllowServerAutoRestart": "Permite al servidor reiniciar automáticamente para aplicar actualizaciones", + "LabelAllowHWTranscoding": "Permitir transcodificación por hardware", + "LabelAllowServerAutoRestart": "Permite al servidor reiniciarse automáticamente para aplicar actualizaciones", "LabelAllowServerAutoRestartHelp": "El servidor solo se reiniciará durante los períodos de inactividad cuando no haya usuarios activos.", - "LabelAllowedRemoteAddresses": "Filtrar IP remota:", - "LabelAllowedRemoteAddressesMode": "Modo de filtrado de IP remota:", - "LabelAppName": "Nombre del App", + "LabelAllowedRemoteAddresses": "Filtro de direcciones IP remotas:", + "LabelAllowedRemoteAddressesMode": "Modo de filtrado de direcciones IP remotas:", + "LabelAppName": "Nombre de la aplicación", "LabelAppNameExample": "Ejemplo: Sickbeard, Sonarr", "LabelArtists": "Artistas:", "LabelArtistsHelp": "Separar múltiples empleando ;", "LabelAudioLanguagePreference": "Idioma preferido de audio:", - "LabelAutomaticallyRefreshInternetMetadataEvery": "Actualizar automáticamente metadatos de internet:", - "LabelBindToLocalNetworkAddress": "Atar a la dirección de red local:", - "LabelBindToLocalNetworkAddressHelp": "Opcional. Forzar la dirección IP local a la que se vinculara el servidor http. Si se deja en blanco, el servidor vinculara todas las direcciones disponibles. Cambiando este valor se requerirá reiniciar el Servidor Jellyfin.", - "LabelBirthDate": "Fecha de Nacimiento:", + "LabelAutomaticallyRefreshInternetMetadataEvery": "Actualizar automáticamente los metadatos desde Internet:", + "LabelBindToLocalNetworkAddress": "Vincular a la dirección de red local:", + "LabelBindToLocalNetworkAddressHelp": "Opcional. Sobrescribe la dirección IP local a la que se vincula el servidor http. Si se deja vacío, el servidor se vinculará a todas las direcciones disponibles. Cambiar este valor requiere reiniciar el servidor Jellyfin.", + "LabelBirthDate": "Fecha de nacimiento:", "LabelBirthYear": "Año de nacimiento:", "LabelBlastMessageInterval": "Intervalo de mensajes de vida (segundos)", "LabelBlastMessageIntervalHelp": "Determina la duración en segundos del intervalo entre mensajes de vida.", - "LabelBlockContentWithTags": "Bloquear ítems con etiquetas:", - "LabelBurnSubtitles": "Subtitulos quemados:", + "LabelBlockContentWithTags": "Bloquear elementos con las etiquetas:", + "LabelBurnSubtitles": "Quemar subtítulos:", "LabelCache": "Caché:", - "LabelCachePath": "Ruta para el Caché:", - "LabelCachePathHelp": "Especifique una ubicación personalizada para los archivos de caché del servidor tales como las imágenes. Dejar en blanco para utilizar la configuración por defecto del servidor.", + "LabelCachePath": "Ruta de la caché:", + "LabelCachePathHelp": "Especifica una ubicación personalizada para los archivos caché del servidor como las imágenes. Dejar en blanco para utilizar la configuración por defecto del servidor.", "LabelCancelled": "Cancelado", "LabelCertificatePassword": "Contraseña del certificado:", - "LabelCertificatePasswordHelp": "Si su certificado requiere de una contraseña, por favor introdúzcala aquí.", + "LabelCertificatePasswordHelp": "Si tu certificado requiere una contraseña, por favor, introdúcela aquí.", "LabelChannels": "Canales:", "LabelCollection": "Colección:", "LabelCommunityRating": "Calificación de la comunidad:", - "LabelContentType": "Tipo de Contenido:", + "LabelContentType": "Tipo de contenido:", "LabelCountry": "País:", - "LabelCriticRating": "Calificación de la crítica:", + "LabelCriticRating": "Calificación de los críticos:", "LabelCurrentPassword": "Contraseña actual:", - "LabelCustomCertificatePath": "Ruta personalizada del certificado SSL:", - "LabelCustomCertificatePathHelp": "Trayectoria a un archivo PKCS #12 que contiene el certificado y la llave privada para habilitar soporte a TLS en un dominio personalizado.", + "LabelCustomCertificatePath": "Ruta del certificado SSL personalizado:", + "LabelCustomCertificatePathHelp": "Ruta a un archivo PKCS #12 que contiene un certificado y una clave privada para habilitar el soporte TLS en un dominio personalizado.", "LabelCustomCss": "CSS personalizado:", - "LabelCustomCssHelp": "Aplicar tu propio estilo personalizado a la interfaz web.", + "LabelCustomCssHelp": "Aplica tu propio estilo personalizado a la interfaz web.", "LabelCustomDeviceDisplayName": "Nombre a mostrar:", - "LabelCustomDeviceDisplayNameHelp": "Proporcione un nombre personalizado para mostrar o déjelo vacío para usar el nombre reportado por el dispositivo.", + "LabelCustomDeviceDisplayNameHelp": "Proporcione un nombre personalizado para mostrar o déjalo vacío para usar el nombre reportado por el dispositivo.", "LabelCustomRating": "Calificación personalizada:", - "LabelDashboardTheme": "Tema del panel de control del Servidor:", + "LabelDashboardTheme": "Tema del panel de control del servidor:", "LabelDateAdded": "Fecha de adición:", - "LabelDateAddedBehavior": "Comportamiento de fecha de adición para nuevo contenido:", - "LabelDateAddedBehaviorHelp": "Si se encuentra un valor en los metadados siempre será empleado antes que cualquiera de estas opciones.", - "LabelDateTimeLocale": "Configuración regional de Fecha y Hora:", + "LabelDateAddedBehavior": "Comportamiento de la fecha de adición para nuevo contenido:", + "LabelDateAddedBehaviorHelp": "Si un valor de metadatos está presente, siempre se utilizará antes de cualquiera de estas opciones.", + "LabelDateTimeLocale": "Configuración regional de fecha y hora:", "LabelDay": "Día:", "LabelDeathDate": "Fecha de defunción:", "LabelDefaultScreen": "Pantalla por defecto:", "LabelDefaultUser": "Usuario por defecto:", - "LabelDefaultUserHelp": "Determina que usuario de la biblioteca será mostrado en los dispositivos conectados. Este puede ser reemplazado para cada dispositivo empleando perfiles.", + "LabelDefaultUserHelp": "Determina qué biblioteca de usuario será mostrada en los dispositivos conectados. Esto puede ser reemplazado para cada dispositivo empleando perfiles.", "LabelDeviceDescription": "Descripción del dispositivo", "LabelDidlMode": "Modo DIDL:", "LabelDiscNumber": "Número de disco:", - "LabelDisplayLanguage": "Lenguaje de Despliege:", + "LabelDisplayLanguage": "Idioma de pantalla:", "LabelDisplayLanguageHelp": "La traducción de Jellyfin es un proyecto en curso.", - "LabelDisplayMissingEpisodesWithinSeasons": "Mostar episodios no disponibles en las temporadas", - "LabelDisplayMode": "Modo de Pantalla:", + "LabelDisplayMissingEpisodesWithinSeasons": "Mostrar episodios faltantes en las temporadas", + "LabelDisplayMode": "Modo de pantalla:", "LabelDisplayName": "Nombre a mostrar:", "LabelDisplayOrder": "Orden para mostrar:", - "LabelDisplaySpecialsWithinSeasons": "Mostrar especiales dentro de las temporadas en que fueron transmitidos", - "LabelDownMixAudioScale": "Fortalecimiento de audio durante el downmix:", - "LabelDownMixAudioScaleHelp": "Realzar el audio cuando se hace downmix. Un valor de 1 preservará el volumen original.", - "LabelDownloadLanguages": "Descargar lenguajes:", - "LabelDropImageHere": "Arrastre la imagen aquí, o de clic para navegar.", + "LabelDisplaySpecialsWithinSeasons": "Mostrar especiales dentro de las temporadas en las que fueron transmitidas", + "LabelDownMixAudioScale": "Incremento del audio cuando se hace downmix:", + "LabelDownMixAudioScaleHelp": "Incrementa el audio cuando se hace downmix. Un valor de 1 preservará el volumen original.", + "LabelDownloadLanguages": "Descargar idiomas:", + "LabelDropImageHere": "Arrastre la imagen aquí o haz para explorar.", "LabelDropShadow": "Mostrar sombra:", - "LabelEasyPinCode": "Código pin sencillo:", + "LabelEasyPinCode": "Código PIN sencillo:", "LabelEmbedAlbumArtDidl": "Incrustar arte del álbum en DIDL", - "LabelEmbedAlbumArtDidlHelp": "Algunos dispositivos prefieren este método para obtener arte del álbum. Otros podrían fallar al reproducir con esta opción habilitada.", + "LabelEmbedAlbumArtDidlHelp": "Algunos dispositivos prefieren este método para obtener arte de álbumes. Otros podrían fallar al reproducir con esta opción habilitada.", "LabelEnableAutomaticPortMap": "Habilitar mapeo automático de puertos", - "LabelEnableAutomaticPortMapHelp": "Intentar mapear automáticamente el puerto público con el puerto local vía UPnP. Esto podría no funcionar con algunos modelos de ruteadores.", + "LabelEnableAutomaticPortMapHelp": "Redirecciona automáticamente los puertos públicos de tu router a los puertos locales de tu servidor a través de UPnP. Esto puede no funcionar con algunos modelos de routers o configuraciones de red. Los cambios no se aplicarán hasta después de reiniciar el servidor.", "LabelEnableBlastAliveMessages": "Bombardeo de mensajes de vida", - "LabelEnableBlastAliveMessagesHelp": "Habilite esto si el servidor no es detectado de manera confiable por otros dispositivos UPnP en su red.", - "LabelEnableDlnaClientDiscoveryInterval": "Intervalo de Detección de Clientes (segundos)", + "LabelEnableBlastAliveMessagesHelp": "Habilita esto si el servidor no es detectado de manera confiable por otros dispositivos UPnP en tu red.", + "LabelEnableDlnaClientDiscoveryInterval": "Intervalo de descubrimiento de clientes (segundos)", "LabelEnableDlnaClientDiscoveryIntervalHelp": "Determina la duración en segundos entre búsquedas SSDP realizadas por Jellyfin.", - "LabelEnableDlnaDebugLogging": "Habilitar el registro de DLNA en la bitácora", + "LabelEnableDlnaDebugLogging": "Habilitar el registro de depuración de DLNA", "LabelEnableDlnaDebugLoggingHelp": "Crea grandes archivos de registro y solo se debe usar cuando se requiera para solucionar problemas.", "LabelEnableDlnaPlayTo": "Habilitar Reproducir En mediante DLNA", - "LabelEnableDlnaPlayToHelp": "Detecta dispositivos dentro de su red y ofrece la capacidad de controlarlos remotamente.", + "LabelEnableDlnaPlayToHelp": "Detecta dispositivos dentro de tu red y ofrece la capacidad de controlarlos remotamente.", "LabelEnableDlnaServer": "Habilitar servidor DLNA", - "LabelEnableDlnaServerHelp": "Permite a dispositivos UPnP en su red navegar y reproducir contenido.", - "LabelEnableHardwareDecodingFor": "Habilitar decodificacion por hardware para:", + "LabelEnableDlnaServerHelp": "Permite a dispositivos UPnP en tu red explorar y reproducir contenido.", + "LabelEnableHardwareDecodingFor": "Habilitar decodificación por hardware para:", "LabelEnableRealtimeMonitor": "Activar monitoreo en tiempo real", - "LabelEnableRealtimeMonitorHelp": "Los cambios en los archivos serán procesados inmediatamente, en los sistemas de archivo que lo soporten.", + "LabelEnableRealtimeMonitorHelp": "Los cambios en los archivos serán procesados inmediatamente, en los sistemas de archivo soportados.", "LabelEnableSingleImageInDidlLimit": "Limitar a una sola imagen incrustada", - "LabelEnableSingleImageInDidlLimitHelp": "Algunos dispositivos no renderisaran apropiadamente si hay múltiples imágenes incrustadas en el Didl.", - "LabelEndDate": "Fecha de Fin:", - "LabelEpisodeNumber": "Episodio numero:", + "LabelEnableSingleImageInDidlLimitHelp": "Algunos dispositivos no se renderizarán correctamente si se incrustan varias imágenes en DIDL.", + "LabelEndDate": "Fecha de fin:", + "LabelEpisodeNumber": "Episodio número:", "LabelEvent": "Evento:", "LabelEveryXMinutes": "Cada:", - "LabelExtractChaptersDuringLibraryScan": "Extraer imágenes de capítulos durante la exploración de la biblioteca", - "LabelExtractChaptersDuringLibraryScanHelp": "Genera imágenes de capítulos cuando se importan videos durante el análisis de la biblioteca. De lo contrario, se extraerán durante la tarea programada de las imágenes de capítulos, lo que permitirá que el escaneado normal de la biblioteca se complete más rápidamente.", + "LabelExtractChaptersDuringLibraryScan": "Extraer las imágenes de los capítulos durante el escaneo de la biblioteca", + "LabelExtractChaptersDuringLibraryScanHelp": "Genera imágenes de los capítulos cuando se importan videos durante el escaneo de la biblioteca. De lo contrario, se extraerán durante la tarea programada imágenes de capítulos, lo que permitirá que el escaneado normal de la biblioteca se complete más rápidamente.", "LabelFailed": "Fallido", "LabelFileOrUrl": "Archivo o URL:", "LabelFinish": "Terminar", @@ -606,73 +605,73 @@ "LabelFriendlyName": "Nombre amistoso:", "LabelServerNameHelp": "Este nombre se usará para identificar el servidor y se predeterminará al nombre de la computadora del servidor.", "LabelGroupMoviesIntoCollections": "Agrupar películas en colecciones", - "LabelGroupMoviesIntoCollectionsHelp": "Cuando se muestran listados de películas, las películas que pertenecen a una colección serán mostradas agrupadas en un solo ítem.", + "LabelGroupMoviesIntoCollectionsHelp": "Cuando se muestran listados de películas, las películas que pertenecen a una colección serán mostradas agrupadas en un solo artículo.", "LabelH264Crf": "CRF de codificación H264:", - "LabelEncoderPreset": "Codificación H264 predefinido:", - "LabelHardwareAccelerationType": "Aceleración por Hardware:", - "LabelHardwareAccelerationTypeHelp": "Aceleración por Hardware requiere configuración adicional.", - "LabelHomeNetworkQuality": "Calidad en Red Local:", - "LabelHomeScreenSectionValue": "Pagina de inicio sección {0}:", + "LabelEncoderPreset": "Codificación H264 y H265 preestablecida:", + "LabelHardwareAccelerationType": "Aceleración por hardware:", + "LabelHardwareAccelerationTypeHelp": "La aceleración por hardware requiere configuración adicional.", + "LabelHomeNetworkQuality": "Calidad en red local:", + "LabelHomeScreenSectionValue": "Sección {0} de la pantalla de inicio:", "LabelHttpsPort": "Número de puerto local HTTPS:", "LabelHttpsPortHelp": "El número de puerto TCP al que el servidor HTTPS de Jellyfin debería enlazar.", "LabelIconMaxHeight": "Altura máxima del ícono:", - "LabelIconMaxHeightHelp": "Máxima resolución de íconos expuestos vía upnp:icon.", - "LabelIconMaxWidth": "Ancho máximo de ícono:", - "LabelIconMaxWidthHelp": "Máxima resolución de íconos expuestos vía upnp:icon.", - "LabelIdentificationFieldHelp": "Una subcadena insensible a la diferencia entre minúsculas y mayúsculas o expresión regex.", - "LabelImageFetchersHelp": "Habilite y priorice sus recolectores de imágenes preferidos.", + "LabelIconMaxHeightHelp": "Resolución máxima de los íconos expuestos vía upnp:icon.", + "LabelIconMaxWidth": "Ancho máximo del ícono:", + "LabelIconMaxWidthHelp": "Resolución máxima de los íconos expuestos vía upnp:icon.", + "LabelIdentificationFieldHelp": "Una subcadena indiferente a las mayúsculas y minúsculas o una expresión regular (regex).", + "LabelImageFetchersHelp": "Habilita y prioriza tus recolectores de imágenes preferidos.", "LabelImageType": "Tipo de imagen:", "LabelImportOnlyFavoriteChannels": "Restringir a canales marcados como favoritos", - "LabelInNetworkSignInWithEasyPassword": "Habilitar inicio de sesión con mi código pin sencillo para conexiones dentro de la red", - "LabelInNetworkSignInWithEasyPasswordHelp": "Utilice el código PIN fácil para acceder a los clientes en su red local. Su contraseña regular solo se necesitará fuera de casa. Si el código PIN se deja en blanco, no necesitará una contraseña dentro de su red local.", - "LabelInternetQuality": "Calidad en internet:", + "LabelInNetworkSignInWithEasyPassword": "Habilitar inicio de sesión con mi código PIN sencillo para conexiones dentro de la red", + "LabelInNetworkSignInWithEasyPasswordHelp": "Utiliza el código PIN sencillo para acceder a los clientes en tu red local. Tu contraseña regular solo se necesitará fuera de casa. Si el código PIN se deja en blanco, no necesitarás una contraseña dentro de tu red local.", + "LabelInternetQuality": "Calidad en Internet:", "LabelKeepUpTo": "Mantener hasta:", "LabelKidsCategories": "Categorías infantiles:", "LabelKodiMetadataDateFormat": "Formato de fecha de estreno:", "LabelKodiMetadataDateFormatHelp": "Todas las fechas dentro de los archivos NFO serán analizadas usando este formato.", "LabelKodiMetadataEnableExtraThumbs": "Copiar extrafanart al campo extrathumbs", - "LabelKodiMetadataEnableExtraThumbsHelp": "Cuando se descargan imágenes pueden ser almacenadas tanto en extrafanart como extrathumb para maximizar la compatibilidad con skins de Kodi.", + "LabelKodiMetadataEnableExtraThumbsHelp": "Cuando se descargan imágenes estas pueden ser almacenadas tanto en extrafanart como extrathumb para maximizar la compatibilidad con las pieles de Kodi.", "LabelKodiMetadataEnablePathSubstitution": "Habilitar sustitución de ruta", "LabelKodiMetadataEnablePathSubstitutionHelp": "Habilita la sustitución de rutas de imágenes usando la configuración de sustitución de rutas del servidor.", - "LabelKodiMetadataSaveImagePaths": "Guardar rutas de imágenes en los archivos nfo", - "LabelKodiMetadataSaveImagePathsHelp": "Esto se recomienda si tiene nombres de imagenes que no se ajustan a los lineamientos de Kodi.", + "LabelKodiMetadataSaveImagePaths": "Guardar las rutas de las imágenes en los archivos NFO", + "LabelKodiMetadataSaveImagePathsHelp": "Esto se recomienda si tienes nombres de imágenes que no se ajustan a los lineamientos de Kodi.", "LabelKodiMetadataUser": "Guardar los datos de visto del usuario en archivos NFO para:", - "LabelKodiMetadataUserHelp": "Guarda los datos de visto en los archivos NFO para que otras aplicaciones los utilicen.", + "LabelKodiMetadataUserHelp": "Guarda los datos de visto en archivos NFO para que otras aplicaciones los utilicen.", "LabelLanNetworks": "Redes LAN:", "LabelLanguage": "Idioma:", "LabelLineup": "Programación:", "LabelLocalHttpServerPortNumber": "Número de puerto local HTTP:", "LabelLocalHttpServerPortNumberHelp": "El número de puerto TCP al que el servidor HTTP de Jellyfin debería enlazar.", - "LabelLockItemToPreventChanges": "Bloquear este ítem para evitar cambios futuros", + "LabelLockItemToPreventChanges": "Bloquear este elemento para evitar cambios futuros", "LabelLoginDisclaimer": "Aviso legal:", "LabelLoginDisclaimerHelp": "Un mensaje que se mostrará en la parte inferior de la página de inicio de sesión.", - "LabelLogs": "Bitácoras:", + "LabelLogs": "Registros:", "LabelManufacturer": "Fabricante:", "LabelManufacturerUrl": "URL del fabricante", - "LabelMatchType": "Tipo de Coincidencia:", - "LabelMaxBackdropsPerItem": "Número máximo de imágenes de fondo por ítem:", - "LabelMaxChromecastBitrate": "Tasa maxima de bits para El Chromecast:", + "LabelMatchType": "Tipo de coincidencia:", + "LabelMaxBackdropsPerItem": "Número máximo de imágenes de fondo por elemento:", + "LabelMaxChromecastBitrate": "Calidad de transmisión de Chromecast:", "LabelMaxParentalRating": "Máxima clasificación parental permitida:", "LabelMaxResumePercentage": "Porcentaje máximo para la reanudación:", "LabelMaxResumePercentageHelp": "Los medios se consideran totalmente reproducidos si se detienen después de este tiempo.", - "LabelMaxScreenshotsPerItem": "Número máximo de capturas de pantalla por ítem:", + "LabelMaxScreenshotsPerItem": "Número máximo de capturas de pantalla por elemento:", "LabelMaxStreamingBitrate": "Calidad máxima de transmisión:", - "LabelMaxStreamingBitrateHelp": "Especifique una velocidad de bits máxima al hacer streaming.", - "LabelMessageText": "Texto del Mensaje:", - "LabelMessageTitle": "Título del Mensaje:", + "LabelMaxStreamingBitrateHelp": "Especifique una velocidad de bits máxima cuando se transmite.", + "LabelMessageText": "Texto del mensaje:", + "LabelMessageTitle": "Título del mensaje:", "LabelMetadata": "Metadatos:", - "LabelMetadataDownloadLanguage": "Lenguaje preferido para descargas:", - "LabelMetadataDownloadersHelp": "Habilite y priorice sus recolectores de metadatos preferidos. Los recolectores de metadatos de menor prioridad solo serán utilizados para llenar información faltante.", - "LabelMetadataPath": "Ruta para metadatos:", - "LabelMetadataPathHelp": "Especifique una ubicación personalizada para ilustraciones y metadatos descargados.", + "LabelMetadataDownloadLanguage": "Idioma preferido para las descargas:", + "LabelMetadataDownloadersHelp": "Habilita y prioriza tus recolectores de metadatos preferidos. Los recolectores de metadatos de menor prioridad solo serán utilizados para llenar información faltante.", + "LabelMetadataPath": "Ruta para los metadatos:", + "LabelMetadataPathHelp": "Especifique una ubicación personalizada para las ilustraciones y los metadatos descargados.", "LabelMetadataReaders": "Lectores de metadatos:", - "LabelMetadataReadersHelp": "Ordene sus fuentes de metadatos locales por prioridad. El primer archivo encontrado será leído.", + "LabelMetadataReadersHelp": "Ordena tus fuentes de metadatos locales por prioridad. El primer archivo encontrado será leído.", "LabelMetadataSavers": "Grabadores de metadatos:", - "LabelMetadataSaversHelp": "Seleccione los formatos de archivo con los que se guardaran sus metadatos.", + "LabelMetadataSaversHelp": "Selecciona los formatos de archivo con los que se guardarán tus metadatos.", "LabelMethod": "Método:", "LabelMinBackdropDownloadWidth": "Anchura mínima de descarga de imágenes de fondo:", "LabelMinResumeDuration": "Duración mínima para la reanudación:", - "LabelMinResumeDurationHelp": "La duración de vídeo más corta en segundos que guardará la ubicación de reproducción y le permitirá reanudar la reproducción.", + "LabelMinResumeDurationHelp": "La duración de video más corta en segundos que guardará la ubicación de reproducción y te permitirá reanudarla.", "LabelMinResumePercentage": "Porcentaje mínimo para reanudar:", "LabelMinResumePercentageHelp": "Los medios se asumen como no reproducidos si se detienen antes de este tiempo.", "LabelMinScreenshotDownloadWidth": "Anchura mínima de descarga de capturas de pantalla:", @@ -683,296 +682,296 @@ "LabelMonitorUsers": "Monitorear actividad desde:", "LabelMovieCategories": "Categorías de películas:", "LabelMoviePrefix": "Prefijo de la película:", - "LabelMoviePrefixHelp": "Si un prefijo es aplicado al título de las películas, introdúzcalo aquí para que el servidor pueda manejarlo correctamente.", - "LabelMovieRecordingPath": "Ruta para grabaciones de Películas (opcional):", - "LabelMusicStreamingTranscodingBitrate": "Tasa de transcodificación de música:", - "LabelMusicStreamingTranscodingBitrateHelp": "Especifique la tasa de bits máxima al transmitir música.", + "LabelMoviePrefixHelp": "Si un prefijo es aplicado al título de las películas, introdúcelo aquí para que el servidor pueda manejarlo correctamente.", + "LabelMovieRecordingPath": "Ruta para las grabaciones de películas (opcional):", + "LabelMusicStreamingTranscodingBitrate": "Velocidad de bits de transcodificación de música:", + "LabelMusicStreamingTranscodingBitrateHelp": "Especifica la velocidad de bits máxima al transmitir música.", "LabelName": "Nombre:", "LabelNewName": "Nuevo nombre:", - "LabelNewPassword": "Nueva contraseña:", + "LabelNewPassword": "Contraseña nueva:", "LabelNewPasswordConfirm": "Confirmación de contraseña nueva:", - "LabelNewsCategories": "Categorías de Noticias:", + "LabelNewsCategories": "Categorías de noticias:", "LabelNext": "Siguiente", "LabelNotificationEnabled": "Habilitar esta notificación", "LabelNumber": "Número:", "LabelNumberOfGuideDays": "Número de días de datos de la programación a descargar:", "LabelNumberOfGuideDaysHelp": "Descargar más días de datos de programación permite programar con mayor anticipación y ver más listados, pero tomará más tiempo en descargar. Auto hará la selección basada en el número de canales.", "LabelOptionalNetworkPath": "(Opcional) Carpeta de red compartida:", - "LabelOptionalNetworkPathHelp": "Si esta carpeta es compartida en su red, proveer la ruta del recurso compartido de red puede permitir a las aplicaciones Jellyfin en otros dispositivos acceder a los archivos de medios directamente.", + "LabelOptionalNetworkPathHelp": "Si esta carpeta es compartida en su red, proveer la ruta del recurso compartido de red puede permitir a las aplicaciones Jellyfin en otros dispositivos acceder a los archivos de medios directamente. Por ejemplo, {0} o {1}.", "LabelOriginalAspectRatio": "Relación de aspecto original:", - "LabelOriginalTitle": "Titulo original:", - "LabelOverview": "Sinopsis:", + "LabelOriginalTitle": "Título original:", + "LabelOverview": "Resumen:", "LabelParentNumber": "Número antecesor:", "LabelParentalRating": "Clasificación parental:", "LabelPassword": "Contraseña:", "LabelPasswordConfirm": "Contraseña (confirmar):", - "LabelPasswordRecoveryPinCode": "Código pin:", + "LabelPasswordRecoveryPinCode": "Código PIN:", "LabelPath": "Ruta:", "LabelPersonRole": "Rol:", "LabelPersonRoleHelp": "Ejemplo: Conductor de camión de helados", "LabelPlaceOfBirth": "Lugar de nacimiento:", - "LabelPlayDefaultAudioTrack": "Reproducir la pista de audio por defecto independientemente del lenguaje", - "LabelPlaylist": "Lista de Reproducción:", - "LabelPostProcessor": "Aplicación de Post Procesado:", - "LabelPostProcessorArguments": "Argumentos de linea de comando para el post-procesador:", - "LabelPostProcessorArgumentsHelp": "Use {path} como la ruta a el archivo de grabado.", + "LabelPlayDefaultAudioTrack": "Reproducir la pista de audio por defecto independientemente del idioma", + "LabelPlaylist": "Lista de reproducción:", + "LabelPostProcessor": "Aplicación de postprocesamiento:", + "LabelPostProcessorArguments": "Argumentos de la línea de comandos del post-procesador:", + "LabelPostProcessorArgumentsHelp": "Usar {path} como la ruta del archivo grabado.", "LabelPreferredDisplayLanguage": "Idioma de pantalla preferido:", "LabelPreferredDisplayLanguageHelp": "La traducción de Jellyfin es un proyecto en curso.", - "LabelPreferredSubtitleLanguage": "Idioma preferido para subtitulos:", + "LabelPreferredSubtitleLanguage": "Idioma preferido para los subtítulos:", "LabelPrevious": "Anterior", - "LabelProfileAudioCodecs": "Codecs de Audio:", - "LabelProfileCodecsHelp": "Separados por comas. Puede dejarse vació para aplicarlo a todos los codecs.", + "LabelProfileAudioCodecs": "Códecs de audio:", + "LabelProfileCodecsHelp": "Separados por comas. Puede dejarse vacío para aplicarlo a todos los códecs.", "LabelProfileContainer": "Contenedor:", - "LabelProfileContainersHelp": "Separados por comas. Puede dejarse vació para aplicarlo a todos los contenedores.", - "LabelProfileVideoCodecs": "Codecs de Video:", + "LabelProfileContainersHelp": "Separados por comas. Puede dejarse vacío para aplicarlo a todos los contenedores.", + "LabelProfileVideoCodecs": "Códecs de video:", "LabelProtocol": "Protocolo:", "LabelProtocolInfo": "Información del protocolo:", - "LabelProtocolInfoHelp": "El valor que será utilizado cuando se responde a solicitudes GetProtocolInfo desde el dispositivo.", + "LabelProtocolInfoHelp": "El valor que será utilizado cuando se responda a solicitudes GetProtocolInfo desde el dispositivo.", "LabelPublicHttpPort": "Número de puerto HTTP público:", "LabelPublicHttpPortHelp": "El número de puerto público que debe asignarse al puerto HTTP local.", "LabelPublicHttpsPort": "Número de puerto HTTPS público:", "LabelPublicHttpsPortHelp": "El número de puerto público que debe asignarse al puerto HTTPS local.", - "LabelReadHowYouCanContribute": "Infórmese de cómo puede contribuir.", + "LabelReadHowYouCanContribute": "Aprende cómo puedes contribuir.", "LabelReasonForTranscoding": "Motivo para transcodificar:", "LabelRecord": "Grabar:", - "LabelRecordingPath": "Ruta por defecto para grabaciones:", - "LabelRecordingPathHelp": "Especifique la locación para guardar grabaciones. Si se deja en blanco, se usara la carpeta de datos de programa del servidor.", + "LabelRecordingPath": "Ruta por defecto para las grabaciones:", + "LabelRecordingPathHelp": "Especifica la ubicación por defecto para guardar las grabaciones. Si se deja en blanco, se usará la carpeta de datos de programa del servidor.", "LabelRefreshMode": "Modo de actualización:", "LabelReleaseDate": "Fecha de estreno:", - "LabelRemoteClientBitrateLimit": "Limite de tasa de bits para transmisión por Internet (Mbps):", - "LabelRemoteClientBitrateLimitHelp": "Un límite opcional en la tasa de bits para cada transmisión para todos los dispositivos fuera de la red local. Esto es útil para evitar que los clientes soliciten una tasa de bits mayor a la que su conexión de internet puede soportar. Puede resultar en un incremente en la carga del CPU de su servidor para poder transmitir videos al vuelo a una resolución baja.", + "LabelRemoteClientBitrateLimit": "Limite de velocidad de bits para la transmisión por Internet (Mbps):", + "LabelRemoteClientBitrateLimitHelp": "Un límite opcional de velocidad de bits por transmisión para todos los dispositivos fuera de la red. Esto es útil para evitar que los dispositivos soliciten una tasa de bits más alta de la que puede manejar tu conexión a Internet. Esto puede provocar un aumento de la carga de la CPU en el servidor para transcodificar los videos sobre la marcha a una velocidad de bits inferior.", "LabelRuntimeMinutes": "Duración (minutos):", - "LabelSaveLocalMetadata": "Guardar ilustraciones en las carpetas de medios", - "LabelSaveLocalMetadataHelp": "Guardar ilustraciones directamente en las carpetas de medios los colocará en un lugar donde se pueden editar fácilmente.", - "LabelScheduledTaskLastRan": "Ejecutado hace {0}, tomando {1}.", - "LabelScreensaver": "Protector de Pantalla:", - "LabelSeasonNumber": "Temporada numero:", + "LabelSaveLocalMetadata": "Guardar las ilustraciones en las carpetas de los medios", + "LabelSaveLocalMetadataHelp": "Guardar ilustraciones en las carpetas de los medios los colocará en un lugar donde se pueden editar fácilmente.", + "LabelScheduledTaskLastRan": "Última ejecución {0}, tomando {1}.", + "LabelScreensaver": "Protector de pantalla:", + "LabelSeasonNumber": "Temporada número:", "LabelSecureConnectionsMode": "Modo de conexión segura:", - "LabelSelectFolderGroups": "Agrupar automáticamente el contenido de las siguientes carpetas en vistas tales como Películas, Música y TV:", + "LabelSelectFolderGroups": "Agrupar automáticamente el contenido de las siguientes carpetas a vistas como Películas, Música y TV:", "LabelSelectFolderGroupsHelp": "Las carpetas sin marcar serán mostradas individualmente en su propia vista.", - "LabelSelectUsers": "Seleccionar Usuarios:", + "LabelSelectUsers": "Seleccionar usuarios:", "LabelSelectVersionToInstall": "Seleccionar versión a instalar:", "LabelSendNotificationToUsers": "Enviar la notificación a:", "LabelSerialNumber": "Número de serie", - "LabelSeriesRecordingPath": "Ruta para grabaciones de Series (Opcional):", + "LabelSeriesRecordingPath": "Ruta para las grabaciones de series (opcional):", "LabelServerHost": "Servidor:", "LabelServerHostHelp": "192.168.1.100:8096 o https://miservidor.com", - "LabelSimultaneousConnectionLimit": "Limite de transmisiones simultaneas:", - "LabelSkin": "Piel:", + "LabelSimultaneousConnectionLimit": "Límite de transmisiones simultáneas:", + "LabelSkin": "Apariencia:", "LabelSkipBackLength": "Longitud de salto hacia atrás:", "LabelSkipForwardLength": "Longitud de salto hacia adelante:", - "LabelSkipIfAudioTrackPresent": "Omitir si la pista de audio por defecto coincide con el lenguaje de descarga", - "LabelSkipIfAudioTrackPresentHelp": "Desactive esto para asegurar que todos los videos tengan subtítulos, independientemente del lenguaje del audio.", - "LabelSkipIfGraphicalSubsPresent": "Omitir si el video ya contiene subtítulos embebidos", - "LabelSkipIfGraphicalSubsPresentHelp": "Manteniendo las versiones de texto de los subtitulos resultara una entrega mas eficiente de los mismos y disminuirá las posibilidades de que un video sea transcodificado.", - "LabelSonyAggregationFlags": "Banderas de agregación Sony:", + "LabelSkipIfAudioTrackPresent": "Omitir si la pista de audio por defecto coincide con el idioma de descarga", + "LabelSkipIfAudioTrackPresentHelp": "Desactiva esto para asegurarte de que todos los videos tengan subtítulos, independientemente del idioma del audio.", + "LabelSkipIfGraphicalSubsPresent": "Omitir si el video ya contiene subtítulos incrustados", + "LabelSkipIfGraphicalSubsPresentHelp": "Mantener versiones de texto de subtítulos resultará en una entrega más eficiente y disminuirá las posibilidades de que un video sea transcodificado.", + "LabelSonyAggregationFlags": "Marcas de agregación Sony:", "LabelSonyAggregationFlagsHelp": "Determina el contenido del elemento aggregationFlags en el namespace urn:schemas-sonycom:av.", "LabelSortBy": "Ordenar por:", - "LabelSortOrder": "Modo de ordenar:", - "LabelSortTitle": "Titulo para ordenar:", - "LabelSoundEffects": "Efectos de Sonido:", + "LabelSortOrder": "Clasificar ordenado:", + "LabelSortTitle": "Título para ordenar:", + "LabelSoundEffects": "Efectos de sonido:", "LabelSource": "Fuente:", - "LabelSpecialSeasonsDisplayName": "Nombre de la temporada de Especiales:", - "LabelSportsCategories": "Categorías de Deportes:", + "LabelSpecialSeasonsDisplayName": "Nombre de la temporada de especiales:", + "LabelSportsCategories": "Categorías de deportes:", "LabelStartWhenPossible": "Iniciar cuando sea posible:", "LabelStatus": "Estado:", "LabelStopWhenPossible": "Detener cuando sea posible:", "LabelStopping": "Deteniendo", - "LabelSubtitleDownloaders": "Recolectores de Subtitulos:", + "LabelSubtitleDownloaders": "Recolectores de subtítulos:", "LabelSubtitleFormatHelp": "Ejemplo: srt", "LabelSubtitlePlaybackMode": "Modo de subtítulo:", "LabelSubtitles": "Subtítulos", - "LabelSupportedMediaTypes": "Tipos de Medios Soportados:", + "LabelSupportedMediaTypes": "Tipos de medios soportados:", "LabelTVHomeScreen": "Modo de pantalla de TV:", "LabelTag": "Etiqueta:", "LabelTagline": "Eslogan:", "LabelTextBackgroundColor": "Color de fondo para el texto:", - "LabelTextColor": "Color de texto:", - "LabelTextSize": "Tamaño de texto:", + "LabelTextColor": "Color del texto:", + "LabelTextSize": "Tamaño del texto:", "LabelTheme": "Tema:", "LabelTime": "Hora:", - "LabelTimeLimitHours": "Límite de Tiempo (horas):", - "LabelTitle": "Titulo:", - "LabelTrackNumber": "Número de Pista:", - "LabelTranscodingAudioCodec": "Codec de audio:", + "LabelTimeLimitHours": "Límite de tiempo (horas):", + "LabelTitle": "Título:", + "LabelTrackNumber": "Número de pista:", + "LabelTranscodingAudioCodec": "Códec de audio:", "LabelTranscodingContainer": "Contenedor:", - "LabelTranscodingTempPathHelp": "Especifique una ruta personalizada para los archivos de transcodificación servidos a los clientes. Deje en blanco para utilizar el predeterminado del servidor.", - "LabelTranscodingThreadCount": "Conteo de hilos de transcodificación:", - "LabelTranscodingThreadCountHelp": "Seleccione el número máximo de hilos a utilizar al transcodificar. La reducción del número de hilos reducirá el uso de la CPU, pero es posible que no se convierta lo suficientemente rápido como para que la experiencia de reproducción sea fluida.", - "LabelTranscodingVideoCodec": "Codec de video:", - "LabelTriggerType": "Tipo de Evento:", - "LabelTunerIpAddress": "Dirección IP del Sintonizador:", + "LabelTranscodingTempPathHelp": "Especifica una ruta personalizada para los archivos de transcodificación servidos a los clientes. Deja en blanco para utilizar el predeterminado del servidor.", + "LabelTranscodingThreadCount": "Conteo de hilos de la transcodificación:", + "LabelTranscodingThreadCountHelp": "Selecciona el número máximo de hilos a utilizar al transcodificar. Reducir el número de hilos reducirá el uso de la CPU, pero es posible que no se convierta lo suficientemente rápido como para que la experiencia de reproducción sea fluida.", + "LabelTranscodingVideoCodec": "Códec de video:", + "LabelTriggerType": "Tipo de disparador:", + "LabelTunerIpAddress": "Dirección IP del sintonizador:", "LabelTunerType": "Tipo de sintonizador:", "LabelType": "Tipo:", - "LabelTypeMetadataDownloaders": "{0} recolectores de metadatos:", + "LabelTypeMetadataDownloaders": "Recolectores de metadatos para {0}:", "LabelTypeText": "Texto", - "LabelUseNotificationServices": "Emplear los siguientes servicios:", + "LabelUseNotificationServices": "Usar los siguientes servicios:", "LabelUser": "Usuario:", "LabelUserAgent": "Agente de usuario:", - "LabelUserLibrary": "Biblioteca del Usuario:", - "LabelUserLibraryHelp": "Seleccione la biblioteca de usuario que se mostrara en el dispositivo. Deje vacío para heredar la configuración por defecto.", + "LabelUserLibrary": "Biblioteca de usuario:", + "LabelUserLibraryHelp": "Selecciona la biblioteca de usuario que se mostrará en el dispositivo. Déjalo vacío para heredar la configuración por defecto.", "LabelUserRemoteClientBitrateLimitHelp": "Sobrescribe el valor global predeterminado establecido en la configuración de reproducción del servidor.", - "LabelUsername": "Nombre Usuario:", + "LabelUsername": "Nombre de usuario:", "LabelVaapiDevice": "Dispositivo VA API:", - "LabelVaapiDeviceHelp": "Este es un nodo de renderizado que es usado para aceleración por hardware.", + "LabelVaapiDeviceHelp": "Este es el nodo de renderizado que es usado para la aceleración por hardware.", "LabelValue": "Valor:", "LabelVersion": "Versión:", "LabelVersionInstalled": "{0} instalado", "LabelVersionNumber": "Versión {0}", - "LabelXDlnaCap": "X-DLNA cap:", + "LabelXDlnaCap": "X-DLNA límite:", "LabelXDlnaCapHelp": "Determina el contenido del elemento X_DLNACAP en el namespace urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDoc": "Documento X-DLNA:", "LabelXDlnaDocHelp": "Determina el contenido del elemento X_DLNADOC en el namespace urn:schemas-dlna-org:device-1-0.", "LabelYear": "Año:", - "LabelYourFirstName": "Su nombre:", - "LabelYoureDone": "¡Ha terminado!", - "LabelZipCode": "Código Postal:", - "LabelffmpegPath": "Ruta FFmpeg:", - "LabelffmpegPathHelp": "La ruta hacia el archivo de aplicación de ffmpeg, o la carpeta que contenga ffmpeg.", - "LanNetworksHelp": "Lista separada por comas de direcciones IP/mascaras de subred para las redes que serán consideradas como locales al enforzar restricciones de ancho de banda. Si se establece, todas las demás direcciones IP serán consideradas como redes externas y estarán sujetas a restricciones de ancho de banda. Si se deja en blanco, sólo la subred del servidor será considerada como red local.", + "LabelYourFirstName": "Tu nombre:", + "LabelYoureDone": "¡Has terminado!", + "LabelZipCode": "Código postal:", + "LabelffmpegPath": "Ruta del FFmpeg:", + "LabelffmpegPathHelp": "La ruta hacia el archivo de la aplicación ffmpeg, o la carpeta que contenga ffmpeg.", + "LanNetworksHelp": "Lista separada por comas de direcciones IP o entradas de IP/máscara de red para las redes que se considerarán en la red local al aplicar las restricciones de ancho de banda. Si se establecen, todas las demás direcciones IP se considerarán como parte de la red externa y estarán sujetas a las restricciones de ancho de banda externa. Si se deja en blanco, solo se considera a la subred del servidor estar en la red local.", "Large": "Grande", - "LatestFromLibrary": "Más recientes - {0}", - "LearnHowYouCanContribute": "Aprenda como puede contribuír.", - "LibraryAccessHelp": "Seleccione las bibliotecas que desea compartir con este usuario. Los administradores podrán editar todas las carpetas utilizando el gestor de metadatos.", + "LatestFromLibrary": "Últimas - {0}", + "LearnHowYouCanContribute": "Aprende cómo puedes contribuir.", + "LibraryAccessHelp": "Selecciona las bibliotecas que deseas compartir con este usuario. Los administradores podrán editar todas las carpetas utilizando el gestor de metadatos.", "Like": "Me gusta", "LinksValue": "Enlaces: {0}", "List": "Lista", - "Live": "En Vivo", - "LiveBroadcasts": "Transmisiones en vivo", - "LiveTV": "TV en Vivo", - "Logo": "Logotipo", + "Live": "En vivo", + "LiveBroadcasts": "Emisiones en vivo", + "LiveTV": "TV en vivo", + "Logo": "Logo", "ManageLibrary": "Administrar biblioteca", - "ManageRecording": "Administrar grabaciones", - "MapChannels": "Mapear Canales", - "MarkPlayed": "Marcar como Reproducido", - "MarkUnplayed": "Marcar como No Reproducido", - "MaxParentalRatingHelp": "El contenido con clasificación parental superior se ocultará para este usuario.", + "ManageRecording": "Administrar grabación", + "MapChannels": "Mapear canales", + "MarkPlayed": "Marcar como reproducido", + "MarkUnplayed": "Marcar como no reproducido", + "MaxParentalRatingHelp": "El contenido con una calificación más alta será ocultado a este usuario.", "MediaInfoAnamorphic": "Anamórfico", "MediaInfoAspectRatio": "Relación de aspecto", "MediaInfoBitDepth": "Profundidad de bit", - "MediaInfoBitrate": "Tasa de bits", + "MediaInfoBitrate": "Velocidad de bits", "MediaInfoChannels": "Canales", - "MediaInfoCodecTag": "Etiqueta de Codec", + "MediaInfoCodecTag": "Etiqueta de códec", "MediaInfoContainer": "Contenedor", "MediaInfoDefault": "Por defecto", "MediaInfoExternal": "Externo", "MediaInfoForced": "Forzado", - "MediaInfoFramerate": "Velocidad de cuadro", + "MediaInfoFramerate": "Velocidad de cuadros", "MediaInfoInterlaced": "Entrelazado", - "MediaInfoLanguage": "Lenguaje", + "MediaInfoLanguage": "Idioma", "MediaInfoLayout": "Esquema", "MediaInfoLevel": "Nivel", "MediaInfoPath": "Ruta", "MediaInfoPixelFormat": "Formato de pixel", - "MediaInfoProfile": "Perfíl", + "MediaInfoProfile": "Perfil", "MediaInfoRefFrames": "Tramas de referencia", "MediaInfoResolution": "Resolución", "MediaInfoSampleRate": "Tasa de muestreo", "MediaInfoSize": "Tamaño", "MediaInfoTimestamp": "Fecha y hora", - "MediaIsBeingConverted": "Los medios están siendo convertidos a un formato compatible con el dispositivo que esta reproduciendo el medio.", + "MediaIsBeingConverted": "Los medios están siendo convertidos a un formato compatible con el dispositivo que está reproduciendo el medio.", "Menu": "Menú", "MessageAlreadyInstalled": "Esta versión ya se encuentra instalada.", - "MessageAreYouSureDeleteSubtitles": "¿Está seguro de querer eliminar este archivo de subtitulos?", - "MessageAreYouSureYouWishToRemoveMediaFolder": "¿Está seguro de querer eliminar esta carpeta de medios?", - "MessageConfirmDeleteGuideProvider": "¿Está seguro de querer eliminar este proveedor de guía?", - "MessageConfirmDeleteTunerDevice": "¿Está seguro de querer eliminar este dispositivo?", - "MessageConfirmProfileDeletion": "¿Está seguro de querer eliminar este perfil?", - "MessageConfirmRecordingCancellation": "¿cancelar esta grabación?", - "MessageConfirmRemoveMediaLocation": "¿Está seguro de querer eliminar esta ubicación?", - "MessageConfirmRestart": "¿Esta seguro de que desea reiniciar el Servidor Jellyfin?", - "MessageConfirmRevokeApiKey": "¿Esta seguro de querer revocar esta clave api? La conexión de la aplicación con el Servidor Jellyfin sera terminada abruptamente.", - "MessageConfirmShutdown": "¿Está seguro de que desea apagar el servidor?", - "MessageContactAdminToResetPassword": "Por favor contacte a su administrador para restablecer su contraseña.", + "MessageAreYouSureDeleteSubtitles": "¿Estás seguro de querer eliminar este subtítulo?", + "MessageAreYouSureYouWishToRemoveMediaFolder": "¿Estás seguro de querer remover esta carpeta de medios?", + "MessageConfirmDeleteGuideProvider": "¿Estás seguro de querer eliminar este proveedor de guía?", + "MessageConfirmDeleteTunerDevice": "¿Estás seguro de querer eliminar este dispositivo?", + "MessageConfirmProfileDeletion": "¿Estás seguro de querer eliminar este perfil?", + "MessageConfirmRecordingCancellation": "¿Cancelar grabación?", + "MessageConfirmRemoveMediaLocation": "¿Estás seguro de querer remover esta ubicación?", + "MessageConfirmRestart": "¿Estás seguro de que deseas reiniciar el servidor Jellyfin?", + "MessageConfirmRevokeApiKey": "¿Estás seguro de querer revocar esta clave API? La conexión de la aplicación con el servidor Jellyfin será terminada abruptamente.", + "MessageConfirmShutdown": "¿Estás seguro de que deseas apagar el servidor?", + "MessageContactAdminToResetPassword": "Por favor, contacta a tu administrador para restablecer tu contraseña.", "MessageCreateAccountAt": "Crear una cuenta en {0}", - "MessageDeleteTaskTrigger": "¿Está seguro de querer eliminar este disparador de tarea?", - "MessageDirectoryPickerBSDInstruction": "Para BSD, quizás necesite configurar el almacenamiento dentro de su \"FreeNAS Jail\" de manera que permita a Jellyfin accesarlo.", - "MessageDirectoryPickerInstruction": "Las rutas de red pueden ser introducidas manualmente en caso de que el botón de Red no pueda localizar sus dispositivos. Por ejemplo, {0} or {1}.", - "MessageDirectoryPickerLinuxInstruction": "Para Linux en Arch Linux, CentOS, Debian, Fedora, openSUSE, o Ubuntu. Debe conceder al usuario del servicio al menos permisos de lectura a sus ubicaciones de almacenamiento.", - "MessageDownloadQueued": "Descargar cola.", - "MessageEnablingOptionLongerScans": "Habilitar esta opción podría resultar en escaneos de bibliotecas significativamente mas largos.", - "MessageFileReadError": "Hubo un error al leer el archivo. Por favor intente de nuevo.", + "MessageDeleteTaskTrigger": "¿Estás seguro de querer eliminar este disparador de tarea?", + "MessageDirectoryPickerBSDInstruction": "Para BSD, quizás necesites configurar el almacenamiento dentro de tu «FreeNAS Jail» de manera que permita a Jellyfin accederlo.", + "MessageDirectoryPickerInstruction": "Las rutas de red pueden ser introducidas manualmente en caso de que el botón de Red no pueda localizar sus dispositivos. Por ejemplo, {0} o {1}.", + "MessageDirectoryPickerLinuxInstruction": "Para Linux en Arch Linux, CentOS, Debian, Fedora, openSUSE o Ubuntu, debes conceder al usuario del servicio al menos permisos de lectura a tus ubicaciones de almacenamiento.", + "MessageDownloadQueued": "Descarga puesta en la cola.", + "MessageEnablingOptionLongerScans": "Habilitar esta opción podría resultar en escaneos de bibliotecas significativamente más largos.", + "MessageFileReadError": "Hubo un error al leer el archivo. Por favor, intenta de nuevo.", "MessageForgotPasswordFileCreated": "El siguiente archivo fue creado en tu servidor y contiene instrucciones de como proceder:", - "MessageForgotPasswordInNetworkRequired": "Por favor intente de nuevo dentro de su red de hogar para iniciar el proceso de restablecimiento de contraseña.", - "MessageInstallPluginFromApp": "Este complemento debe ser instalado desde dentro de la aplicación en la que desea usarlo.", - "MessageInvalidForgotPasswordPin": "Se ha introducido un código PIN inválido o expirado. Por favor, inténtelo de nuevo.", - "MessageInvalidUser": "Nombre del usuario o contraseña inválidos. Por favor intenta de nuevo.", - "MessageItemSaved": "Ítem guardado.", - "MessageItemsAdded": "Ítems agregados.", + "MessageForgotPasswordInNetworkRequired": "Por favor, intenta de nuevo dentro de tu red local para iniciar el proceso de restablecimiento de contraseña.", + "MessageInstallPluginFromApp": "Este complemento debe ser instalado desde dentro de la aplicación en la que deseas usarlo.", + "MessageInvalidForgotPasswordPin": "Se ha introducido un código PIN inválido o expirado. Por favor, inténtalo de nuevo.", + "MessageInvalidUser": "Nombre de usuario o contraseña inválidos. Por favor, intenta de nuevo.", + "MessageItemSaved": "Elemento guardado.", + "MessageItemsAdded": "Elementos agregados.", "MessageLeaveEmptyToInherit": "Dejar vacío para heredar la configuración de un elemento superior o del valor predeterminado global.", "MessageNoAvailablePlugins": "No hay complementos disponibles.", - "MessageNoMovieSuggestionsAvailable": "No hay sugerencias de películas disponibles en este momento. Comienza a ver y a calificar tus películas, y regresa para ver tus recomendaciones.", - "MessageNoPluginsInstalled": "No tienes extensiones instaladas.", - "MessageNoTrailersFound": "No se encontraron tráilers. Instale el canal de tráilers para mejorar su experiencia con películas al agregar una biblioteca de tráilers desde el Internet.", + "MessageNoMovieSuggestionsAvailable": "No hay sugerencias de películas disponibles en este momento. Comienza a ver y a calificar tus películas, y luego regresa para ver tus recomendaciones.", + "MessageNoPluginsInstalled": "No tienes complementos instalados.", + "MessageNoTrailersFound": "No se encontraron trailers. Instala el canal de trailers para mejorar tu experiencia con películas al agregar una biblioteca de trailers desde Internet.", "MessageNothingHere": "Nada aquí.", "MessagePasswordResetForUsers": "Los siguientes usuarios han restablecido sus contraseñas. Ahora pueden iniciar sesión con los códigos PIN que se usaron para realizar el restablecimiento.", - "MessagePlayAccessRestricted": "La reproducción de este contenido está actualmente restringida. Póngase en contacto con el administrador del servidor para obtener más información.", - "MessagePleaseEnsureInternetMetadata": "Por favor asegúrese que la descarga de metadatos de internet esta habilitada.", - "MessagePleaseWait": "Espere por favor. Esto podría tomar un minuto.", - "MessagePluginConfigurationRequiresLocalAccess": "Para configurar este complemento por favor inicie sesión en su servidor local directamente.", - "MessagePluginInstallDisclaimer": "Los complementos desarrollados por miembros de la comunidad Jellyfin son una gran forma de mejorar su experiencia con Jellyfin con características y beneficios adicionales. Antes de instalar, conozca el impacto que pueden ocasionar en su Servidor Jellyfin, tales como exploración de la biblioteca que puede tomar más tiempo, procesamiento en segundo plano adicional y estabilidad del sistema reducida.", - "MessageReenableUser": "Vea abajo para volverlo a habilitar", - "MessageSettingsSaved": "Configuración guardada.", - "MessageTheFollowingLocationWillBeRemovedFromLibrary": "Las siguientes ubicaciones de medios se eliminarán de su biblioteca:", - "MessageUnableToConnectToServer": "No es posible conectarse al servidor seleccionado en este momento. Por favor asegúrese de que se encuentra en ejecución e inténtelo nuevamente.", - "MessageUnsetContentHelp": "El contenido será mostrado como carpetas simples. Para mejores resultados utilice el administrador de metadatos para establecer los tipos de contenido para las sub-carpetas.", - "MessageYouHaveVersionInstalled": "Actualmente cuenta con la vesión {0} instalada.", + "MessagePlayAccessRestricted": "La reproducción de este contenido está actualmente restringida. Por favor, contacta al administrador del servidor para obtener más información.", + "MessagePleaseEnsureInternetMetadata": "Por favor, asegúrate de que la descarga de metadatos de Internet está habilitada.", + "MessagePleaseWait": "Por favor, espera. Esto podría tomar un minuto.", + "MessagePluginConfigurationRequiresLocalAccess": "Para configurar este complemento por favor, inicia sesión en tu servidor local directamente.", + "MessagePluginInstallDisclaimer": "Los complementos desarrollados por miembros de la comunidad Jellyfin son una gran forma de mejorar tu experiencia con Jellyfin con características y beneficios adicionales. Antes de instalar, por favor, conoce el impacto que pueden ocasionar en tu servidor Jellyfin, tales como escaneo más largo de bibliotecas, procesamiento en segundo plano adicional y reducción de la estabilidad del sistema.", + "MessageReenableUser": "Ver abajo para volver a habilitar", + "MessageSettingsSaved": "Configuraciones guardadas.", + "MessageTheFollowingLocationWillBeRemovedFromLibrary": "Las siguientes ubicaciones de medios se removerán de tu biblioteca:", + "MessageUnableToConnectToServer": "No podemos conectarnos al servidor seleccionado en este momento. Por favor, asegúrate de que está funcionando e inténtalo de nuevo.", + "MessageUnsetContentHelp": "El contenido será mostrado como carpetas simples. Para mejores resultados utiliza el administrador de metadatos para establecer los tipos de contenido para las subcarpetas.", + "MessageYouHaveVersionInstalled": "Actualmente cuentas con la versión {0} instalada.", "Metadata": "Metadatos", - "MetadataManager": "Administrador de Metadatos", - "MetadataSettingChangeHelp": "Cambiar los ajustes de metadata afectará al contenido nuevo añadido a partir de ahora. Para actualizar el contenido existente, abra la pantalla de detalles y haga clic en actualizar, o realice actualizaciones masivas usando el administrador de metadata.", - "MinutesAfter": "minutos despues", - "MinutesBefore": "Minutos antes", + "MetadataManager": "Administrador de metadatos", + "MetadataSettingChangeHelp": "Cambiar la configuración de los metadatos afectará al nuevo contenido que se añada en el futuro. Para actualizar el contenido existente, abre la pantalla de detalles y haz clic en el botón actualizar, o realiza actualizaciones masivas usando el administrador de metadatos.", + "MinutesAfter": "minutos después", + "MinutesBefore": "minutos antes", "Mobile": "Móvil", "Monday": "Lunes", - "MoreFromValue": "Mas de {0}", + "MoreFromValue": "Más de {0}", "MoreUsersCanBeAddedLater": "Más usuarios pueden ser añadidos más tarde desde el panel de control.", "MoveLeft": "Mover a la izquierda", "MoveRight": "Mover a la derecha", - "MovieLibraryHelp": "Revisar la {0}Guía para nombrar películas{1}.", + "MovieLibraryHelp": "Revisar la {0}guía de nombrado de películas{1}.", "Movies": "Películas", "Mute": "Silenciar", - "MySubtitles": "Mis Subtitulos", + "MySubtitles": "Mis subtítulos", "Name": "Nombre", "Never": "Nunca", - "NewCollection": "Nueva Colección", - "NewCollectionHelp": "Las colecciones le permiten disfrutar de agrupaciones personalizadas de películas y otros contenidos de la biblioteca.", + "NewCollection": "Nueva colección", + "NewCollectionHelp": "Las colecciones te permiten disfrutar de agrupaciones personalizadas de películas y otros contenidos de la biblioteca.", "NewCollectionNameExample": "Ejemplo: Colección Guerra de las Galaxias", "NewEpisodes": "Episodios nuevos", "NewEpisodesOnly": "Solo episodios nuevos", "News": "Noticias", "Next": "Siguiente", - "NextUp": "A Continuación", - "NoNewDevicesFound": "No se encontraron nuevos dispositivos. Para agregar un sintonizador nuevo, cierre este cuadro de dialogo e introduzca a la información del dispositivo manualmente.", + "NextUp": "A continuación", + "NoNewDevicesFound": "No se encontraron nuevos dispositivos. Para agregar un sintonizador nuevo, cierra este diálogo e introduce la información del dispositivo manualmente.", "NoNextUpItemsMessage": "No se encontró nada. ¡Comienza a ver tus programas!", - "NoPluginConfigurationMessage": "El complemento no tiene configuraciones disponibles.", + "NoPluginConfigurationMessage": "Este complemento no tiene configuraciones disponibles.", "NoSubtitleSearchResultsFound": "No se encontraron resultados.", - "NoSubtitles": "Nada", - "NoSubtitlesHelp": "Los subtítulos no serán cargados por defecto. Pero pueden ser activados manualmente durante la reproducción.", + "NoSubtitles": "Ninguno", + "NoSubtitlesHelp": "Los subtítulos no serán cargados por defecto. Pueden ser activados manualmente durante la reproducción.", "None": "Ninguno", "NumLocationsValue": "{0} carpetas", "Off": "Apagar", "OneChannel": "Un canal", - "OnlyForcedSubtitles": "Únicamente forzados", - "OnlyForcedSubtitlesHelp": "Se cargarán únicamente subtítulos marcados como forzados.", + "OnlyForcedSubtitles": "Solo forzados", + "OnlyForcedSubtitlesHelp": "Solo se cargarán subtítulos marcados como forzados.", "OnlyImageFormats": "Solo formatos de imagen (VOBSUB, PGS, SUB)", "OptionAdminUsers": "Administradores", "OptionAlbum": "Álbum", - "OptionAlbumArtist": "Artista del Álbum", + "OptionAlbumArtist": "Artista del álbum", "OptionAllUsers": "Todos los usuarios", - "OptionAllowAudioPlaybackTranscoding": "Permitir la reproducción de audio que requiera de transcodificación", - "OptionAllowBrowsingLiveTv": "Permitir acceso a TV en Vivo", + "OptionAllowAudioPlaybackTranscoding": "Permitir la reproducción de audio que requiera transcodificación", + "OptionAllowBrowsingLiveTv": "Permitir acceso a TV en vivo", "OptionAllowContentDownloading": "Permitir descarga y sincronización de medios", "OptionAllowLinkSharing": "Permitir compartir medios en redes sociales", - "OptionAllowLinkSharingHelp": "Solo son compartidas paginas web que contengan información sobre los medios. Los archivos de medios nunca son compartidos públicamente. Son compartidos por un tiempo limitado y expiraran después de {0} días.", - "OptionAllowManageLiveTv": "Permitir gestión de grabación de TV en Vivo", + "OptionAllowLinkSharingHelp": "Solo son compartidas páginas web que contienen información sobre los medios. Los archivos de medios nunca son compartidos públicamente. Los compartidos tienen un límite de tiempo y expirarán después de {0} días.", + "OptionAllowManageLiveTv": "Permitir gestión de grabación de TV en vivo", "OptionAllowMediaPlayback": "Permitir reproducción de medios", - "OptionAllowMediaPlaybackTranscodingHelp": "Restringir el acceso a transcodificacion podría causar fallas en la reproducción in las aplicaciones Jellyfin debido a los formatos de medios no soportados.", + "OptionAllowMediaPlaybackTranscodingHelp": "Restringir el acceso a la transcodificación podría causar fallas en la reproducción en las aplicaciones Jellyfin debido a los formatos de medios no soportados.", "OptionAllowRemoteControlOthers": "Permitir control remoto de otros usuarios", "OptionAllowRemoteSharedDevices": "Permitir control remoto de dispositivos compartidos", - "OptionAllowRemoteSharedDevicesHelp": "Los dispositivos DLNA se consideran compartidos hasta que un usuario comienza a controlarlos.", + "OptionAllowRemoteSharedDevicesHelp": "Los dispositivos DLNA se considerarán compartidos hasta que un usuario comience a controlarlos.", "OptionAllowSyncTranscoding": "Permitir descarga y sincronización de medios que requieran transcodificación", "OptionAllowUserToManageServer": "Permitir a este usuario administrar el servidor", - "OptionAllowVideoPlaybackRemuxing": "Permitir reproducción de video que requiera conversión sin re-codificar", + "OptionAllowVideoPlaybackRemuxing": "Permitir reproducción de video que requiera conversión sin recodificar", "OptionAllowVideoPlaybackTranscoding": "Permitir la reproducción de video que requiera de transcodificación", "OptionArtist": "Artista", "OptionAscending": "Ascendente", @@ -981,129 +980,129 @@ "OptionAutomaticallyGroupSeries": "Fusionar automáticamente series esparcidas a través de múltiples carpetas", "OptionAutomaticallyGroupSeriesHelp": "Si se habilita, las series que se reparten a través de múltiples carpetas dentro de esta biblioteca serán fusionadas en una sola serie.", "OptionBlockBooks": "Libros", - "OptionBlockChannelContent": "Contenido de Canales de Internet", - "OptionBlockLiveTvChannels": "Canales de TV en Vivo", + "OptionBlockChannelContent": "Contenido de canales de Internet", + "OptionBlockLiveTvChannels": "Canales de TV en vivo", "OptionBlockMovies": "Películas", "OptionBlockMusic": "Música", - "OptionBlockTrailers": "Tráilers", + "OptionBlockTrailers": "Trailers", "OptionBlockTvShows": "Programas de TV", - "OptionCommunityRating": "Calificación de la Comunidad", + "OptionCommunityRating": "Calificación de la comunidad", "OptionContinuing": "Continuando", - "OptionCriticRating": "Calificación de la Crítica", + "OptionCriticRating": "Calificación de los críticos", "OptionCustomUsers": "Personalizado", "OptionDaily": "Diario", - "OptionDateAdded": "Fecha de Adición", - "OptionDateAddedFileTime": "Emplear fecha de creación del archivo", - "OptionDateAddedImportTime": "Emplear la fecha de escaneo en la biblioteca", - "OptionDatePlayed": "Fecha de Reproducción", + "OptionDateAdded": "Fecha de adición", + "OptionDateAddedFileTime": "Usar la fecha de creación del archivo", + "OptionDateAddedImportTime": "Usar la fecha de escaneo en la biblioteca", + "OptionDatePlayed": "Fecha de reproducción", "OptionDescending": "Descendente", "OptionDisableUser": "Desactivar este usuario", - "OptionDisableUserHelp": "Si está desactivado, el servidor no aceptará conexiones de este usuario. Las conexiones existentes serán finalizadas abruptamente.", + "OptionDisableUserHelp": "Si se desactiva, el servidor no aceptará conexiones de este usuario. Las conexiones existentes serán finalizadas abruptamente.", "OptionDislikes": "No me gusta", - "OptionDisplayFolderView": "Mostrar una vista de carpetas para mostrar carpetas de medios simples", - "OptionDisplayFolderViewHelp": "Muestra las carpetas junto con sus otras bibliotecas multimedia. Esto puede ser útil si desea tener una vista de carpeta sencilla.", + "OptionDisplayFolderView": "Mostrar una vista de carpetas para mostrar las carpetas simples de los medios", + "OptionDisplayFolderViewHelp": "Muestra las carpetas junto con sus otras bibliotecas de medios. Esto puede ser útil si deseas tener una vista simple de carpeta.", "OptionDownloadArtImage": "Arte", - "OptionDownloadBackImage": "Atras", + "OptionDownloadBackImage": "Parte trasera", "OptionDownloadBannerImage": "Banner", "OptionDownloadBoxImage": "Caja", - "OptionDownloadDiscImage": "DIsco", - "OptionDownloadImagesInAdvance": "Descargar las imágenes desde el inicio", - "OptionDownloadImagesInAdvanceHelp": "Por defecto, la mayoría de las imágenes son descargadas solo cuando son solicitadas por una aplicacion Jellyfin. Habilite esta opción para descargar todas las imágenes por adelantado, a medida que se agregen mas medios. Esto podría causar escaneos de bibliotecas significativamente largos.", + "OptionDownloadDiscImage": "Disco", + "OptionDownloadImagesInAdvance": "Descargar las imágenes con antelación", + "OptionDownloadImagesInAdvanceHelp": "Por defecto, la mayoría de las imágenes solo son descargadas cuando son solicitadas por una aplicación Jellyfin. Habilita esta opción para descargar todas las imágenes por adelantado, a medida que se agreguen nuevos medios. Esto podría causar escaneos de bibliotecas significativamente más largos.", "OptionDownloadMenuImage": "Menú", "OptionDownloadPrimaryImage": "Principal", "OptionDownloadThumbImage": "Miniatura", "OptionDvd": "DVD", - "OptionEmbedSubtitles": "Embeber dentro del contenedor", + "OptionEmbedSubtitles": "Incrustar dentro del contenedor", "OptionEnableAccessFromAllDevices": "Habilitar acceso desde todos los dispositivos", "OptionEnableAccessToAllChannels": "Habilitar acceso a todos los canales", - "OptionEnableAccessToAllLibraries": "Habilitar el acceso a todas las bibliotecas", + "OptionEnableAccessToAllLibraries": "Habilitar acceso a todas las bibliotecas", "OptionEnableExternalContentInSuggestions": "Habilitar contenido externo en las sugerencias", "OptionEnableExternalContentInSuggestionsHelp": "Permitir que los trailers de Internet y los programas de televisión en vivo se incluyan en el contenido sugerido.", "OptionEnableForAllTuners": "Habilitar para todos los dispositivos sintonizadores", - "OptionEnableM2tsMode": "Habilitar modo M2ts", - "OptionEnableM2tsModeHelp": "Habilita el modo m2ts cuando se codifican mpegs.", + "OptionEnableM2tsMode": "Habilitar modo M2TS", + "OptionEnableM2tsModeHelp": "Habilita el modo m2ts cuando se codifican mpegts.", "OptionEnded": "Finalizado", "OptionEquals": "Igual a", "OptionEstimateContentLength": "Estimar la duración del contenido cuando se transcodifica", "OptionEveryday": "Todos los días", "OptionExternallyDownloaded": "Descarga externa", - "OptionExtractChapterImage": "Habilitar extracción de imágenes de capitulo", + "OptionExtractChapterImage": "Habilitar la extracción de imágenes de los capítulos", "OptionFavorite": "Favoritos", "OptionFriday": "Viernes", - "OptionHasSpecialFeatures": "Características Especiales", + "OptionHasSpecialFeatures": "Características especiales", "OptionHasSubtitles": "Subtítulos", - "OptionHasThemeSong": "Canción del Tema", - "OptionHasThemeVideo": "Video del Tema", - "OptionHasTrailer": "Tráiler", - "OptionHideUser": "Ocultar este usuario en las pantallas de inicio de sesión", + "OptionHasThemeSong": "Canción temática", + "OptionHasThemeVideo": "Video temático", + "OptionHasTrailer": "Trailer", + "OptionHideUser": "Ocultar este usuario de las pantallas de inicio de sesión", "OptionHideUserFromLoginHelp": "Útil para cuentas privadas o de administrador ocultas. El usuario tendrá que iniciar sesión manualmente introduciendo su nombre de usuario y contraseña.", "OptionHlsSegmentedSubtitles": "Subtítulos segmentados HLS", "OptionHomeVideos": "Fotos", - "OptionIgnoreTranscodeByteRangeRequests": "Ignorar solicitudes de transcodificación de rango de byte", - "OptionIgnoreTranscodeByteRangeRequestsHelp": "Si se habilita, estas solicitudes seran honradas pero se ignorará el encabezado de rango de byte.", + "OptionIgnoreTranscodeByteRangeRequests": "Ignorar solicitudes de transcodificación de rango de bytes", + "OptionIgnoreTranscodeByteRangeRequestsHelp": "Si se habilita, estas solicitudes serán honradas pero se ignorará el encabezado de rango de bytes.", "OptionImdbRating": "Calificación de IMDb", "OptionLikes": "Me gusta", "OptionMax": "Máximo", - "OptionMissingEpisode": "Episodios Faltantes", + "OptionMissingEpisode": "Episodios faltantes", "OptionMonday": "Lunes", "OptionNameSort": "Nombre", - "OptionNew": "Nuevo...", + "OptionNew": "Nuevo…", "OptionNone": "Ninguno", "OptionOnAppStartup": "Al iniciar la aplicación", "OptionOnInterval": "En un intervalo", - "OptionParentalRating": "Clasificación Parental", + "OptionParentalRating": "Clasificación parental", "OptionPlainStorageFolders": "Mostrar todas las carpetas como carpetas de almacenamiento simples", - "OptionPlainStorageFoldersHelp": "Si se habilita, todos las carpetas serán representadas en DIDL como \"object.container.storageFolder\" en lugar de un tipo más específico, como \"object.container.person.musicArtist\".", + "OptionPlainStorageFoldersHelp": "Si se habilita, todos las carpetas serán representadas en DIDL como «object.container.storageFolder» en lugar de un tipo más específico, como «object.container.person.musicArtist».", "OptionPlainVideoItems": "Mostrar todos los videos como elementos de video simples", - "OptionPlainVideoItemsHelp": "Se se habilita, todos los videos serán representados en DIDL como \"object.item.videoItem\" en lugar de un tipo más específico, como \"object.item.videoItem.movie\".", - "OptionPlayCount": "Contador", + "OptionPlainVideoItemsHelp": "Si se habilita, todos los videos serán representados en DIDL como «object.item.videoItem» en lugar de un tipo más específico, como «object.item.videoItem.movie».", + "OptionPlayCount": "Contador de reproducciones", "OptionPlayed": "Reproducido", - "OptionPremiereDate": "Fecha de Estreno", + "OptionPremiereDate": "Fecha de estreno", "OptionProfilePhoto": "Foto", - "OptionProfileVideoAudio": "Audio del Video", - "OptionProtocolHls": "Transmisión en vivo por Http", - "OptionReleaseDate": "Fecha de Estreno", - "OptionReportByteRangeSeekingWhenTranscoding": "Reportar que el servidor soporta busqueda de bytes al transcodificar", - "OptionReportByteRangeSeekingWhenTranscodingHelp": "Esto es requerido para algunos dispositivos que no pueden hacer búsquedas por tiempo muy bien.", - "OptionRequirePerfectSubtitleMatch": "Solo descargar subtitulos que corresponden perfectamente para mis archivos de video", - "OptionRequirePerfectSubtitleMatchHelp": "Solicitar una coincidencia perfecta filtrara los subtitulos para incluir solo aquellos que han sido verificados exactamente con su archivo de video. Desmarcar esta opción incrementara las probabilidades de que los subtitulos sean descargados, a la vez que incrementara las posibilidades de obtener subtitulos mal sincronizados o con texto incorrecto.", - "OptionResElement": "Elemento Res", + "OptionProfileVideoAudio": "Audio del video", + "OptionProtocolHls": "Transmisión en vivo por HTTP", + "OptionReleaseDate": "Fecha de estreno", + "OptionReportByteRangeSeekingWhenTranscoding": "Reportar que el servidor soporta la búsqueda de bytes cuando se transcodifica", + "OptionReportByteRangeSeekingWhenTranscodingHelp": "Esto es requerido para algunos dispositivos que no pueden hacer búsquedas de tiempo muy bien.", + "OptionRequirePerfectSubtitleMatch": "Solo descargar subtítulos que coincidan perfectamente con mis archivos de video", + "OptionRequirePerfectSubtitleMatchHelp": "Solicitar una coincidencia perfecta filtrará los subtítulos para incluir solo aquellos que han sido probados y verificados exactamente con tu archivo de video. Desmarcar esta opción incrementará las probabilidades de que se descarguen subtítulos, pero incrementará las posibilidades de obtener subtítulos mal sincronizados o con texto incorrecto.", + "OptionResElement": "elemento res", "OptionResumable": "Reanudable", "OptionRuntime": "Duración", "OptionSaturday": "Sábado", "OptionSaveMetadataAsHidden": "Guardar metadatos e imágenes como archivos ocultos", - "OptionSaveMetadataAsHiddenHelp": "Cambiar esto aplicará a nuevos metadatos alamacenados en lo sucesivo. Los archivos de metadatos existentes serán actualizados la próxima vez que sean guardados por el Servidor Jellyfin.", + "OptionSaveMetadataAsHiddenHelp": "Cambiar esto se aplicará a los nuevos metadatos guardados en el futuro. Los archivos de metadatos existentes serán actualizados la próxima vez que sean guardados por el servidor Jellyfin.", "OptionSpecialEpisode": "Especiales", "OptionSubstring": "Subcadena", "OptionSunday": "Domingo", "OptionThursday": "Jueves", - "OptionTrackName": "Nombre de la Pista", + "OptionTrackName": "Nombre de la pista", "OptionTuesday": "Martes", "OptionTvdbRating": "Calificación de TVDB", - "OptionUnairedEpisode": "Episodios no Emitidos", + "OptionUnairedEpisode": "Episodios no emitidos", "OptionUnplayed": "No reproducido", - "OptionWakeFromSleep": "Al Despertar", + "OptionWakeFromSleep": "Despertar de la suspensión", "OptionWednesday": "Miércoles", - "OptionWeekdays": "Entre semana", + "OptionWeekdays": "Días de semana", "OptionWeekends": "Fines de semana", "OptionWeekly": "Semanal", "OriginalAirDateValue": "Fecha de emisión original: {0}", - "Overview": "Sinopsis", - "PackageInstallCancelled": "{0} (versión {1}) instalación cancelada.", - "PackageInstallCompleted": "{0} (version {1}) instalación completada.", - "PackageInstallFailed": "{0} (versión {1}) instalación fallida.", - "ParentalRating": "Clasificación Parental", - "PasswordMatchError": "La Contraseña y la confirmación de la contraseña deben coincidir.", + "Overview": "Resumen", + "PackageInstallCancelled": "Instalación cancelada de {0} (versión {1}).", + "PackageInstallCompleted": "Instalación completada de {0} (versión {1}).", + "PackageInstallFailed": "Instalación fallida de {0} (versión {1}).", + "ParentalRating": "Clasificación parental", + "PasswordMatchError": "La contraseña y la confirmación de la contraseña deben coincidir.", "PasswordResetComplete": "La contraseña ha sido restablecida.", - "PasswordResetConfirmation": "¿Está seguro de querer restablecer la contraseña?", - "PasswordResetHeader": "Restablecer Contraseña", + "PasswordResetConfirmation": "¿Estás seguro de querer restablecer la contraseña?", + "PasswordResetHeader": "Restablecer contraseña", "PasswordSaved": "Contraseña guardada.", "People": "Personas", - "PerfectMatch": "Coincidencia exacta", + "PerfectMatch": "Coincidencia perfecta", "Photos": "Fotos", "PictureInPicture": "Pantalla en pantalla", - "PinCodeResetComplete": "El código pin ha sido restablecido.", - "PinCodeResetConfirmation": "¿Esta seguro de querer restablecer el código pin?", + "PinCodeResetComplete": "El código PIN ha sido restablecido.", + "PinCodeResetConfirmation": "¿Estás seguro de que quieres restablecer el código PIN?", "PlaceFavoriteChannelsAtBeginning": "Colocar canales favoritos al inicio", "Play": "Reproducir", "PlayAllFromHere": "Reproducir todos desde aquí", @@ -1113,100 +1112,100 @@ "PlayNextEpisodeAutomatically": "Reproducir el siguiente episodio automáticamente", "Played": "Reproducido", "Playlists": "Listas de reproducción", - "PleaseAddAtLeastOneFolder": "Por favor agregue al menos una carpeta a esta biblioteca dando clic al botón de Agregar.", - "PleaseConfirmPluginInstallation": "Por favor haga clic en OK para confirmar que ha leido lo que se encuentra arriba y que desea proceder con la instalación del complemento.", - "PleaseEnterNameOrId": "Por favor introduzca un nombre o ID externo.", - "PleaseRestartServerName": "Por favor reinicie el Servidor Jellyfin - {0}.", - "PleaseSelectTwoItems": "Por favor selecciona al menos dos ítems.", - "PluginInstalledMessage": "El complemento ha sido instalado exitosamente. El Servidor Jellyfin necesitará reiniciarse para que los cambios surtan efecto.", - "PreferEmbeddedTitlesOverFileNames": "Prefererir titulos embebidos por encima de los nombres de archivo", - "PreferEmbeddedTitlesOverFileNamesHelp": "Esto determina el titulo mostrado por defecto cuando no hay disponibles metadatos en internet o localmente.", + "PleaseAddAtLeastOneFolder": "Por favor, agrega al menos una carpeta a esta biblioteca dando clic al botón Agregar.", + "PleaseConfirmPluginInstallation": "Por favor, haz clic en OK para confirmar que has leído lo que se encuentra arriba y que deseas proceder con la instalación del complemento.", + "PleaseEnterNameOrId": "Por favor, introduce un nombre o ID externo.", + "PleaseRestartServerName": "Por favor, reinicia el servidor Jellyfin - {0}.", + "PleaseSelectTwoItems": "Por favor, selecciona al menos dos elementos.", + "PluginInstalledMessage": "El complemento ha sido instalado con éxito. El servidor Jellyfin necesitará ser reiniciado para que los cambios surtan efecto.", + "PreferEmbeddedTitlesOverFileNames": "Preferir títulos incrustados a los nombres de archivo", + "PreferEmbeddedTitlesOverFileNamesHelp": "Esto determina el título mostrado por defecto cuando no hay disponibles metadatos en Internet o localmente.", "PreferredNotRequired": "Preferido, más no es requerido", - "Premiere": "Premier", + "Premiere": "Estreno", "Premieres": "Estrenos", "Previous": "Anterior", "Primary": "Principal", "Producer": "Productor", - "ProductionLocations": "Lugares de produccion", + "ProductionLocations": "Lugares de producción", "Programs": "Programas", "Quality": "Calidad", "QueueAllFromHere": "Encolar todos desde aquí", - "Raised": "Elevacion", + "Raised": "Elevado", "Rate": "Calificación", "RecentlyWatched": "Visto recientemente", - "RecommendationBecauseYouLike": "Porque te gustó {0}", + "RecommendationBecauseYouLike": "Porque te gusta {0}", "RecommendationBecauseYouWatched": "Porque viste {0}", "RecommendationDirectedBy": "Dirigido por {0}", "RecommendationStarring": "Protagonizado por {0}", "Record": "Grabar", - "RecordSeries": "Grabar Series", + "RecordSeries": "Grabar series", "RecordingCancelled": "Grabación cancelada.", - "RecordingPathChangeMessage": "Cambiar su carpeta de grabaciones no moverá sus grabaciones existentes del la antigua locación a la nueva. Necesita moverlas manualmente si lo desea.", + "RecordingPathChangeMessage": "Cambiar la carpeta de grabaciones no moverá las grabaciones existentes de la antigua ubicación a la nueva. Necesitan moverse manualmente si se desea.", "RecordingScheduled": "Grabación programada.", "Recordings": "Grabaciones", "Refresh": "Actualizar", - "RefreshDialogHelp": "Los metadatos son actualizados basándose en las configuraciones y servicios de internet que que estén activados en el panel de control de su Servidor de Jellyfin.", + "RefreshDialogHelp": "Los metadatos son actualizados basándose en las configuraciones y servicios de Internet que estén activados en el panel de control de tu servidor Jellyfin.", "RefreshMetadata": "Actualizar metadatos", - "RefreshQueued": "Actualización programada.", + "RefreshQueued": "Actualización puesta en la cola.", "ReleaseDate": "Fecha de estreno", - "RememberMe": "Recuerdame", + "RememberMe": "Recuérdame", "RemoveFromCollection": "Remover de la colección", - "RemoveFromPlaylist": "Eliminar de la lista de reproducción", + "RemoveFromPlaylist": "Remover de la lista de reproducción", "Repeat": "Repetir", - "RepeatAll": "Repetir todas", + "RepeatAll": "Repetir todos", "RepeatEpisodes": "Repetir episodios", "RepeatMode": "Modo de repetición", "RepeatOne": "Repetir uno", - "ReplaceAllMetadata": "Remplazar todos los metadatos", + "ReplaceAllMetadata": "Reemplazar todos los metadatos", "ReplaceExistingImages": "Reemplazar imágenes existentes", "RequiredForAllRemoteConnections": "Requerido para todas las conexiones remotas", - "RestartPleaseWaitMessage": "Por favor espere mientras el Servidor Jellyfin cierra y reinicia. Este puede tomar un minuto o dos.", + "RestartPleaseWaitMessage": "Por favor, espera mientras el servidor Jellyfin se apaga y reinicia. Esto puede tomar un minuto o dos.", "ResumeAt": "Reanudar desde {0}", - "Rewind": "Regresar", + "Rewind": "Rebobinar", "RunAtStartup": "Ejecutar al iniciar", "Runtime": "Duración", "Saturday": "Sábado", "Save": "Guardar", - "SaveSubtitlesIntoMediaFolders": "Guardar subtitulos en las carpetas de medios", - "SaveSubtitlesIntoMediaFoldersHelp": "Almacenar subtitulos junto al archivo de video permitirá administrarlos con mas facilidad.", - "ScanForNewAndUpdatedFiles": "Buscar archivos nuevos y actualizados", + "SaveSubtitlesIntoMediaFolders": "Guardar subtítulos en las carpetas de los medios", + "SaveSubtitlesIntoMediaFoldersHelp": "Almacenar los subtítulos junto a los archivos de video permitirá administrarlos con más facilidad.", + "ScanForNewAndUpdatedFiles": "Escanear por archivos nuevos y actualizados", "ScanLibrary": "Escanear biblioteca", - "Schedule": "Programacion", + "Schedule": "Programación", "Screenshot": "Captura de pantalla", "Screenshots": "Capturas de pantalla", "Search": "Buscar", - "SearchForCollectionInternetMetadata": "Buscar en internet ilustraciones y metadatos", + "SearchForCollectionInternetMetadata": "Buscar en Internet por ilustraciones y metadatos", "SearchForMissingMetadata": "Buscar metadatos faltantes", - "SearchForSubtitles": "Buscar Subtitulos", + "SearchForSubtitles": "Buscar subtítulos", "SearchResults": "Resultados de la búsqueda", "SendMessage": "Enviar mensaje", "SeriesCancelled": "Serie cancelada.", - "SeriesDisplayOrderHelp": "Ordenar los episodios por fecha transmisión, orden del DVD o por su numeración absoluta.", + "SeriesDisplayOrderHelp": "Ordenar los episodios por fecha de emisión, orden del DVD o numeración absoluta.", "SeriesRecordingScheduled": "Grabación de series programadas.", - "SeriesSettings": "Configuración de la Serie", + "SeriesSettings": "Configuración de la serie", "SeriesYearToPresent": "{0} - Actualidad", - "ServerNameIsRestarting": "El Servidor Jellyfin - {0} se esta reiniciando.", - "ServerNameIsShuttingDown": "El Servidor Jellyfin - {0} se esta apagando.", - "ServerRestartNeededAfterPluginInstall": "El Servidor Jellyfin necesitará reiniciarse después de instalar un complemento.", - "ServerUpdateNeeded": "Este Servidor Jellyfin necesita ser actualizado. Para descargar la ultima versión, por favor visite {0}", + "ServerNameIsRestarting": "El servidor Jellyfin - {0} se está reiniciando.", + "ServerNameIsShuttingDown": "El servidor Jellyfin - {0} se está apagando.", + "ServerRestartNeededAfterPluginInstall": "El servidor Jellyfin necesitará reiniciarse después de instalar un complemento.", + "ServerUpdateNeeded": "Este servidor Jellyfin necesita ser actualizado. Para descargar la última versión, por favor, visita {0}", "Settings": "Configuración", "SettingsSaved": "Configuración guardada.", - "SettingsWarning": "Cambiar estos valores podría causar inestabilidad o fallas de conexión, si experimenta cualquier problema, recomendamos volver a los valores por defecto.", + "SettingsWarning": "Cambiar estos valores podría causar inestabilidad o fallas de conexión. Si experimentas cualquier problema, recomendamos volver a los valores por defecto.", "Share": "Compartir", "ShowAdvancedSettings": "Mostrar configuraciones avanzadas", "ShowIndicatorsFor": "Mostrar indicadores para:", - "ShowTitle": "Mostrar titulo", + "ShowTitle": "Mostrar título", "ShowYear": "Mostrar año", "Shows": "Programas", "Shuffle": "Aleatorio", - "SimultaneousConnectionLimitHelp": "El numero máximo de transmisiones simultaneas permitidas. Ingrese 0 para ilimitados.", + "SimultaneousConnectionLimitHelp": "El numero máximo de transmisiones simultáneas permitidas. Ingresa 0 para sin límite.", "SkipEpisodesAlreadyInMyLibrary": "No grabar episodios que ya se encuentran en mi biblioteca", "SkipEpisodesAlreadyInMyLibraryHelp": "Los episodios serán comparados usando el numero de temporada y de episodio, cuando estén disponibles.", "Small": "Pequeño", "SmallCaps": "Mayúsculas pequeñas", "Smaller": "Más pequeño", "Smart": "Inteligente", - "SmartSubtitlesHelp": "Los subtítulos que coincidan con el lenguaje preferido serán cargados cuando el audio se encuentre en un lenguaje extranjero.", + "SmartSubtitlesHelp": "Los subtítulos que coincidan con el idioma preferido serán cargados cuando el audio se encuentre en un idioma extranjero.", "Songs": "Canciones", "Sort": "Ordenar", "SortByValue": "Ordenar por {0}", @@ -1215,94 +1214,94 @@ "Sports": "Deportes", "StopRecording": "Detener grabación", "Studios": "Estudios", - "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Estos ajustes también aplican a cualquier reproducción de Chromecast iniciada por este dispositivo.", - "SubtitleAppearanceSettingsDisclaimer": "Estos ajustes no se aplicarán a los subtítulos gráficos (PGS, DVD, etc.) o a los subtítulos ASS/SSA que incrustan sus propios estilos.", - "SubtitleDownloadersHelp": "Habilite y priorice sus recolectores de subtitulos en orden de preferencia.", + "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Estos ajustes también aplican a cualquier reproducción en Chromecast iniciada por este dispositivo.", + "SubtitleAppearanceSettingsDisclaimer": "Estos ajustes no se aplicarán a los subtítulos gráficos (PGS, DVD, etc.) o a los subtítulos ASS/SSA que incorporen sus propios estilos.", + "SubtitleDownloadersHelp": "Habilita y prioriza tus recolectores de subtítulos en orden de prioridad.", "Subtitles": "Subtítulos", "Suggestions": "Sugerencias", "Sunday": "Domingo", "Sync": "Sincronizar", - "SystemDlnaProfilesHelp": "Los perfiles del sistema son de sólo lectura. Los cambios a un perfíl de sistema serán guardados en un perfíl personalizado nuevo.", + "SystemDlnaProfilesHelp": "Los perfiles del sistema son de solo lectura. Los cambios a un perfil del sistema serán guardados en un nuevo perfil personalizado.", "TabAccess": "Acceso", "TabAdvanced": "Avanzado", - "TabAlbumArtists": "Artistas del Álbum", + "TabAlbumArtists": "Artistas del álbum", "TabAlbums": "Álbumes", "TabArtists": "Artistas", "TabCatalog": "Catálogo", "TabChannels": "Canales", "TabCollections": "Colecciones", "TabContainers": "Contenedores", - "TabDashboard": "Panel de Control", + "TabDashboard": "Panel de control", "TabDevices": "Dispositivos", - "TabDirectPlay": "Reproducción Directa", - "TabDisplay": "Pantalla", + "TabDirectPlay": "Reproducción directa", + "TabDisplay": "Mostrar", "TabEpisodes": "Episodios", "TabFavorites": "Favoritos", "TabGenres": "Géneros", "TabGuide": "Guía", "TabLatest": "Recientes", - "TabLiveTV": "TV en Vivo", - "TabLogs": "Bitácoras", + "TabLiveTV": "TV en vivo", + "TabLogs": "Registros", "TabMetadata": "Metadatos", "TabMovies": "Películas", "TabMusic": "Música", - "TabMusicVideos": "Videos Musicales", - "TabMyPlugins": "Mis Complementos", + "TabMusicVideos": "Videos musicales", + "TabMyPlugins": "Mis complementos", "TabNetworks": "Cadenas", "TabNfoSettings": "Configuración de NFO", "TabNotifications": "Notificaciones", "TabOther": "Otros", - "TabParentalControl": "Control Parental", + "TabParentalControl": "Control parental", "TabPassword": "Contraseña", "TabPlayback": "Reproducción", - "TabPlaylist": "Lista de Reproducción", + "TabPlaylist": "Lista de reproducción", "TabPlaylists": "Listas de reproducción", "TabPlugins": "Complementos", - "TabProfile": "Perfíl", + "TabProfile": "Perfil", "TabProfiles": "Perfiles", "TabRecordings": "Grabaciones", "TabResponses": "Respuestas", "TabResumeSettings": "Reanudar", - "TabScheduledTasks": "Tareas Programadas", + "TabScheduledTasks": "Tareas programadas", "TabServer": "Servidor", "TabSettings": "Configuración", "TabShows": "Programas", "TabSongs": "Canciones", "TabStreaming": "Transmisión", "TabSuggestions": "Sugerencias", - "TabTrailers": "Tráilers", + "TabTrailers": "Trailers", "TabTranscoding": "Transcodificación", - "TabUpcoming": "Proximamente", + "TabUpcoming": "Próximamente", "TabUsers": "Usuarios", "Tags": "Etiquetas", "TagsValue": "Etiquetas: {0}", - "TellUsAboutYourself": "Díganos sobre usted", - "ThemeSongs": "Canciones de Tema", - "ThemeVideos": "Videos de Tema", - "TheseSettingsAffectSubtitlesOnThisDevice": "Estas configuraciones solo afectan subtitulo de este dispositivo", - "ThisWizardWillGuideYou": "Este asistente le guiará a través del proceso de instalación. Para comenzar, por favor seleccione su lenguaje preferido.", + "TellUsAboutYourself": "Háblanos de ti", + "ThemeSongs": "Canciones temáticas", + "ThemeVideos": "Videos temáticos", + "TheseSettingsAffectSubtitlesOnThisDevice": "Estas configuraciones afectan a los subtítulos en este dispositivo", + "ThisWizardWillGuideYou": "Este asistente te guiará a través del proceso de instalación. Para comenzar, por favor, selecciona tu idioma preferido.", "Thumb": "Miniatura", "Thursday": "Jueves", - "TitleHardwareAcceleration": "Aceleración por Hardware", - "TitleHostingSettings": "Configuraciones de Alojamiento", + "TitleHardwareAcceleration": "Aceleración por hardware", + "TitleHostingSettings": "Configuraciones de alojamiento", "TitlePlayback": "Reproducción", - "TrackCount": "{0} Pistas", - "Trailers": "Tráilers", + "TrackCount": "{0} pistas", + "Trailers": "Trailers", "Transcoding": "Transcodificando", "Tuesday": "Martes", - "TvLibraryHelp": "Vea la {0}Guía para nombrar series de TV{1}.", + "TvLibraryHelp": "Revisa la {0}guía de nombrado de series de TV{1}.", "Uniform": "Uniforme", - "UninstallPluginConfirmation": "¿Está seguro de querer desinstalar {0}?", - "UninstallPluginHeader": "Desinstalar Complemento", - "Unmute": "Activar Sonido", + "UninstallPluginConfirmation": "¿Estás seguro de querer desinstalar {0}?", + "UninstallPluginHeader": "Desinstalar complemento", + "Unmute": "Activar sonido", "Unplayed": "No reproducido", "Unrated": "Sin clasificar", "Up": "Arriba", "Upload": "Subir", - "UserAgentHelp": "Proporcionar un encabezado HTTP personalizado de agente de usuario.", - "UserProfilesIntro": "Jellyfin incluye soporte para perfiles de usuario con configuraciones de pantalla detalladas, estado de reproducción y controles parentales.", + "UserAgentHelp": "Proporciona un encabezado HTTP de agente de usuario personalizado.", + "UserProfilesIntro": "Jellyfin incluye soporte para perfiles de usuario con detalladas configuraciones de pantalla, estado de reproducción y controles parentales.", "ValueAlbumCount": "{0} álbumes", - "ValueAudioCodec": "Códec de Audio: {0}", + "ValueAudioCodec": "Códec de audio: {0}", "ValueCodec": "Códec: {0}", "ValueConditions": "Condiciones: {0}", "ValueContainer": "Contenedor: {0}", @@ -1321,23 +1320,23 @@ "ValueSpecialEpisodeName": "Especial - {0}", "ValueTimeLimitMultiHour": "Límite de tiempo: {0} horas", "ValueTimeLimitSingleHour": "Límite de tiempo: 1 hora", - "ValueVideoCodec": "Códec de Video: {0}", + "ValueVideoCodec": "Códec de video: {0}", "VideoRange": "Rango de video", - "ViewAlbum": "Ver album", + "ViewAlbum": "Ver álbum", "ViewArtist": "Ver artista", "ViewPlaybackInfo": "Ver información de reproducción", "Watched": "Visto", "Wednesday": "Miércoles", "WelcomeToProject": "¡Bienvenido a Jellyfin!", - "Whitelist": "Permitidos", - "WizardCompleted": "Eso es todo lo que necesitamos por ahora. Jellyfin ha comenzado a recolectar información sobre su biblioteca multimedia. Revise algunas de nuestras aplicaciones, y luego haga clic en Finalizar para ver el Panel de Control.", + "Whitelist": "Lista blanca", + "WizardCompleted": "Eso es todo lo que necesitamos por ahora. Jellyfin ha comenzado a recolectar información sobre tu biblioteca de medios. Revisa alguna de nuestras aplicaciones, y luego haz clic en Finalizar para ver el Panel de control.", "Writer": "Escritor", "XmlDocumentAttributeListHelp": "Estos atributos se aplican al elemento raíz de cada respuesta XML.", - "XmlTvKidsCategoriesHelp": "Los programas con estas categorías serán mostrados como programas infantiles. Separe varios con un \"|\".", - "XmlTvMovieCategoriesHelp": "Los programas con estas categorías serán mostrado como películas. Separe varios con un \"|\".", - "XmlTvNewsCategoriesHelp": "Los programas con estas categorías serán mostrado como programas noticiosos. Separe varios con un \"|\".", - "XmlTvPathHelp": "Ruta al archivo XMLTV. Jellyfin leerá este archivo y lo revisará periódicamente en busca de actualizaciones. Usted es responsable de crear y actualizar el archivo.", - "XmlTvSportsCategoriesHelp": "Los programas con estas categorías serán mostrados como programas deportivos. Separe varios con un \"|\".", + "XmlTvKidsCategoriesHelp": "Los programas con estas categorías serán mostrados como programas infantiles. Separa varios con un «|».", + "XmlTvMovieCategoriesHelp": "Los programas con estas categorías serán mostrados como películas. Separa varios con un «|».", + "XmlTvNewsCategoriesHelp": "Los programas con estas categorías serán mostrados como programas de noticias. Separa varios con un «|».", + "XmlTvPathHelp": "Una ruta a un archivo XMLTV. Jellyfin leerá este archivo y lo revisará periódicamente en busca de actualizaciones. Tú eres responsable de crear y actualizar el archivo.", + "XmlTvSportsCategoriesHelp": "Los programas con estas categorías serán mostrados como programas deportivos. Separa varios con un «|».", "Yes": "Sí", "Yesterday": "Ayer", "Actor": "Actor", @@ -1345,16 +1344,16 @@ "Auto": "Auto", "ButtonInfo": "Info", "ButtonNo": "No", - "ButtonOk": "Ok", + "ButtonOk": "OK", "ButtonTrailer": "Trailer", - "AuthProviderHelp": "Seleccione un proveedor de autenticación que se utilizará para autenticar la contraseña de este usuario.", + "AuthProviderHelp": "Selecciona un proveedor de autenticación que se utilizará para autenticar la contraseña de este usuario.", "Director": "Director", "Extras": "Extras", "General": "General", "HeaderAdmin": "Administrador", "HeaderApp": "Aplicación", "HeaderError": "Error", - "HeaderFavoriteMovies": "Peliculas favoritas", + "HeaderFavoriteMovies": "Películas favoritas", "HeaderFavoriteShows": "Programas favoritos", "HeaderFavoriteEpisodes": "Episodios favoritos", "HeaderFavoriteAlbums": "Álbumes favoritos", @@ -1373,7 +1372,7 @@ "LabelServerName": "Nombre del servidor:", "LabelTranscodePath": "Ruta de transcodificación:", "LabelTranscodes": "Transcodificaciones:", - "LabelUserLoginAttemptsBeforeLockout": "Intentos fallidos de inicio de sesión antes de que el usuario se bloquee:", + "LabelUserLoginAttemptsBeforeLockout": "Intentos fallidos de inicio de sesión antes de que el usuario sea bloqueado:", "DashboardVersionNumber": "Versión: {0}", "DashboardServerName": "Servidor: {0}", "DashboardOperatingSystem": "Sistema operativo: {0}", @@ -1382,40 +1381,40 @@ "LabelWeb": "Web:", "LaunchWebAppOnStartup": "Iniciar la interfaz web al iniciar el servidor", "LaunchWebAppOnStartupHelp": "Abre el cliente web en su navegador web predeterminado cuando se inicia el servidor. Esto no ocurrirá cuando se utilice la función de reinicio del servidor.", - "LeaveBlankToNotSetAPassword": "Puede dejar este campo en blanco para no establecer ninguna contraseña.", + "LeaveBlankToNotSetAPassword": "Puedes dejar este campo en blanco para no establecer ninguna contraseña.", "MediaInfoCodec": "Códec", "MediaInfoSoftware": "Software", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Dato", "MediaInfoStreamTypeEmbeddedImage": "Imagen incrustada", - "MediaInfoStreamTypeSubtitle": "Subtitulo", + "MediaInfoStreamTypeSubtitle": "Subtítulo", "MediaInfoStreamTypeVideo": "Video", - "MessageImageFileTypeAllowed": "Solo se admite archivos JPEG y PNG.", - "MessageImageTypeNotSelected": "Seleccione un tipo de imagen en el menú desplegable.", - "MessageNoCollectionsAvailable": "Las colecciones le permiten disfrutar de agrupaciones personalizadas de películas, series y álbumes. Haga clic en el botón + para comenzar a crear colecciones.", - "MessageNoServersAvailable": "No se encontraron servidores utilizando el descubrimiento automático del servidor.", + "MessageImageFileTypeAllowed": "Solo son soportados archivos JPEG y PNG.", + "MessageImageTypeNotSelected": "Por favor, selecciona un tipo de imagen del menú desplegable.", + "MessageNoCollectionsAvailable": "Las colecciones te permiten disfrutar de agrupaciones personalizadas de películas, series y álbumes. Haz clic en el botón + para comenzar a crear colecciones.", + "MessageNoServersAvailable": "No se encontraron servidores utilizando el descubrimiento automático de servidores.", "MusicAlbum": "Álbum de música", "MusicArtist": "Artista musical", "MusicVideo": "Video musical", "No": "No", "Normal": "Normal", "Option3D": "3D", - "OptionBanner": "Baner", + "OptionBanner": "Banner", "OptionBluray": "Blu-ray", "OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)", "OptionDownloadLogoImage": "Logo", "OptionIsHD": "HD", "OptionIsSD": "SD", "OptionList": "Lista", - "OptionLoginAttemptsBeforeLockout": "Determina cuántos intentos de inicio de sesión incorrectos se pueden hacer antes de que ocurra el bloqueo.", - "OptionLoginAttemptsBeforeLockoutHelp": "Un valor cero significa heredar el valor predeterminado de tres intentos para los usuarios normales y cinco para los administradores. Ajustar esto a -1 deshabilitará la función.", + "OptionLoginAttemptsBeforeLockout": "Determina cuantos intentos de inicio de sesión incorrectos se pueden hacer antes de que ocurra el bloqueo.", + "OptionLoginAttemptsBeforeLockoutHelp": "Un valor de cero significa heredar el valor predeterminado de tres intentos para los usuarios normales y cinco para los administradores. Ajustar esto a -1 deshabilitará la función.", "OptionPoster": "Póster", "OptionPosterCard": "Ficha de póster", "OptionProfileAudio": "Audio", "OptionProfileVideo": "Video", "OptionProtocolHttp": "HTTP", "OptionRegex": "Expresión regular", - "PasswordResetProviderHelp": "Elija un proveedor de restablecimiento de contraseña para usar cuando este usuario solicite un restablecimiento de contraseña", + "PasswordResetProviderHelp": "Elige un proveedor de restablecimiento de contraseña para usar cuando este usuario solicite un restablecimiento de contraseña", "PlaybackData": "Datos de reproducción", "Series": "Series", "SubtitleOffset": "Desplazamiento de subtítulos", @@ -1427,76 +1426,133 @@ "ValueSeriesCount": "{0} series", "Vertical": "Vertical", "OptionThumb": "Miniatura", - "OptionThumbCard": "Pequeña miniatura", - "HeaderFavoriteBooks": "Libros Favoritos", + "OptionThumbCard": "Tarjeta miniatura", + "HeaderFavoriteBooks": "Libros favoritos", "LabelPleaseRestart": "Los cambios tendrán efecto después de recargar manualmente el cliente web.", "LabelPlayMethod": "Método de reproducción:", "LabelPlayer": "Reproductor:", "LabelFolder": "Carpeta:", - "LabelBaseUrlHelp": "Puede añadir un subdirectorio personalizado aquí para acceder al servidor desde una URL más única.", + "LabelBaseUrlHelp": "Añade un subdirectorio personalizado a la URL del servidor. Por ejemplo: http://ejemplo.com/<urlbase>", "LabelBaseUrl": "URL base:", "LabelBitrate": "Velocidad de bits:", "LabelAudioSampleRate": "Frecuencia de muestreo de audio:", "LabelAudioCodec": "Códec de audio:", "LabelAudioChannels": "Canales de audio:", "LabelAudioBitrate": "Velocidad de bits de audio:", - "LabelAudioBitDepth": "Profundidad de bit de audio:", + "LabelAudioBitDepth": "Profundidad de bits de audio:", "FetchingData": "Obteniendo datos adicionales", "CopyStreamURLSuccess": "URL copiada con éxito.", "CopyStreamURL": "Copiar la URL de la transmisión", "ButtonAddImage": "Agregar imagen", "TabNetworking": "Redes", - "MusicLibraryHelp": "Revisar la {0}Guía para nombrar música{1}.", + "MusicLibraryHelp": "Revisar la {0}guía de nombrado de música{1}.", "MoreMediaInfo": "Información multimedia", "LabelVideoCodec": "Códec de video:", - "LabelVideoBitrate": "Velocidad de bits:", + "LabelVideoBitrate": "Velocidad de bits de video:", "LabelTranscodingProgress": "Progreso de la transcodificación:", "LabelTranscodingFramerate": "Velocidad de cuadros de la transcodificación:", "LabelSize": "Tamaño:", - "SelectAdminUsername": "Por favor seleccione un nombre de usuario para la cuenta de administrador.", - "EnableFastImageFadeInHelp": "Habilita la animación de desvanecido rápido para las imágenes cargadas", + "SelectAdminUsername": "Por favor, selecciona un nombre de usuario para la cuenta de administrador.", "LabelDroppedFrames": "Cuadros saltados:", - "CopyStreamURLError": "Hubo un error copiando la URL.", + "CopyStreamURLError": "Hubo un error al copiar la URL.", "ButtonSplit": "Dividir", "WeeklyAt": "{0}s a las {1}", "OnApplicationStartup": "Cuando se inicia la aplicación", "EveryXHours": "Cada {0} horas", "EveryHour": "Cada hora", "EveryXMinutes": "Cada {0} minutos", - "OnWakeFromSleep": "Al despertar del sueño", + "OnWakeFromSleep": "Al despertar de la suspensión", "DailyAt": "Diariamente a las {0}", "LastSeen": "Ultima vez visto {0}", "PersonRole": "como {0}", "ListPaging": "{0}-{1} de {2}", - "WriteAccessRequired": "El servidor Jellyfin requiere permiso de escritura en esta carpeta. Por favor brinde acceso de escritura e intente de nuevo.", - "PathNotFound": "No se pudo encontrar la ruta. Por favor asegúrese de que la ruta es valida e intente de nuevo.", + "WriteAccessRequired": "El servidor Jellyfin requiere permiso de escritura en esta carpeta. Por favor, asegúrate de tener acceso de escritura e inténtalo de nuevo.", + "PathNotFound": "No se pudo encontrar la ruta. Por favor, asegúrate de que la ruta es válida e inténtalo de nuevo.", "Track": "Pista", "Season": "Temporada", - "ReleaseGroup": "Grupo de salida", - "PreferEmbeddedEpisodeInfosOverFileNames": "Preferir información embebida en el episodio sobre el nombre de archivo", - "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Esto utiliza la información de el episodio desde metadatos embebidos si esta disponible.", - "PlaybackErrorNoCompatibleStream": "Hubo un error con el perfilado del cliente y el servidor no esta enviando un formato compatible.", + "ReleaseGroup": "Grupo que lo estrenó", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferir información del episodio incrustada a los nombres de archivo", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Esto utiliza la información del episodio desde los metadatos incrustados si están disponibles.", + "PlaybackErrorNoCompatibleStream": "Este cliente no es compatible con los medios y el servidor no está enviando un formato de medios compatible.", "Person": "Persona", "OtherArtist": "Otro artista", "OptionRandom": "Aleatorio", - "OptionForceRemoteSourceTranscoding": "Forzar transcodificación para fuentes remotas (como TV en Vivo)", - "NoCreatedLibraries": "Parece que no has creado ninguna biblioteca todavía. {0}Quisieras crear una ahora?{1}", + "OptionForceRemoteSourceTranscoding": "Forzar transcodificación de fuentes remotas (como TV en vivo)", + "NoCreatedLibraries": "Parece que no has creado ninguna biblioteca todavía. {0}¿Quisieras crear una ahora?{1}", "Movie": "Película", - "MessageConfirmAppExit": "Deseas salir?", + "MessageConfirmAppExit": "¿Deseas salir?", "LabelVideoResolution": "Resolución de video:", "LabelStreamType": "Tipo de transmisión:", - "EnableFastImageFadeIn": "Desvanecido de imagen rápido", "LabelPlayerDimensions": "Dimensiones del reproductor:", "LabelCorruptedFrames": "Cuadros corruptos:", "HeaderNavigation": "Navegación", "HeaderFavoritePeople": "Personas favoritas", "Episode": "Episodio", - "ClientSettings": "Configuración de cliente", + "ClientSettings": "Configuración del cliente", "BoxSet": "Box Set", - "AskAdminToCreateLibrary": "Preguntar al administrador para crear una biblioteca.", + "AskAdminToCreateLibrary": "Pide a un administrador crear una biblioteca.", "Artist": "Artista", - "AllowFfmpegThrottlingHelp": "Cuando una transcodificación o remux se encuentra muy por delante de la posición de reproducción, pausar el proceso para consumir menos recursos. Esto es mas practico cuando se esta viendo sin buscar constantemente. Deshabilitar esta opción si experimentas problemas de reproducción.", - "AllowFfmpegThrottling": "Aceleración de Transcoders", - "AlbumArtist": "Álbum de artista", - "Album": "Álbum" + "AllowFfmpegThrottlingHelp": "Cuando una transcodificación o remuxeado se adelanta lo suficiente de la posición de reproducción actual, se pausa el proceso para que consuma menos recursos. Esto es más útil cuando se mira sin buscar con frecuencia. Apaga esto si experimentas problemas de reproducción.", + "AllowFfmpegThrottling": "Regular transcodificaciones", + "AlbumArtist": "Artista del álbum", + "Album": "Álbum", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "LabelDeinterlaceMethod": "Método de desentrelazado:", + "DeinterlaceMethodHelp": "Seleccione el método de desentrelazado que se usará al transcodificar contenido entrelazado.", + "Filter": "Filtro", + "New": "Nuevo", + "MessageUnauthorizedUser": "No estás autorizado para acceder al servidor en este momento. Por favor, contacta al administrador del servidor para más información.", + "LabelLibraryPageSizeHelp": "Establece el número de elementos a mostrar en una página de biblioteca. Establece en 0 para deshabilitar el paginado.", + "LabelLibraryPageSize": "Tamaño de las páginas de las bibliotecas:", + "HeaderFavoritePlaylists": "Listas de reproducción favoritas", + "ButtonTogglePlaylist": "Lista de reproducción", + "ButtonToggleContextMenu": "Más", + "UnsupportedPlayback": "Jellyfin no puede desencriptar contenido protegido por DRM de todas formas todo el contenido será intentado, incluyendo los títulos protegidos. Algunos archivos pueden aparecer completamente en negro debido al encriptado o características no soportadas, como títulos interactivos.", + "TabDVR": "DVR", + "SaveChanges": "Guardar cambios", + "LabelRequireHttpsHelp": "Si se marca, el servidor redirigirá automáticamente todas las solicitudes a través de HTTP a HTTPS. Esto no tiene efecto si el servidor no está escuchando en HTTPS.", + "LabelRequireHttps": "Requerir HTTPS", + "LabelNightly": "Nocturno", + "LabelStable": "Estable", + "LabelChromecastVersion": "Versión de Chromecast", + "LabelEnableHttpsHelp": "Permite al servidor escuchar en el puerto HTTPS configurado. Un certificado válido también debe ser configurado para que esto tenga efecto.", + "LabelEnableHttps": "Habilitar HTTPS", + "HeaderServerAddressSettings": "Configuración de la dirección del servidor", + "HeaderRemoteAccessSettings": "Opciones de acceso remoto", + "HeaderHttpsSettings": "Opciones HTTPS", + "HeaderDVR": "DVR", + "ApiKeysCaption": "Lista de claves API actualmente habilitadas", + "SyncPlayAccessHelp": "Selecciona el nivel de acceso que este usuario tiene a la función SyncPlay. SyncPlay permite sincronizar la reproducción con otros dispositivos.", + "MessageSyncPlayErrorMedia": "¡Fallo al activar SyncPlay! Error en el archivo de medios.", + "MessageSyncPlayErrorMissingSession": "¡Fallo al activar SyncPlay! Falta la sesión.", + "MessageSyncPlayErrorNoActivePlayer": "No se ha encontrado ningún reproductor activo. SyncPlay ha sido desactivado.", + "MessageSyncPlayErrorAccessingGroups": "Se produjo un error al acceder a la lista de grupos.", + "MessageSyncPlayLibraryAccessDenied": "El acceso a este contenido está restringido.", + "MessageSyncPlayJoinGroupDenied": "Permiso requerido para usar SyncPlay.", + "MessageSyncPlayCreateGroupDenied": "Permiso requerido para crear un grupo.", + "MessageSyncPlayGroupDoesNotExist": "Fallo al unirse al grupo porque éste no existe.", + "MessageSyncPlayPlaybackPermissionRequired": "Permiso de reproducción requerido.", + "MessageSyncPlayNoGroupsAvailable": "No hay grupos disponibles. Empieza a reproducir algo primero.", + "MessageSyncPlayGroupWait": "{0} está cargando...", + "MessageSyncPlayUserLeft": "{0} abandonó el grupo.", + "MessageSyncPlayUserJoined": "{0} se ha unido al grupo.", + "MessageSyncPlayDisabled": "SyncPlay deshabilitado.", + "MessageSyncPlayEnabled": "SyncPlay habilitado.", + "LabelSyncPlayAccess": "Acceso a SyncPlay", + "LabelSyncPlayAccessNone": "Deshabilitado para este usuario", + "LabelSyncPlayAccessJoinGroups": "Permitir al usuario unirse a grupos", + "LabelSyncPlayAccessCreateAndJoinGroups": "Permitir al usuario crear y unirse a grupos", + "LabelSyncPlayLeaveGroupDescription": "Deshabilitar SyncPlay", + "LabelSyncPlayLeaveGroup": "Abandonar grupo", + "LabelSyncPlayNewGroupDescription": "Crear un nuevo grupo", + "LabelSyncPlayNewGroup": "Nuevo grupo", + "LabelSyncPlaySyncMethod": "Método de sincronización:", + "LabelSyncPlayPlaybackDiff": "Diferencia de tiempo de reproducción:", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Tiempo compensado respecto al servidor:", + "HeaderSyncPlayEnabled": "SyncPlay habilitado", + "HeaderSyncPlaySelectGroup": "Unirse a un grupo", + "EnableDetailsBannerHelp": "Mostrar una imagen banner en la parte superior de la página de detalles del elemento.", + "EnableDetailsBanner": "Banner de detalles" } diff --git a/src/strings/es.json b/src/strings/es.json index 08c806f8cd..eedfa3d739 100644 --- a/src/strings/es.json +++ b/src/strings/es.json @@ -1,5 +1,5 @@ { - "AccessRestrictedTryAgainLater": "El acceso está restringido actualmente. Por favor, inténtalo más tarde.", + "AccessRestrictedTryAgainLater": "Actualmente el acceso está restringido. Por favor, inténtalo de nuevo más tarde.", "Add": "Añadir", "AddItemToCollectionHelp": "Agregue elementos a las colecciones buscándolos y haciendo clic con el botón derecho o tocando los menús para agregarlos a una colección.", "AddToCollection": "Añadir a la colección", @@ -310,7 +310,7 @@ "HeaderLatestMovies": "Últimas películas", "HeaderLatestMusic": "Última música", "HeaderLatestRecordings": "Últimas grabaciones", - "HeaderLibraries": "Blibliotecas", + "HeaderLibraries": "Bibliotecas", "HeaderLibraryAccess": "Acceso a la biblioteca", "HeaderLibraryFolders": "Carpetas de la biblioteca", "HeaderLibraryOrder": "Orden de la biblioteca", @@ -511,7 +511,7 @@ "LabelEmbedAlbumArtDidl": "Incorporar la carátula del álbum en didl", "LabelEmbedAlbumArtDidlHelp": "Algunos dispositivos prefieren este método para obtener la carátula del álbum. Otros pueden fallar al reproducir con esta opción habilitada.", "LabelEnableAutomaticPortMap": "Habilitar asignación de puertos automático", - "LabelEnableAutomaticPortMapHelp": "UPnP permite la configuración del router para acceso externo de forma fácil y automática. Esto puede no funcionar en algunos modelos de routers. Los cambios no se aplicarán hasta que el servidor sea reiniciado.", + "LabelEnableAutomaticPortMapHelp": "Reenvia automáticamente los puertos públicos de su Router a los puertos locales de su servidor a través de UPnP. Es posible que esto no funcione con algunos modelos de Routers o configuraciones de red. Los cambios no se aplicarán hasta después de reiniciar el servidor.", "LabelEnableBlastAliveMessages": "Explotar mensajes en vivo", "LabelEnableBlastAliveMessagesHelp": "Active aquí si el servidor no es detectado correctamente por otros dispositivos UPnP en su red.", "LabelEnableDlnaClientDiscoveryInterval": "Intervalo de detección de cliente (segundos)", @@ -957,7 +957,7 @@ "OptionMissingEpisode": "Episodios que faltan", "OptionMonday": "Lunes", "OptionNameSort": "Nombre", - "OptionNew": "Nuevo...", + "OptionNew": "Nuevo…", "OptionNone": "Nada", "OptionOnAppStartup": "Al iniciar la aplicación", "OptionOnInterval": "En un intervalo", @@ -1093,7 +1093,7 @@ "Share": "Compartir", "ShowAdvancedSettings": "Mostrar opciones avanzadas", "ShowIndicatorsFor": "Mostrar indicaciones para:", - "Shows": "Series", + "Shows": "Mostrar", "Shuffle": "Mezclar", "SimultaneousConnectionLimitHelp": "Número máximo de transmisiones simultáneas permitidas. Pon 0 para no tener límite.", "SkipEpisodesAlreadyInMyLibrary": "No grabar episodios que ya están en mi biblioteca", @@ -1228,13 +1228,13 @@ "Aired": "Emitido", "AnyLanguage": "Cualquier idioma", "Anytime": "En cualquier momento", - "AroundTime": "Aproximadamente {0}", + "AroundTime": "Aproximadamente", "Ascending": "Ascendente", "Audio": "Audio", "Auto": "Automático", "AutoBasedOnLanguageSetting": "Automático (basado en la configuración de idioma)", "Banner": "Pancarta", - "BurnSubtitlesHelp": "Determina si el servidor debe grabar subtítulos al transcodificar videos. Evitar esto mejorará altamente el rendimiento del servidor. Seleccione Auto para grabar formatos basados en imágenes (VOBSUB, PGS, SUB/IDX) y ciertos subtítulos ASS o SSA.", + "BurnSubtitlesHelp": "Determina si el servidor debe grabar los subtítulos en el vídeo al transcodificar. Desactivar esta opción puede mejorar el rendimiento. Seleccione 'Auto' para grabar formatos basados en imágenes (VOBSUB, PGS, SUB/IDX) y ciertos subtítulos ASS o SSA.", "ButtonInfo": "Información", "ChangingMetadataImageSettingsNewContent": "Los cambios a la configuración de descarga de etiquetas e imágenes sólo se aplicará al nuevo contenido que se añada a la biblioteca. Para aplicar los cambios a los elementos existentes necesitarás actualizar las etiquetas manualmente.", "ColorPrimaries": "Colores primarios", @@ -1248,7 +1248,7 @@ "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": "Dirección", + "Director": "Dirección de", "Directors": "Directores", "Display": "Mostrar", "DisplayInMyMedia": "Mostrar en la pantalla de inicio", @@ -1328,10 +1328,7 @@ "No": "No", "Normal": "Normal", "Off": "Apagado", - "Option2Player": "2+", "Option3D": "3D", - "Option3Player": "3+", - "Option4Player": "4+", "OptionActor": "Actor", "OptionAuto": "Automático", "OptionAutomatic": "Automático", @@ -1404,8 +1401,8 @@ "RunAtStartup": "Ejecutar al iniciar", "Series": "Series", "SeriesDisplayOrderHelp": "Ordena los episodios por fecha de emisión, orden de DVD o número absoluto.", - "ShowTitle": "Título del show", - "ShowYear": "Año del show", + "ShowTitle": "Mostrar título", + "ShowYear": "Mostrar año", "SmallCaps": "Letras minúsculas", "Smaller": "Más pequeño", "Sort": "Ordenar", @@ -1442,7 +1439,7 @@ "LabelPlayMethod": "Método de reproducción:", "LabelPlayer": "Reproductor:", "LabelFolder": "Carpeta:", - "LabelBaseUrlHelp": "Puede agregar un subdirectorio personalizado aquí para acceder al servidor desde una URL única.", + "LabelBaseUrlHelp": "Puede agregar aquí un subdirectorio personalizado para el acceso al servidor a través de una URL única.", "LabelBaseUrl": "URL base:", "LabelBitrate": "Bitrate:", "LabelAudioSampleRate": "Frecuencia de muestreo de audio:", @@ -1461,8 +1458,8 @@ "ButtonSplit": "Dividir", "HeaderNavigation": "Navegación", "MessageConfirmAppExit": "¿Quieres salir?", - "EnableFastImageFadeInHelp": "Las imágenes que hayan terminado de cargarse mostrarán una pequeña animación", - "EnableFastImageFadeIn": "Cargar las imágenes suavemente", + "EnableFasterAnimationsHelp": "Las animaciones y transiciones durarán menos tiempo", + "EnableFasterAnimations": "Animaciones más rápidas", "CopyStreamURLError": "Ha habido un error copiando la dirección.", "AllowFfmpegThrottlingHelp": "Cuando una transcodificación o un remux se adelanta lo suficiente desde la posición de reproducción actual, pause el proceso para que consuma menos recursos. Esto es más útil cuando se reproduce de forma linear, sin saltar de posición de reproducción a menudo. Desactívelo si experimenta problemas de reproducción.", "PlaybackErrorNoCompatibleStream": "Este contenido no es compatible con este dispositivo y no se puede reproducir: No se puede obtener del servidor en un formato compatible.", @@ -1499,6 +1496,69 @@ "Episode": "Episodio", "BoxSet": "Box Set", "Artist": "Artista", - "AlbumArtist": "Álbum de artista", - "Album": "Álbum" + "AlbumArtist": "Artista del álbum", + "Album": "Álbum", + "LabelDeinterlaceMethod": "Metodo de desentrelazar:", + "DeinterlaceMethodHelp": "Seleccione el método de desentrelazar para el transcodificar de contenido entrelazado.", + "LabelLibraryPageSize": "Tamaño de la página de la biblioteca:", + "LabelLibraryPageSizeHelp": "Establece la cantidad de artículos a mostrar en una página de la biblioteca. Ponlo en 0 para desactivar la paginación.", + "UnsupportedPlayback": "No es posible desencriptar contenido protegido mediante DRM; sin embargo se intentará su reproducción. Algunos archivos pueden aparecer completamente negros debido a encriptación u otras características no soportadas, como títulos interactivos.", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "MessageUnauthorizedUser": "No tiene autorización para acceder al servidor en este momento. Póngase en contacto con el administrador del servidor para obtener más información.", + "ButtonTogglePlaylist": "Lista de reproducción", + "ButtonToggleContextMenu": "Más", + "Filter": "Filtro", + "New": "Nuevo", + "HeaderFavoritePlaylists": "Lista reproducción favorita", + "ApiKeysCaption": "Lista de las claves API actuales", + "LabelNightly": "Nightly", + "LabelStable": "Estable", + "LabelChromecastVersion": "Versión de Chromecast", + "HeaderServerAddressSettings": "Configuración de la dirección del Servidor", + "HeaderRemoteAccessSettings": "Opciones de Acceso Remoto", + "HeaderHttpsSettings": "Opciones HTTPS", + "LabelRequireHttpsHelp": "Si se marca, el servidor redirigirá automáticamente todas las solicitudes de HTTP hacia HTTPS. Esto no tiene efecto si el servidor no está escuchando en HTTPS.", + "LabelRequireHttps": "Necesita HTTPS", + "LabelEnableHttpsHelp": "Permite que el servidor escuche en el puerto HTTPS configurado. También se debe configurar un certificado válido para que esto surta efecto.", + "LabelEnableHttps": "Activar HTTPS", + "TabDVR": "DVR", + "SaveChanges": "Guardar cambios", + "EnableBlurhash": "Mostrar una representación de las imágenes mientras cargan", + "EnableBlurhashHelp": "Aparecerá una representación de los colores de las imágenes antes de que terminen de cargar", + "HeaderDVR": "DVR", + "SyncPlayAccessHelp": "Selecciona los permisos de este usuario para utilizar SyncPlay. SyncPlay te permite sincronizar la reproducción entre varios dispositivos.", + "MessageSyncPlayErrorMedia": "¡No se pudo activar SyncPlay! Error de medio.", + "MessageSyncPlayErrorMissingSession": "¡No se pudo activar SyncPlay! Sesión desconectada.", + "MessageSyncPlayErrorNoActivePlayer": "No hay reproductor activo. SyncPlay ha sido desactivado.", + "MessageSyncPlayErrorAccessingGroups": "Ocurrió un error al acceder a la lista de grupos.", + "MessageSyncPlayLibraryAccessDenied": "Acceso restringido a este contenido.", + "MessageSyncPlayJoinGroupDenied": "Requiere permiso para usar SyncPlay.", + "MessageSyncPlayCreateGroupDenied": "Requiere permiso para crear un grupo.", + "MessageSyncPlayGroupDoesNotExist": "No se pudo unir al grupo porque no existe.", + "MessageSyncPlayPlaybackPermissionRequired": "Requiere permiso para reproducir.", + "MessageSyncPlayNoGroupsAvailable": "No hay grupos disponibles. Reproduce algo primero.", + "MessageSyncPlayGroupWait": "{0} se está cargando...", + "MessageSyncPlayUserLeft": "{0} abandonó el grupo.", + "MessageSyncPlayUserJoined": "{0} se ha unido al grupo.", + "MessageSyncPlayDisabled": "SyncPlay inactivo.", + "MessageSyncPlayEnabled": "SyncPlay activo.", + "LabelSyncPlayAccess": "Acceso a SyncPlay", + "LabelSyncPlayAccessNone": "Inactivo para este usuario", + "LabelSyncPlayAccessJoinGroups": "Permitir a usuarios unirse a grupos", + "LabelSyncPlayAccessCreateAndJoinGroups": "Permitir a usuarios crear y unirse a grupos", + "LabelSyncPlayLeaveGroupDescription": "Inhabilitar SyncPlay", + "LabelSyncPlayLeaveGroup": "Abandonar grupo", + "LabelSyncPlayNewGroupDescription": "Crear un nuevo grupo", + "LabelSyncPlayNewGroup": "Nuevo grupo", + "LabelSyncPlaySyncMethod": "Método de sincronización:", + "LabelSyncPlayPlaybackDiff": "Diferencia del tiempo de reproducción:", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Huso horario de el servidor:", + "HeaderSyncPlayEnabled": "Syncplay activo", + "HeaderSyncPlaySelectGroup": "Unirse a un grupo", + "EnableDetailsBannerHelp": "Mostrar imagen de banner en el tope de la página de detalles del elemento.", + "EnableDetailsBanner": "Barra de Detalles", + "ShowMore": "Mostrar más", + "ShowLess": "Mostrar menos" } diff --git a/src/strings/es_419.json b/src/strings/es_419.json new file mode 100644 index 0000000000..895c107e90 --- /dev/null +++ b/src/strings/es_419.json @@ -0,0 +1,1550 @@ +{ + "ValueSpecialEpisodeName": "Especial - {0}", + "Sync": "Sincronizar", + "Songs": "Canciones", + "Shows": "Programas", + "Playlists": "Listas de reproducción", + "Photos": "Fotos", + "Movies": "Películas", + "HeaderNextUp": "A continuación", + "HeaderLiveTV": "TV en vivo", + "HeaderFavoriteSongs": "Canciones favoritas", + "HeaderFavoriteShows": "Programas favoritos", + "HeaderFavoriteEpisodes": "Episodios favoritos", + "HeaderFavoriteArtists": "Artistas favoritos", + "HeaderFavoriteAlbums": "Álbumes favoritos", + "HeaderContinueWatching": "Continuar viendo", + "HeaderAlbumArtists": "Artistas del álbum", + "Genres": "Géneros", + "Folders": "Carpetas", + "Favorites": "Favoritos", + "Collections": "Colecciones", + "Channels": "Canales", + "Books": "Libros", + "Artists": "Artistas", + "Albums": "Álbumes", + "TabLatest": "Recientes", + "HeaderUser": "Usuario", + "AlbumArtist": "Artista del álbum", + "Album": "Álbum", + "Aired": "Transmitido", + "AirDate": "Fecha de emisión", + "AdditionalNotificationServices": "Explora el catálogo de complementos para instalar servicios de notificaciones adicionales.", + "AddedOnValue": "Agregado {0}", + "AddToPlaylist": "Agregar a lista de reproducción", + "AddToPlayQueue": "Agregar a la cola de reproducción", + "AddToCollection": "Agregar a colección", + "AddItemToCollectionHelp": "Agrega elementos a las colecciones buscándolos y utilizando sus menúes al hacer clic derecho o al tocarlos para agregarlos a una colección.", + "Add": "Agregar", + "Actor": "Actor", + "AccessRestrictedTryAgainLater": "El acceso está restringido actualmente. Por favor, inténtalo más tarde.", + "Absolute": "Absoluto", + "YadifBob": "YADIF Bob", + "Trailers": "Trailers", + "TabTrailers": "Trailers", + "ReleaseGroup": "Grupo que lo estrenó", + "OptionThumbCard": "Tarjeta miniatura", + "OptionResElement": "elemento res", + "OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)", + "OptionBluray": "Blu-ray", + "OptionBlockTrailers": "Trailers", + "LabelNightly": "Nocturno", + "HeaderVideos": "Videos", + "Director": "Director", + "Depressed": "Presionado", + "BoxSet": "Box Set", + "UnsupportedPlayback": "Jellyfin no puede desencriptar contenido protegido por DRM de todas formas todo el contenido será intentado, incluyendo los títulos protegidos. Algunos archivos pueden aparecer completamente en negro debido al encriptado o características no soportadas, como títulos interactivos.", + "OnApplicationStartup": "Cuando se inicia la aplicación", + "EveryXHours": "Cada {0} horas", + "EveryHour": "Cada hora", + "EveryXMinutes": "Cada {0} minutos", + "OnWakeFromSleep": "Al despertar de la suspensión", + "WeeklyAt": "{0}s a las {1}", + "DailyAt": "Diariamente a las {0}", + "LastSeen": "Ultima vez visto {0}", + "PersonRole": "como {0}", + "ListPaging": "{0}-{1} de {2}", + "WriteAccessRequired": "El servidor Jellyfin requiere permiso de escritura en esta carpeta. Por favor, asegúrate de tener acceso de escritura e inténtalo de nuevo.", + "PathNotFound": "No se pudo encontrar la ruta. Por favor, asegúrate de que la ruta es válida e inténtalo de nuevo.", + "Yesterday": "Ayer", + "Yes": "Sí", + "Yadif": "YADIF", + "XmlTvSportsCategoriesHelp": "Los programas con estas categorías serán mostrados como programas deportivos. Separa varios con un «|».", + "XmlTvPathHelp": "Una ruta a un archivo XMLTV. Jellyfin leerá este archivo y lo revisará periódicamente en busca de actualizaciones. Tú eres responsable de crear y actualizar el archivo.", + "XmlTvNewsCategoriesHelp": "Los programas con estas categorías serán mostrados como programas de noticias. Separa varios con un «|».", + "XmlTvMovieCategoriesHelp": "Los programas con estas categorías serán mostrados como películas. Separa varios con un «|».", + "XmlTvKidsCategoriesHelp": "Los programas con estas categorías serán mostrados como programas infantiles. Separa varios con un «|».", + "XmlDocumentAttributeListHelp": "Estos atributos se aplican al elemento raíz de cada respuesta XML.", + "Writer": "Escritor", + "WizardCompleted": "Eso es todo lo que necesitamos por ahora. Jellyfin ha comenzado a recolectar información sobre tu biblioteca de medios. Revisa alguna de nuestras aplicaciones, y luego haz clic en Finalizar para ver el Panel de control.", + "Whitelist": "Lista blanca", + "WelcomeToProject": "¡Bienvenido a Jellyfin!", + "Wednesday": "Miércoles", + "Watched": "Visto", + "ViewPlaybackInfo": "Ver información de reproducción", + "ViewArtist": "Ver artista", + "ViewAlbum": "Ver álbum", + "VideoRange": "Rango de video", + "Vertical": "Vertical", + "ValueVideoCodec": "Códec de video: {0}", + "ValueTimeLimitSingleHour": "Límite de tiempo: 1 hora", + "ValueTimeLimitMultiHour": "Límite de tiempo: {0} horas", + "ValueSongCount": "{0} canciones", + "ValueSeriesCount": "{0} series", + "ValueSeconds": "{0} segundos", + "ValueOneSong": "1 canción", + "ValueOneSeries": "1 serie", + "ValueOneMusicVideo": "1 video musical", + "ValueOneMovie": "1 película", + "ValueOneEpisode": "1 episodio", + "ValueOneAlbum": "1 álbum", + "ValueMusicVideoCount": "{0} videos musicales", + "ValueMovieCount": "{0} películas", + "ValueMinutes": "{0} min", + "ValueEpisodeCount": "{0} episodios", + "ValueDiscNumber": "Disco {0}", + "ValueContainer": "Contenedor: {0}", + "ValueConditions": "Condiciones: {0}", + "ValueCodec": "Códec: {0}", + "ValueAudioCodec": "Códec de audio: {0}", + "ValueAlbumCount": "{0} álbumes", + "UserProfilesIntro": "Jellyfin incluye soporte para perfiles de usuario con detalladas configuraciones de pantalla, estado de reproducción y controles parentales.", + "UserAgentHelp": "Proporciona un encabezado HTTP de agente de usuario personalizado.", + "Upload": "Subir", + "Up": "Arriba", + "Unrated": "Sin clasificar", + "Unplayed": "No reproducido", + "Unmute": "Activar sonido", + "UninstallPluginHeader": "Desinstalar complemento", + "UninstallPluginConfirmation": "¿Estás seguro de querer desinstalar {0}?", + "Uniform": "Uniforme", + "TvLibraryHelp": "Revisa la {0}guía de nombrado de series de TV{1}.", + "Tuesday": "Martes", + "Transcoding": "Transcodificando", + "TrackCount": "{0} pistas", + "Track": "Pista", + "TitlePlayback": "Reproducción", + "TitleHostingSettings": "Configuraciones de alojamiento", + "TitleHardwareAcceleration": "Aceleración por hardware", + "Thursday": "Jueves", + "Thumb": "Miniatura", + "ThisWizardWillGuideYou": "Este asistente te guiará a través del proceso de instalación. Para comenzar, por favor, selecciona tu idioma preferido.", + "TheseSettingsAffectSubtitlesOnThisDevice": "Estas configuraciones afectan a los subtítulos en este dispositivo", + "ThemeVideos": "Videos temáticos", + "ThemeSongs": "Canciones temáticas", + "TellUsAboutYourself": "Háblanos de ti", + "TagsValue": "Etiquetas: {0}", + "Tags": "Etiquetas", + "TabUsers": "Usuarios", + "TabUpcoming": "Próximamente", + "TabTranscoding": "Transcodificación", + "TabSuggestions": "Sugerencias", + "TabStreaming": "Transmisión", + "TabSongs": "Canciones", + "TabShows": "Programas", + "TabSettings": "Configuración", + "TabServer": "Servidor", + "TabSeries": "Series", + "TabScheduledTasks": "Tareas programadas", + "TabResumeSettings": "Reanudar", + "TabResponses": "Respuestas", + "TabRecordings": "Grabaciones", + "TabProfiles": "Perfiles", + "TabProfile": "Perfil", + "TabPlugins": "Complementos", + "TabPlaylists": "Listas de reproducción", + "TabPlaylist": "Lista de reproducción", + "TabPlayback": "Reproducción", + "TabPassword": "Contraseña", + "TabParentalControl": "Control parental", + "TabOther": "Otros", + "TabNotifications": "Notificaciones", + "TabNfoSettings": "Configuración de NFO", + "TabNetworking": "Redes", + "TabNetworks": "Cadenas", + "TabMyPlugins": "Mis complementos", + "TabMusicVideos": "Videos musicales", + "TabMusic": "Música", + "TabMovies": "Películas", + "TabMetadata": "Metadatos", + "TabLogs": "Registros", + "TabLiveTV": "TV en vivo", + "TabInfo": "Información", + "TabGuide": "Guía", + "TabGenres": "Géneros", + "TabFavorites": "Favoritos", + "TabEpisodes": "Episodios", + "TabDVR": "DVR", + "TabDisplay": "Mostrar", + "TabDirectPlay": "Reproducción directa", + "TabDevices": "Dispositivos", + "TabDashboard": "Panel de control", + "TabContainers": "Contenedores", + "TabCollections": "Colecciones", + "TabCodecs": "Códecs", + "TabChannels": "Canales", + "TabCatalog": "Catálogo", + "OptionPoster": "Póster", + "OptionPlayed": "Reproducido", + "OptionPlayCount": "Contador de reproducciones", + "OptionPlainVideoItemsHelp": "Si se habilita, todos los videos serán representados en DIDL como «object.item.videoItem» en lugar de un tipo más específico, como «object.item.videoItem.movie».", + "OptionPlainVideoItems": "Mostrar todos los videos como elementos de video simples", + "OptionPlainStorageFoldersHelp": "Si se habilita, todos las carpetas serán representadas en DIDL como «object.container.storageFolder» en lugar de un tipo más específico, como «object.container.person.musicArtist».", + "OptionPlainStorageFolders": "Mostrar todas las carpetas como carpetas de almacenamiento simples", + "OptionParentalRating": "Clasificación parental", + "OptionOnInterval": "En un intervalo", + "OptionOnAppStartup": "Al iniciar la aplicación", + "OptionNone": "Ninguno", + "OptionNew": "Nuevo…", + "OptionNameSort": "Nombre", + "OptionMonday": "Lunes", + "OptionMissingEpisode": "Episodios faltantes", + "OptionMax": "Máximo", + "OptionLoginAttemptsBeforeLockoutHelp": "Un valor de cero significa heredar el valor predeterminado de tres intentos para los usuarios normales y cinco para los administradores. Ajustar esto a -1 deshabilitará la función.", + "OptionLoginAttemptsBeforeLockout": "Determina cuantos intentos de inicio de sesión incorrectos se pueden hacer antes de que ocurra el bloqueo.", + "OptionList": "Lista", + "OptionLikes": "Me gusta", + "OptionIsSD": "SD", + "OptionIsHD": "HD", + "OptionImdbRating": "Calificación de IMDb", + "OptionIgnoreTranscodeByteRangeRequestsHelp": "Si se habilita, estas solicitudes serán honradas pero se ignorará el encabezado de rango de bytes.", + "OptionIgnoreTranscodeByteRangeRequests": "Ignorar solicitudes de transcodificación de rango de bytes", + "OptionHomeVideos": "Fotos", + "OptionHlsSegmentedSubtitles": "Subtítulos segmentados HLS", + "OptionHideUserFromLoginHelp": "Útil para cuentas privadas o de administrador ocultas. El usuario tendrá que iniciar sesión manualmente introduciendo su nombre de usuario y contraseña.", + "OptionHideUser": "Ocultar este usuario de las pantallas de inicio de sesión", + "OptionHasTrailer": "Trailer", + "OptionHasThemeVideo": "Video temático", + "OptionHasThemeSong": "Canción temática", + "OptionHasSubtitles": "Subtítulos", + "OptionHasSpecialFeatures": "Características especiales", + "OptionFriday": "Viernes", + "OptionFavorite": "Favoritos", + "OptionExtractChapterImage": "Habilitar la extracción de imágenes de los capítulos", + "OptionExternallyDownloaded": "Descarga externa", + "OptionEveryday": "Todos los días", + "OptionEstimateContentLength": "Estimar la duración del contenido cuando se transcodifica", + "OptionEquals": "Igual a", + "OptionEnded": "Finalizado", + "OptionEnableM2tsModeHelp": "Habilita el modo m2ts cuando se codifican mpegts.", + "OptionEnableM2tsMode": "Habilitar modo M2TS", + "OptionEnableForAllTuners": "Habilitar para todos los dispositivos sintonizadores", + "OptionEnableExternalContentInSuggestionsHelp": "Permitir que los trailers de Internet y los programas de televisión en vivo se incluyan en el contenido sugerido.", + "OptionEnableExternalContentInSuggestions": "Habilitar contenido externo en las sugerencias", + "OptionEnableAccessToAllLibraries": "Habilitar acceso a todas las bibliotecas", + "OptionEnableAccessToAllChannels": "Habilitar acceso a todos los canales", + "OptionEnableAccessFromAllDevices": "Habilitar acceso desde todos los dispositivos", + "OptionEmbedSubtitles": "Incrustar dentro del contenedor", + "OptionDvd": "DVD", + "OptionDownloadThumbImage": "Miniatura", + "OptionDownloadPrimaryImage": "Principal", + "OptionDownloadMenuImage": "Menú", + "OptionDownloadLogoImage": "Logo", + "OptionDownloadImagesInAdvanceHelp": "Por defecto, la mayoría de las imágenes solo son descargadas cuando son solicitadas por una aplicación Jellyfin. Habilita esta opción para descargar todas las imágenes por adelantado, a medida que se agreguen nuevos medios. Esto podría causar escaneos de bibliotecas significativamente más largos.", + "OptionDownloadImagesInAdvance": "Descargar las imágenes con antelación", + "OptionDownloadDiscImage": "Disco", + "OptionDownloadBoxImage": "Caja", + "OptionDownloadBannerImage": "Banner", + "OptionDownloadBackImage": "Parte trasera", + "OptionDownloadArtImage": "Arte", + "OptionDisplayFolderViewHelp": "Muestra las carpetas junto con sus otras bibliotecas de medios. Esto puede ser útil si deseas tener una vista simple de carpeta.", + "OptionDisplayFolderView": "Mostrar una vista de carpetas para mostrar las carpetas simples de los medios", + "OptionDislikes": "No me gusta", + "OptionDisableUserHelp": "Si se desactiva, el servidor no aceptará conexiones de este usuario. Las conexiones existentes serán finalizadas abruptamente.", + "OptionDisableUser": "Desactivar este usuario", + "OptionDescending": "Descendente", + "OptionDatePlayed": "Fecha de reproducción", + "OptionDateAddedImportTime": "Usar la fecha de escaneo en la biblioteca", + "OptionDateAddedFileTime": "Usar la fecha de creación del archivo", + "OptionDateAdded": "Fecha de adición", + "OptionDaily": "Diario", + "OptionCustomUsers": "Personalizado", + "OptionCriticRating": "Calificación de los críticos", + "OptionContinuing": "Continuando", + "OptionCommunityRating": "Calificación de la comunidad", + "OptionBlockTvShows": "Programas de TV", + "OptionBlockMusic": "Música", + "OptionBlockMovies": "Películas", + "OptionBlockLiveTvChannels": "Canales de TV en vivo", + "OptionBlockChannelContent": "Contenido de canales de Internet", + "OptionBlockBooks": "Libros", + "OptionBanner": "Banner", + "OptionAutomaticallyGroupSeriesHelp": "Si se habilita, las series que se reparten a través de múltiples carpetas dentro de esta biblioteca serán fusionadas en una sola serie.", + "OptionAutomaticallyGroupSeries": "Fusionar automáticamente series esparcidas a través de múltiples carpetas", + "OptionAutomatic": "Automático", + "OptionAuto": "Automático", + "OptionAscending": "Ascendente", + "OptionArtist": "Artista", + "OptionAllowVideoPlaybackTranscoding": "Permitir la reproducción de video que requiera de transcodificación", + "OptionAllowVideoPlaybackRemuxing": "Permitir reproducción de video que requiera conversión sin recodificar", + "OptionAllowUserToManageServer": "Permitir a este usuario administrar el servidor", + "OptionAllowSyncTranscoding": "Permitir descarga y sincronización de medios que requieran transcodificación", + "OptionAllowRemoteSharedDevicesHelp": "Los dispositivos DLNA se considerarán compartidos hasta que un usuario comience a controlarlos.", + "OptionAllowRemoteSharedDevices": "Permitir control remoto de dispositivos compartidos", + "OptionAllowRemoteControlOthers": "Permitir control remoto de otros usuarios", + "OptionAllowMediaPlaybackTranscodingHelp": "Restringir el acceso a la transcodificación podría causar fallas en la reproducción en las aplicaciones Jellyfin debido a los formatos de medios no soportados.", + "OptionAllowMediaPlayback": "Permitir reproducción de medios", + "OptionAllowManageLiveTv": "Permitir gestión de grabación de TV en vivo", + "OptionAllowLinkSharingHelp": "Solo son compartidas páginas web que contienen información sobre los medios. Los archivos de medios nunca son compartidos públicamente. Los compartidos tienen un límite de tiempo y expirarán después de {0} días.", + "OptionAllowLinkSharing": "Permitir compartir medios en redes sociales", + "OptionAllowContentDownloading": "Permitir descarga y sincronización de medios", + "OptionAllowBrowsingLiveTv": "Permitir acceso a TV en vivo", + "OptionForceRemoteSourceTranscoding": "Forzar transcodificación de fuentes remotas (como TV en vivo)", + "OptionAllowAudioPlaybackTranscoding": "Permitir la reproducción de audio que requiera transcodificación", + "OptionAllUsers": "Todos los usuarios", + "OptionAlbumArtist": "Artista del álbum", + "OptionAlbum": "Álbum", + "OptionAdminUsers": "Administradores", + "Option3D": "3D", + "OnlyImageFormats": "Solo formatos de imagen (VOBSUB, PGS, SUB)", + "OnlyForcedSubtitlesHelp": "Solo se cargarán subtítulos marcados como forzados.", + "OnlyForcedSubtitles": "Solo forzados", + "OneChannel": "Un canal", + "Off": "Apagar", + "NumLocationsValue": "{0} carpetas", + "Normal": "Normal", + "None": "Ninguno", + "NoSubtitlesHelp": "Los subtítulos no serán cargados por defecto. Pueden ser activados manualmente durante la reproducción.", + "NoSubtitles": "Ninguno", + "NoSubtitleSearchResultsFound": "No se encontraron resultados.", + "NoPluginConfigurationMessage": "Este complemento no tiene configuraciones disponibles.", + "NoNextUpItemsMessage": "No se encontró nada. ¡Comienza a ver tus programas!", + "NoNewDevicesFound": "No se encontraron nuevos dispositivos. Para agregar un sintonizador nuevo, cierra este diálogo e introduce la información del dispositivo manualmente.", + "NoCreatedLibraries": "Parece que no has creado ninguna biblioteca todavía. {0}¿Quisieras crear una ahora?{1}", + "No": "No", + "NextUp": "A continuación", + "Next": "Siguiente", + "News": "Noticias", + "NewEpisodesOnly": "Solo episodios nuevos", + "NewEpisodes": "Episodios nuevos", + "NewCollectionNameExample": "Ejemplo: Colección Guerra de las Galaxias", + "NewCollectionHelp": "Las colecciones te permiten disfrutar de agrupaciones personalizadas de películas y otros contenidos de la biblioteca.", + "NewCollection": "Nueva colección", + "Never": "Nunca", + "Name": "Nombre", + "MySubtitles": "Mis subtítulos", + "Mute": "Silenciar", + "MusicVideo": "Video musical", + "MusicLibraryHelp": "Revisar la {0}guía de nombrado de música{1}.", + "MusicArtist": "Artista musical", + "MusicAlbum": "Álbum de música", + "Movie": "Película", + "MovieLibraryHelp": "Revisar la {0}guía de nombrado de películas{1}.", + "MoveRight": "Mover a la derecha", + "MoveLeft": "Mover a la izquierda", + "MoreMediaInfo": "Información multimedia", + "MoreUsersCanBeAddedLater": "Más usuarios pueden ser añadidos más tarde desde el panel de control.", + "MoreFromValue": "Más de {0}", + "Monday": "Lunes", + "Mobile": "Móvil", + "MinutesBefore": "minutos antes", + "MinutesAfter": "minutos después", + "MetadataSettingChangeHelp": "Cambiar la configuración de los metadatos afectará al nuevo contenido que se añada en el futuro. Para actualizar el contenido existente, abre la pantalla de detalles y haz clic en el botón actualizar, o realiza actualizaciones masivas usando el administrador de metadatos.", + "MetadataManager": "Administrador de metadatos", + "Metadata": "Metadatos", + "MessageSyncPlayErrorMedia": "¡Fallo al activar SyncPlay! Error en el archivo de medios.", + "MessageSyncPlayErrorMissingSession": "¡Fallo al activar SyncPlay! Falta la sesión.", + "MessageSyncPlayErrorNoActivePlayer": "No se ha encontrado ningún reproductor activo. SyncPlay ha sido desactivado.", + "MessageSyncPlayErrorAccessingGroups": "Se produjo un error al acceder a la lista de grupos.", + "MessageSyncPlayLibraryAccessDenied": "El acceso a este contenido está restringido.", + "MessageSyncPlayJoinGroupDenied": "Permiso requerido para usar SyncPlay.", + "MessageSyncPlayCreateGroupDenied": "Permiso requerido para crear un grupo.", + "MessageSyncPlayGroupDoesNotExist": "Fallo al unirse al grupo porque éste no existe.", + "MessageSyncPlayPlaybackPermissionRequired": "Permiso de reproducción requerido.", + "MessageSyncPlayNoGroupsAvailable": "No hay grupos disponibles. Empieza a reproducir algo primero.", + "MessageSyncPlayGroupWait": "{0} está cargando...", + "MessageSyncPlayUserLeft": "{0} abandonó el grupo.", + "MessageSyncPlayUserJoined": "{0} se ha unido al grupo.", + "MessageSyncPlayDisabled": "SyncPlay deshabilitado.", + "MessageSyncPlayEnabled": "SyncPlay habilitado.", + "MessageYouHaveVersionInstalled": "Actualmente cuentas con la versión {0} instalada.", + "MessageUnsetContentHelp": "El contenido será mostrado como carpetas simples. Para mejores resultados utiliza el administrador de metadatos para establecer los tipos de contenido para las subcarpetas.", + "MessageUnableToConnectToServer": "No podemos conectarnos al servidor seleccionado en este momento. Por favor, asegúrate de que está funcionando e inténtalo de nuevo.", + "MessageTheFollowingLocationWillBeRemovedFromLibrary": "Las siguientes ubicaciones de medios se removerán de tu biblioteca:", + "MessageSettingsSaved": "Configuraciones guardadas.", + "MessageReenableUser": "Ver abajo para volver a habilitar", + "MessagePluginInstallDisclaimer": "Los complementos desarrollados por miembros de la comunidad Jellyfin son una gran forma de mejorar tu experiencia con Jellyfin con características y beneficios adicionales. Antes de instalar, por favor, conoce el impacto que pueden ocasionar en tu servidor Jellyfin, tales como escaneo más largo de bibliotecas, procesamiento en segundo plano adicional y reducción de la estabilidad del sistema.", + "MessagePluginConfigurationRequiresLocalAccess": "Para configurar este complemento por favor, inicia sesión en tu servidor local directamente.", + "MessagePleaseWait": "Por favor, espera. Esto podría tomar un minuto.", + "MessagePleaseEnsureInternetMetadata": "Por favor, asegúrate de que la descarga de metadatos de Internet está habilitada.", + "MessagePlayAccessRestricted": "La reproducción de este contenido está actualmente restringida. Por favor, contacta al administrador del servidor para obtener más información.", + "MessagePasswordResetForUsers": "Los siguientes usuarios han restablecido sus contraseñas. Ahora pueden iniciar sesión con los códigos PIN que se usaron para realizar el restablecimiento.", + "MessageNothingHere": "Nada aquí.", + "MessageNoTrailersFound": "No se encontraron trailers. Instala el canal de trailers para mejorar tu experiencia con películas al agregar una biblioteca de trailers desde Internet.", + "MessageNoServersAvailable": "No se encontraron servidores utilizando el descubrimiento automático de servidores.", + "MessageNoPluginsInstalled": "No tienes complementos instalados.", + "MessageNoMovieSuggestionsAvailable": "No hay sugerencias de películas disponibles en este momento. Comienza a ver y a calificar tus películas, y luego regresa para ver tus recomendaciones.", + "MessageNoCollectionsAvailable": "Las colecciones te permiten disfrutar de agrupaciones personalizadas de películas, series y álbumes. Haz clic en el botón + para comenzar a crear colecciones.", + "MessageNoAvailablePlugins": "No hay complementos disponibles.", + "MessageLeaveEmptyToInherit": "Dejar vacío para heredar la configuración de un elemento superior o del valor predeterminado global.", + "MessageItemsAdded": "Elementos agregados.", + "MessageItemSaved": "Elemento guardado.", + "MessageUnauthorizedUser": "No estás autorizado para acceder al servidor en este momento. Por favor, contacta al administrador del servidor para más información.", + "MessageInvalidUser": "Nombre de usuario o contraseña inválidos. Por favor, intenta de nuevo.", + "MessageInvalidForgotPasswordPin": "Se ha introducido un código PIN inválido o expirado. Por favor, inténtalo de nuevo.", + "MessageInstallPluginFromApp": "Este complemento debe ser instalado desde dentro de la aplicación en la que deseas usarlo.", + "MessageImageTypeNotSelected": "Por favor, selecciona un tipo de imagen del menú desplegable.", + "MessageImageFileTypeAllowed": "Solo son soportados archivos JPEG y PNG.", + "MessageForgotPasswordInNetworkRequired": "Por favor, intenta de nuevo dentro de tu red local para iniciar el proceso de restablecimiento de contraseña.", + "MessageForgotPasswordFileCreated": "El siguiente archivo fue creado en tu servidor y contiene instrucciones de como proceder:", + "MessageFileReadError": "Hubo un error al leer el archivo. Por favor, intenta de nuevo.", + "MessageEnablingOptionLongerScans": "Habilitar esta opción podría resultar en escaneos de bibliotecas significativamente más largos.", + "MessageDownloadQueued": "Descarga puesta en la cola.", + "MessageDirectoryPickerLinuxInstruction": "Para Linux en Arch Linux, CentOS, Debian, Fedora, openSUSE o Ubuntu, debes conceder al usuario del servicio al menos permisos de lectura a tus ubicaciones de almacenamiento.", + "MessageDirectoryPickerBSDInstruction": "Para BSD, quizás necesites configurar el almacenamiento dentro de tu «FreeNAS Jail» de manera que permita a Jellyfin accederlo.", + "List": "Lista", + "LinksValue": "Enlaces: {0}", + "Like": "Me gusta", + "LibraryAccessHelp": "Selecciona las bibliotecas que deseas compartir con este usuario. Los administradores podrán editar todas las carpetas utilizando el gestor de metadatos.", + "LeaveBlankToNotSetAPassword": "Puedes dejar este campo en blanco para no establecer ninguna contraseña.", + "LearnHowYouCanContribute": "Aprende cómo puedes contribuir.", + "LaunchWebAppOnStartupHelp": "Abre el cliente web en su navegador web predeterminado cuando se inicia el servidor. Esto no ocurrirá cuando se utilice la función de reinicio del servidor.", + "LaunchWebAppOnStartup": "Iniciar la interfaz web al iniciar el servidor", + "LatestFromLibrary": "Últimas - {0}", + "Large": "Grande", + "LanNetworksHelp": "Lista separada por comas de direcciones IP o entradas de IP/máscara de red para las redes que se considerarán en la red local al aplicar las restricciones de ancho de banda. Si se establecen, todas las demás direcciones IP se considerarán como parte de la red externa y estarán sujetas a las restricciones de ancho de banda externa. Si se deja en blanco, solo se considera a la subred del servidor estar en la red local.", + "LabelffmpegPathHelp": "La ruta hacia el archivo de la aplicación ffmpeg, o la carpeta que contenga ffmpeg.", + "LabelffmpegPath": "Ruta del FFmpeg:", + "LabelZipCode": "Código postal:", + "LabelYoureDone": "¡Has terminado!", + "LabelYourFirstName": "Tu nombre:", + "LabelYear": "Año:", + "LabelXDlnaDocHelp": "Determina el contenido del elemento X_DLNADOC en el namespace urn:schemas-dlna-org:device-1-0.", + "LabelXDlnaDoc": "Documento X-DLNA:", + "LabelXDlnaCapHelp": "Determina el contenido del elemento X_DLNACAP en el namespace urn:schemas-dlna-org:device-1-0.", + "LabelXDlnaCap": "X-DLNA límite:", + "LabelWeb": "Web:", + "LabelVideoResolution": "Resolución de video:", + "LabelVideoCodec": "Códec de video:", + "LabelVideoBitrate": "Velocidad de bits de video:", + "LabelVideo": "Video", + "DashboardArchitecture": "Arquitectura: {0}", + "DashboardOperatingSystem": "Sistema operativo: {0}", + "DashboardServerName": "Servidor: {0}", + "DashboardVersionNumber": "Versión: {0}", + "LabelVersionInstalled": "{0} instalado", + "LabelVersion": "Versión:", + "LabelValue": "Valor:", + "LabelVaapiDeviceHelp": "Este es el nodo de renderizado que es usado para la aceleración por hardware.", + "LabelVaapiDevice": "Dispositivo VA API:", + "LabelUsername": "Nombre de usuario:", + "LabelUserRemoteClientBitrateLimitHelp": "Sobrescribe el valor global predeterminado establecido en la configuración de reproducción del servidor.", + "LabelUserLoginAttemptsBeforeLockout": "Intentos fallidos de inicio de sesión antes de que el usuario sea bloqueado:", + "LabelUserLibraryHelp": "Selecciona la biblioteca de usuario que se mostrará en el dispositivo. Déjalo vacío para heredar la configuración por defecto.", + "LabelUserLibrary": "Biblioteca de usuario:", + "LabelUserAgent": "Agente de usuario:", + "LabelUser": "Usuario:", + "LabelUseNotificationServices": "Usar los siguientes servicios:", + "LabelTypeText": "Texto", + "LabelTypeMetadataDownloaders": "Recolectores de metadatos para {0}:", + "LabelType": "Tipo:", + "LabelTunerType": "Tipo de sintonizador:", + "LabelTunerIpAddress": "Dirección IP del sintonizador:", + "LabelTriggerType": "Tipo de disparador:", + "LabelTranscodingVideoCodec": "Códec de video:", + "LabelTranscodingThreadCountHelp": "Selecciona el número máximo de hilos a utilizar al transcodificar. Reducir el número de hilos reducirá el uso de la CPU, pero es posible que no se convierta lo suficientemente rápido como para que la experiencia de reproducción sea fluida.", + "LabelTranscodingThreadCount": "Conteo de hilos de la transcodificación:", + "LabelTranscodingProgress": "Progreso de la transcodificación:", + "LabelTranscodingFramerate": "Velocidad de cuadros de la transcodificación:", + "LabelTranscodes": "Transcodificaciones:", + "LabelTranscodingTempPathHelp": "Especifica una ruta personalizada para los archivos de transcodificación servidos a los clientes. Deja en blanco para utilizar el predeterminado del servidor.", + "LabelTranscodePath": "Ruta de transcodificación:", + "LabelTranscodingContainer": "Contenedor:", + "LabelTranscodingAudioCodec": "Códec de audio:", + "LabelTrackNumber": "Número de pista:", + "LabelTitle": "Título:", + "LabelTimeLimitHours": "Límite de tiempo (horas):", + "LabelTime": "Hora:", + "LabelTheme": "Tema:", + "LabelTextSize": "Tamaño del texto:", + "LabelTextColor": "Color del texto:", + "LabelTextBackgroundColor": "Color de fondo para el texto:", + "LabelTagline": "Eslogan:", + "LabelTag": "Etiqueta:", + "LabelTVHomeScreen": "Modo de pantalla de TV:", + "LabelSyncPlayAccess": "Acceso a SyncPlay", + "LabelSyncPlayAccessNone": "Deshabilitado para este usuario", + "LabelSyncPlayAccessJoinGroups": "Permitir al usuario unirse a grupos", + "LabelSyncPlayAccessCreateAndJoinGroups": "Permitir al usuario crear y unirse a grupos", + "LabelSyncPlayLeaveGroupDescription": "Deshabilitar SyncPlay", + "LabelSyncPlayLeaveGroup": "Abandonar grupo", + "LabelSyncPlayNewGroupDescription": "Crear un nuevo grupo", + "LabelSyncPlayNewGroup": "Nuevo grupo", + "LabelSyncPlaySyncMethod": "Método de sincronización:", + "LabelSyncPlayPlaybackDiff": "Diferencia de tiempo de reproducción:", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Tiempo compensado respecto al servidor:", + "LabelSupportedMediaTypes": "Tipos de medios soportados:", + "LabelSubtitles": "Subtítulos", + "LabelSubtitlePlaybackMode": "Modo de subtítulo:", + "LabelSubtitleFormatHelp": "Ejemplo: srt", + "LabelSubtitleDownloaders": "Recolectores de subtítulos:", + "LabelStreamType": "Tipo de transmisión:", + "LabelStopping": "Deteniendo", + "LabelStopWhenPossible": "Detener cuando sea posible:", + "LabelStatus": "Estado:", + "LabelStartWhenPossible": "Iniciar cuando sea posible:", + "LabelSportsCategories": "Categorías de deportes:", + "LabelSpecialSeasonsDisplayName": "Nombre de la temporada de especiales:", + "LabelSource": "Fuente:", + "LabelSoundEffects": "Efectos de sonido:", + "LabelSortTitle": "Título para ordenar:", + "LabelSortOrder": "Clasificar ordenado:", + "LabelSortBy": "Ordenar por:", + "LabelSonyAggregationFlagsHelp": "Determina el contenido del elemento aggregationFlags en el namespace urn:schemas-sonycom:av.", + "LabelSonyAggregationFlags": "Marcas de agregación Sony:", + "LabelSkipIfGraphicalSubsPresentHelp": "Mantener versiones de texto de subtítulos resultará en una entrega más eficiente y disminuirá las posibilidades de que un video sea transcodificado.", + "LabelSkipIfGraphicalSubsPresent": "Omitir si el video ya contiene subtítulos incrustados", + "LabelSkipIfAudioTrackPresentHelp": "Desactiva esto para asegurarte de que todos los videos tengan subtítulos, independientemente del idioma del audio.", + "LabelSkipIfAudioTrackPresent": "Omitir si la pista de audio por defecto coincide con el idioma de descarga", + "LabelSkipForwardLength": "Longitud de salto hacia adelante:", + "LabelSkipBackLength": "Longitud de salto hacia atrás:", + "Sunday": "Domingo", + "Suggestions": "Sugerencias", + "Subtitles": "Subtítulos", + "SubtitleOffset": "Desplazamiento de subtítulos", + "SubtitleDownloadersHelp": "Habilita y prioriza tus recolectores de subtítulos en orden de prioridad.", + "SubtitleAppearanceSettingsDisclaimer": "Estos ajustes no se aplicarán a los subtítulos gráficos (PGS, DVD, etc.) o a los subtítulos ASS/SSA que incorporen sus propios estilos.", + "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Estos ajustes también aplican a cualquier reproducción en Chromecast iniciada por este dispositivo.", + "Studios": "Estudios", + "StopRecording": "Detener grabación", + "Sports": "Deportes", + "SortName": "Nombre para ordenar", + "SortChannelsBy": "Ordenar canales por:", + "SortByValue": "Ordenar por {0}", + "Sort": "Ordenar", + "SmartSubtitlesHelp": "Los subtítulos que coincidan con el idioma preferido serán cargados cuando el audio se encuentre en un idioma extranjero.", + "Smart": "Inteligente", + "Smaller": "Más pequeño", + "SmallCaps": "Mayúsculas pequeñas", + "Small": "Pequeño", + "SkipEpisodesAlreadyInMyLibraryHelp": "Los episodios serán comparados usando el numero de temporada y de episodio, cuando estén disponibles.", + "SkipEpisodesAlreadyInMyLibrary": "No grabar episodios que ya se encuentran en mi biblioteca", + "SimultaneousConnectionLimitHelp": "El numero máximo de transmisiones simultáneas permitidas. Ingresa 0 para sin límite.", + "Filter": "Filtro", + "New": "Nuevo", + "Shuffle": "Aleatorio", + "ShowYear": "Mostrar año", + "ShowTitle": "Mostrar título", + "ShowIndicatorsFor": "Mostrar indicadores para:", + "ShowAdvancedSettings": "Mostrar configuraciones avanzadas", + "Share": "Compartir", + "SettingsWarning": "Cambiar estos valores podría causar inestabilidad o fallas de conexión. Si experimentas cualquier problema, recomendamos volver a los valores por defecto.", + "SettingsSaved": "Configuración guardada.", + "Settings": "Configuración", + "ServerUpdateNeeded": "Este servidor Jellyfin necesita ser actualizado. Para descargar la última versión, por favor, visita {0}", + "ServerRestartNeededAfterPluginInstall": "El servidor Jellyfin necesitará reiniciarse después de instalar un complemento.", + "ServerNameIsShuttingDown": "El servidor Jellyfin - {0} se está apagando.", + "ServerNameIsRestarting": "El servidor Jellyfin - {0} se está reiniciando.", + "SeriesYearToPresent": "{0} - Actualidad", + "SeriesSettings": "Configuración de la serie", + "SeriesRecordingScheduled": "Grabación de series programadas.", + "SeriesDisplayOrderHelp": "Ordenar los episodios por fecha de emisión, orden del DVD o numeración absoluta.", + "SeriesCancelled": "Serie cancelada.", + "Series": "Series", + "SendMessage": "Enviar mensaje", + "SelectAdminUsername": "Por favor, selecciona un nombre de usuario para la cuenta de administrador.", + "Season": "Temporada", + "SearchResults": "Resultados de la búsqueda", + "SearchForSubtitles": "Buscar subtítulos", + "SearchForMissingMetadata": "Buscar metadatos faltantes", + "SearchForCollectionInternetMetadata": "Buscar en Internet por ilustraciones y metadatos", + "Search": "Buscar", + "Screenshots": "Capturas de pantalla", + "Screenshot": "Captura de pantalla", + "Schedule": "Programación", + "ScanLibrary": "Escanear biblioteca", + "ScanForNewAndUpdatedFiles": "Escanear por archivos nuevos y actualizados", + "SaveSubtitlesIntoMediaFoldersHelp": "Almacenar los subtítulos junto a los archivos de video permitirá administrarlos con más facilidad.", + "SaveSubtitlesIntoMediaFolders": "Guardar subtítulos en las carpetas de los medios", + "SaveChanges": "Guardar cambios", + "Save": "Guardar", + "Saturday": "Sábado", + "Runtime": "Duración", + "RunAtStartup": "Ejecutar al iniciar", + "Rewind": "Rebobinar", + "ResumeAt": "Reanudar desde {0}", + "RestartPleaseWaitMessage": "Por favor, espera mientras el servidor Jellyfin se apaga y reinicia. Esto puede tomar un minuto o dos.", + "ReplaceExistingImages": "Reemplazar imágenes existentes", + "ReplaceAllMetadata": "Reemplazar todos los metadatos", + "RepeatOne": "Repetir uno", + "RepeatMode": "Modo de repetición", + "RepeatEpisodes": "Repetir episodios", + "RepeatAll": "Repetir todos", + "Repeat": "Repetir", + "RemoveFromPlaylist": "Remover de la lista de reproducción", + "RemoveFromCollection": "Remover de la colección", + "RememberMe": "Recuérdame", + "ReleaseDate": "Fecha de estreno", + "RefreshQueued": "Actualización puesta en la cola.", + "RefreshMetadata": "Actualizar metadatos", + "RefreshDialogHelp": "Los metadatos son actualizados basándose en las configuraciones y servicios de Internet que estén activados en el panel de control de tu servidor Jellyfin.", + "Refresh": "Actualizar", + "Recordings": "Grabaciones", + "RecordingScheduled": "Grabación programada.", + "RecordingPathChangeMessage": "Cambiar la carpeta de grabaciones no moverá las grabaciones existentes de la antigua ubicación a la nueva. Necesitan moverse manualmente si se desea.", + "RecordingCancelled": "Grabación cancelada.", + "RecordSeries": "Grabar series", + "Record": "Grabar", + "RecommendationStarring": "Protagonizado por {0}", + "RecommendationDirectedBy": "Dirigido por {0}", + "RecommendationBecauseYouWatched": "Porque viste {0}", + "RecommendationBecauseYouLike": "Porque te gusta {0}", + "RecentlyWatched": "Visto recientemente", + "Rate": "Calificación", + "Raised": "Elevado", + "QueueAllFromHere": "Encolar todos desde aquí", + "Quality": "Calidad", + "Programs": "Programas", + "ProductionLocations": "Lugares de producción", + "Producer": "Productor", + "Primary": "Principal", + "Previous": "Anterior", + "Premieres": "Estrenos", + "Premiere": "Estreno", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferir información del episodio incrustada a los nombres de archivo", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Esto utiliza la información del episodio desde los metadatos incrustados si están disponibles.", + "PreferEmbeddedTitlesOverFileNamesHelp": "Esto determina el título mostrado por defecto cuando no hay disponibles metadatos en Internet o localmente.", + "PreferEmbeddedTitlesOverFileNames": "Preferir títulos incrustados a los nombres de archivo", + "PluginInstalledMessage": "El complemento ha sido instalado con éxito. El servidor Jellyfin necesitará ser reiniciado para que los cambios surtan efecto.", + "PleaseSelectTwoItems": "Por favor, selecciona al menos dos elementos.", + "PleaseRestartServerName": "Por favor, reinicia el servidor Jellyfin - {0}.", + "PleaseEnterNameOrId": "Por favor, introduce un nombre o ID externo.", + "PleaseConfirmPluginInstallation": "Por favor, haz clic en OK para confirmar que has leído lo que se encuentra arriba y que deseas proceder con la instalación del complemento.", + "PleaseAddAtLeastOneFolder": "Por favor, agrega al menos una carpeta a esta biblioteca dando clic al botón Agregar.", + "Played": "Reproducido", + "PlaybackErrorNoCompatibleStream": "Este cliente no es compatible con los medios y el servidor no está enviando un formato de medios compatible.", + "PlayNextEpisodeAutomatically": "Reproducir el siguiente episodio automáticamente", + "PlayNext": "Reproducir siguiente", + "PlayFromBeginning": "Reproducir desde el inicio", + "PlayCount": "Cantidad de reproducciones", + "PlaybackData": "Datos de reproducción", + "PlayAllFromHere": "Reproducir todos desde aquí", + "Play": "Reproducir", + "PlaceFavoriteChannelsAtBeginning": "Colocar canales favoritos al inicio", + "PinCodeResetConfirmation": "¿Estás seguro de que quieres restablecer el código PIN?", + "PinCodeResetComplete": "El código PIN ha sido restablecido.", + "PictureInPicture": "Pantalla en pantalla", + "Person": "Persona", + "PerfectMatch": "Coincidencia perfecta", + "People": "Personas", + "PasswordSaved": "Contraseña guardada.", + "PasswordResetProviderHelp": "Elige un proveedor de restablecimiento de contraseña para usar cuando este usuario solicite un restablecimiento de contraseña", + "PasswordResetHeader": "Restablecer contraseña", + "PasswordResetConfirmation": "¿Estás seguro de querer restablecer la contraseña?", + "PasswordResetComplete": "La contraseña ha sido restablecida.", + "PasswordMatchError": "La contraseña y la confirmación de la contraseña deben coincidir.", + "ParentalRating": "Clasificación parental", + "PackageInstallFailed": "Instalación fallida de {0} (versión {1}).", + "PackageInstallCompleted": "Instalación completada de {0} (versión {1}).", + "PackageInstallCancelled": "Instalación cancelada de {0} (versión {1}).", + "Overview": "Resumen", + "OtherArtist": "Otro artista", + "OriginalAirDateValue": "Fecha de emisión original: {0}", + "OptionWeekly": "Semanal", + "OptionWeekends": "Fines de semana", + "OptionWeekdays": "Días de semana", + "OptionWednesday": "Miércoles", + "OptionWakeFromSleep": "Despertar de la suspensión", + "OptionUnplayed": "No reproducido", + "OptionUnairedEpisode": "Episodios no emitidos", + "OptionTvdbRating": "Calificación de TVDB", + "OptionTuesday": "Martes", + "OptionTrackName": "Nombre de la pista", + "OptionThumb": "Miniatura", + "OptionThursday": "Jueves", + "OptionSunday": "Domingo", + "OptionSubstring": "Subcadena", + "OptionSpecialEpisode": "Especiales", + "OptionSaveMetadataAsHiddenHelp": "Cambiar esto se aplicará a los nuevos metadatos guardados en el futuro. Los archivos de metadatos existentes serán actualizados la próxima vez que sean guardados por el servidor Jellyfin.", + "OptionSaveMetadataAsHidden": "Guardar metadatos e imágenes como archivos ocultos", + "OptionSaturday": "Sábado", + "OptionRuntime": "Duración", + "OptionResumable": "Reanudable", + "OptionRequirePerfectSubtitleMatchHelp": "Solicitar una coincidencia perfecta filtrará los subtítulos para incluir solo aquellos que han sido probados y verificados exactamente con tu archivo de video. Desmarcar esta opción incrementará las probabilidades de que se descarguen subtítulos, pero incrementará las posibilidades de obtener subtítulos mal sincronizados o con texto incorrecto.", + "OptionRequirePerfectSubtitleMatch": "Solo descargar subtítulos que coincidan perfectamente con mis archivos de video", + "OptionReportByteRangeSeekingWhenTranscodingHelp": "Esto es requerido para algunos dispositivos que no pueden hacer búsquedas de tiempo muy bien.", + "OptionReportByteRangeSeekingWhenTranscoding": "Reportar que el servidor soporta la búsqueda de bytes cuando se transcodifica", + "OptionReleaseDate": "Fecha de estreno", + "OptionRegex": "Expresión regular", + "OptionRandom": "Aleatorio", + "OptionProtocolHttp": "HTTP", + "OptionProtocolHls": "Transmisión en vivo por HTTP", + "OptionProfileVideoAudio": "Audio del video", + "OptionProfileVideo": "Video", + "OptionProfilePhoto": "Foto", + "OptionProfileAudio": "Audio", + "OptionPremiereDate": "Fecha de estreno", + "OptionPosterCard": "Ficha de póster", + "LabelSkin": "Apariencia:", + "LabelSize": "Tamaño:", + "LabelSimultaneousConnectionLimit": "Límite de transmisiones simultáneas:", + "LabelServerName": "Nombre del servidor:", + "LabelServerHostHelp": "192.168.1.100:8096 o https://miservidor.com", + "LabelServerHost": "Servidor:", + "LabelSeriesRecordingPath": "Ruta para las grabaciones de series (opcional):", + "LabelSerialNumber": "Número de serie", + "LabelSendNotificationToUsers": "Enviar la notificación a:", + "LabelSelectVersionToInstall": "Seleccionar versión a instalar:", + "LabelSelectUsers": "Seleccionar usuarios:", + "LabelSelectFolderGroupsHelp": "Las carpetas sin marcar serán mostradas individualmente en su propia vista.", + "LabelSelectFolderGroups": "Agrupar automáticamente el contenido de las siguientes carpetas a vistas como Películas, Música y TV:", + "LabelSeasonNumber": "Temporada número:", + "EnableFastImageFadeInHelp": "Habilita una animación más rápida de desvanecimiento para las imágenes cargadas.", + "EnableFastImageFadeIn": "Desvanecimiento rápido de animaciones", + "LabelScreensaver": "Protector de pantalla:", + "LabelScheduledTaskLastRan": "Última ejecución {0}, tomando {1}.", + "LabelSaveLocalMetadataHelp": "Guardar ilustraciones en las carpetas de los medios los colocará en un lugar donde se pueden editar fácilmente.", + "LabelSaveLocalMetadata": "Guardar las ilustraciones en las carpetas de los medios", + "LabelRuntimeMinutes": "Duración (minutos):", + "LabelRequireHttpsHelp": "Si se marca, el servidor redirigirá automáticamente todas las solicitudes a través de HTTP a HTTPS. Esto no tiene efecto si el servidor no está escuchando en HTTPS.", + "LabelRequireHttps": "Requerir HTTPS", + "LabelRemoteClientBitrateLimitHelp": "Un límite opcional de velocidad de bits por transmisión para todos los dispositivos fuera de la red. Esto es útil para evitar que los dispositivos soliciten una tasa de bits más alta de la que puede manejar tu conexión a Internet. Esto puede provocar un aumento de la carga de la CPU en el servidor para transcodificar los videos sobre la marcha a una velocidad de bits inferior.", + "LabelRemoteClientBitrateLimit": "Limite de velocidad de bits para la transmisión por Internet (Mbps):", + "LabelReleaseDate": "Fecha de estreno:", + "LabelRefreshMode": "Modo de actualización:", + "LabelRecordingPathHelp": "Especifica la ubicación por defecto para guardar las grabaciones. Si se deja en blanco, se usará la carpeta de datos de programa del servidor.", + "LabelRecordingPath": "Ruta por defecto para las grabaciones:", + "LabelRecord": "Grabar:", + "LabelReasonForTranscoding": "Motivo para transcodificar:", + "LabelReadHowYouCanContribute": "Aprende cómo puedes contribuir.", + "LabelPublicHttpsPortHelp": "El número de puerto público que debe asignarse al puerto HTTPS local.", + "LabelPublicHttpsPort": "Número de puerto HTTPS público:", + "LabelPublicHttpPortHelp": "El número de puerto público que debe asignarse al puerto HTTP local.", + "LabelPublicHttpPort": "Número de puerto HTTP público:", + "LabelProtocolInfoHelp": "El valor que será utilizado cuando se responda a solicitudes GetProtocolInfo desde el dispositivo.", + "LabelProtocolInfo": "Información del protocolo:", + "LabelProtocol": "Protocolo:", + "LabelProfileVideoCodecs": "Códecs de video:", + "LabelProfileContainersHelp": "Separados por comas. Puede dejarse vacío para aplicarlo a todos los contenedores.", + "LabelProfileContainer": "Contenedor:", + "LabelProfileCodecsHelp": "Separados por comas. Puede dejarse vacío para aplicarlo a todos los códecs.", + "LabelProfileCodecs": "Códecs:", + "LabelProfileAudioCodecs": "Códecs de audio:", + "LabelPrevious": "Anterior", + "LabelPreferredSubtitleLanguage": "Idioma preferido para los subtítulos:", + "LabelPreferredDisplayLanguageHelp": "La traducción de Jellyfin es un proyecto en curso.", + "LabelPreferredDisplayLanguage": "Idioma de pantalla preferido:", + "LabelPostProcessorArgumentsHelp": "Usar {path} como la ruta del archivo grabado.", + "LabelPostProcessorArguments": "Argumentos de la línea de comandos del post-procesador:", + "LabelPostProcessor": "Aplicación de postprocesamiento:", + "LabelPleaseRestart": "Los cambios tendrán efecto después de recargar manualmente el cliente web.", + "LabelPlayMethod": "Método de reproducción:", + "LabelPlaylist": "Lista de reproducción:", + "LabelPlayerDimensions": "Dimensiones del reproductor:", + "LabelPlayer": "Reproductor:", + "LabelPlayDefaultAudioTrack": "Reproducir la pista de audio por defecto independientemente del idioma", + "LabelPlaceOfBirth": "Lugar de nacimiento:", + "LabelPersonRoleHelp": "Ejemplo: Conductor de camión de helados", + "LabelPersonRole": "Rol:", + "LabelPath": "Ruta:", + "LabelPasswordRecoveryPinCode": "Código PIN:", + "LabelPasswordResetProvider": "Proveedor de restablecimiento de contraseña:", + "LabelPasswordConfirm": "Contraseña (confirmar):", + "LabelPassword": "Contraseña:", + "LabelParentalRating": "Clasificación parental:", + "LabelParentNumber": "Número antecesor:", + "LabelOverview": "Resumen:", + "LabelOriginalTitle": "Título original:", + "LabelOriginalAspectRatio": "Relación de aspecto original:", + "LabelOptionalNetworkPathHelp": "Si esta carpeta es compartida en su red, proveer la ruta del recurso compartido de red puede permitir a las aplicaciones Jellyfin en otros dispositivos acceder a los archivos de medios directamente. Por ejemplo, {0} o {1}.", + "LabelOptionalNetworkPath": "(Opcional) Carpeta de red compartida:", + "LabelNumberOfGuideDaysHelp": "Descargar más días de datos de programación permite programar con mayor anticipación y ver más listados, pero tomará más tiempo en descargar. Auto hará la selección basada en el número de canales.", + "LabelNumberOfGuideDays": "Número de días de datos de la programación a descargar:", + "LabelNumber": "Número:", + "LabelNotificationEnabled": "Habilitar esta notificación", + "LabelNext": "Siguiente", + "LabelNewsCategories": "Categorías de noticias:", + "LabelNewPasswordConfirm": "Confirmación de contraseña nueva:", + "LabelNewPassword": "Contraseña nueva:", + "LabelNewName": "Nuevo nombre:", + "LabelStable": "Estable", + "LabelChromecastVersion": "Versión de Chromecast", + "LabelName": "Nombre:", + "LabelMusicStreamingTranscodingBitrateHelp": "Especifica la velocidad de bits máxima al transmitir música.", + "LabelMusicStreamingTranscodingBitrate": "Velocidad de bits de transcodificación de música:", + "LabelMovieRecordingPath": "Ruta para las grabaciones de películas (opcional):", + "LabelMoviePrefixHelp": "Si un prefijo es aplicado al título de las películas, introdúcelo aquí para que el servidor pueda manejarlo correctamente.", + "LabelMoviePrefix": "Prefijo de la película:", + "LabelMovieCategories": "Categorías de películas:", + "LabelMonitorUsers": "Monitorear actividad desde:", + "LabelModelUrl": "URL del modelo", + "LabelModelNumber": "Número del modelo", + "LabelModelName": "Nombre del modelo", + "LabelModelDescription": "Descripción del modelo", + "LabelMinScreenshotDownloadWidth": "Anchura mínima de descarga de capturas de pantalla:", + "LabelMinResumePercentageHelp": "Los medios se asumen como no reproducidos si se detienen antes de este tiempo.", + "LabelMinResumePercentage": "Porcentaje mínimo para reanudar:", + "LabelMinResumeDurationHelp": "La duración de video más corta en segundos que guardará la ubicación de reproducción y te permitirá reanudarla.", + "LabelMinResumeDuration": "Duración mínima para la reanudación:", + "LabelMinBackdropDownloadWidth": "Anchura mínima de descarga de imágenes de fondo:", + "LabelMethod": "Método:", + "LabelMetadataSaversHelp": "Selecciona los formatos de archivo con los que se guardarán tus metadatos.", + "LabelMetadataSavers": "Grabadores de metadatos:", + "LabelMetadataReadersHelp": "Ordena tus fuentes de metadatos locales por prioridad. El primer archivo encontrado será leído.", + "LabelMetadataReaders": "Lectores de metadatos:", + "LabelMetadataPathHelp": "Especifique una ubicación personalizada para las ilustraciones y los metadatos descargados.", + "LabelKidsCategories": "Categorías infantiles:", + "LabelKeepUpTo": "Mantener hasta:", + "LabelInternetQuality": "Calidad en Internet:", + "LabelInNetworkSignInWithEasyPasswordHelp": "Utiliza el código PIN sencillo para acceder a los clientes en tu red local. Tu contraseña regular solo se necesitará fuera de casa. Si el código PIN se deja en blanco, no necesitarás una contraseña dentro de tu red local.", + "LabelInNetworkSignInWithEasyPassword": "Habilitar inicio de sesión con mi código PIN sencillo para conexiones dentro de la red", + "LabelImportOnlyFavoriteChannels": "Restringir a canales marcados como favoritos", + "LabelImageType": "Tipo de imagen:", + "LabelImageFetchersHelp": "Habilita y prioriza tus recolectores de imágenes preferidos.", + "LabelIdentificationFieldHelp": "Una subcadena indiferente a las mayúsculas y minúsculas o una expresión regular (regex).", + "LabelIconMaxWidthHelp": "Resolución máxima de los íconos expuestos vía upnp:icon.", + "LabelIconMaxWidth": "Ancho máximo del ícono:", + "LabelIconMaxHeightHelp": "Resolución máxima de los íconos expuestos vía upnp:icon.", + "LabelIconMaxHeight": "Altura máxima del ícono:", + "LabelHttpsPortHelp": "El número de puerto TCP al que el servidor HTTPS de Jellyfin debería enlazar.", + "LabelHttpsPort": "Número de puerto local HTTPS:", + "LabelHomeScreenSectionValue": "Sección {0} de la pantalla de inicio:", + "LabelHomeNetworkQuality": "Calidad en red local:", + "LabelHardwareAccelerationTypeHelp": "La aceleración por hardware requiere configuración adicional.", + "LabelHardwareAccelerationType": "Aceleración por hardware:", + "LabelEncoderPreset": "Codificación H264 y H265 preestablecida:", + "LabelH264Crf": "CRF de codificación H264:", + "LabelGroupMoviesIntoCollectionsHelp": "Cuando se muestran listados de películas, las películas que pertenecen a una colección serán mostradas agrupadas en un solo artículo.", + "LabelGroupMoviesIntoCollections": "Agrupar películas en colecciones", + "LabelServerNameHelp": "Este nombre se usará para identificar el servidor y se predeterminará al nombre de la computadora del servidor.", + "LabelFriendlyName": "Nombre amistoso:", + "LabelFormat": "Formato:", + "LabelForgotPasswordUsernameHelp": "Introduce tu nombre de usuario, si lo recuerdas.", + "LabelFont": "Fuente:", + "LabelFolder": "Carpeta:", + "LabelFinish": "Terminar", + "LabelFileOrUrl": "Archivo o URL:", + "LabelFailed": "Fallido", + "LabelExtractChaptersDuringLibraryScanHelp": "Genera imágenes de los capítulos cuando se importan videos durante el escaneo de la biblioteca. De lo contrario, se extraerán durante la tarea programada imágenes de capítulos, lo que permitirá que el escaneado normal de la biblioteca se complete más rápidamente.", + "LabelExtractChaptersDuringLibraryScan": "Extraer las imágenes de los capítulos durante el escaneo de la biblioteca", + "LabelBaseUrlHelp": "Añade un subdirectorio personalizado a la URL del servidor. Por ejemplo: http://ejemplo.com/<urlbase>", + "LabelBaseUrl": "URL base:", + "LabelEveryXMinutes": "Cada:", + "LabelEvent": "Evento:", + "LabelEpisodeNumber": "Episodio número:", + "LabelEndDate": "Fecha de fin:", + "LabelEnableSingleImageInDidlLimitHelp": "Algunos dispositivos no se renderizarán correctamente si se incrustan varias imágenes en DIDL.", + "LabelEnableSingleImageInDidlLimit": "Limitar a una sola imagen incrustada", + "LabelEnableRealtimeMonitorHelp": "Los cambios en los archivos serán procesados inmediatamente, en los sistemas de archivo soportados.", + "LabelEnableRealtimeMonitor": "Activar monitoreo en tiempo real", + "LabelEnableHttpsHelp": "Permite al servidor escuchar en el puerto HTTPS configurado. Un certificado válido también debe ser configurado para que esto tenga efecto.", + "LabelEnableHttps": "Habilitar HTTPS", + "LabelEnableHardwareDecodingFor": "Habilitar decodificación por hardware para:", + "LabelEnableDlnaServerHelp": "Permite a dispositivos UPnP en tu red explorar y reproducir contenido.", + "LabelEnableDlnaServer": "Habilitar servidor DLNA", + "LabelEnableDlnaPlayToHelp": "Detecta dispositivos dentro de tu red y ofrece la capacidad de controlarlos remotamente.", + "LabelEnableDlnaPlayTo": "Habilitar Reproducir En mediante DLNA", + "LabelEnableDlnaDebugLoggingHelp": "Crea grandes archivos de registro y solo se debe usar cuando se requiera para solucionar problemas.", + "LabelEnableDlnaDebugLogging": "Habilitar el registro de depuración de DLNA", + "LabelEnableDlnaClientDiscoveryIntervalHelp": "Determina la duración en segundos entre búsquedas SSDP realizadas por Jellyfin.", + "LabelEnableDlnaClientDiscoveryInterval": "Intervalo de descubrimiento de clientes (segundos)", + "LabelEnableBlastAliveMessagesHelp": "Habilita esto si el servidor no es detectado de manera confiable por otros dispositivos UPnP en tu red.", + "LabelEnableBlastAliveMessages": "Bombardeo de mensajes de vida", + "LabelEnableAutomaticPortMapHelp": "Redirecciona automáticamente los puertos públicos de tu router a los puertos locales de tu servidor a través de UPnP. Esto puede no funcionar con algunos modelos de routers o configuraciones de red. Los cambios no se aplicarán hasta después de reiniciar el servidor.", + "LabelEnableAutomaticPortMap": "Habilitar mapeo automático de puertos", + "LabelEmbedAlbumArtDidlHelp": "Algunos dispositivos prefieren este método para obtener arte de álbumes. Otros podrían fallar al reproducir con esta opción habilitada.", + "LabelEmbedAlbumArtDidl": "Incrustar arte del álbum en DIDL", + "LabelEasyPinCode": "Código PIN sencillo:", + "LabelDynamicExternalId": "{0} Id:", + "LabelDropShadow": "Mostrar sombra:", + "LabelDroppedFrames": "Cuadros saltados:", + "LabelDropImageHere": "Arrastre la imagen aquí o haz para explorar.", + "LabelDownloadLanguages": "Descargar idiomas:", + "LabelDownMixAudioScaleHelp": "Incrementa el audio cuando se hace downmix. Un valor de 1 preservará el volumen original.", + "LabelDownMixAudioScale": "Incremento del audio cuando se hace downmix:", + "LabelDisplaySpecialsWithinSeasons": "Mostrar especiales dentro de las temporadas en las que fueron transmitidas", + "LabelDisplayOrder": "Orden para mostrar:", + "LabelDisplayName": "Nombre a mostrar:", + "LabelDisplayMode": "Modo de pantalla:", + "LabelDisplayMissingEpisodesWithinSeasons": "Mostrar episodios faltantes en las temporadas", + "LabelDisplayLanguageHelp": "La traducción de Jellyfin es un proyecto en curso.", + "LabelDisplayLanguage": "Idioma de pantalla:", + "LabelDiscNumber": "Número de disco:", + "LabelDidlMode": "Modo DIDL:", + "LabelDeviceDescription": "Descripción del dispositivo", + "LabelDeinterlaceMethod": "Método de desentrelazado:", + "LabelDefaultUserHelp": "Determina qué biblioteca de usuario será mostrada en los dispositivos conectados. Esto puede ser reemplazado para cada dispositivo empleando perfiles.", + "LabelDefaultUser": "Usuario por defecto:", + "LabelDefaultScreen": "Pantalla por defecto:", + "LabelDeathDate": "Fecha de defunción:", + "LabelDay": "Día:", + "LabelDateTimeLocale": "Configuración regional de fecha y hora:", + "LabelDateAddedBehaviorHelp": "Si un valor de metadatos está presente, siempre se utilizará antes de cualquiera de estas opciones.", + "LabelDateAddedBehavior": "Comportamiento de la fecha de adición para nuevo contenido:", + "LabelDateAdded": "Fecha de adición:", + "LabelDashboardTheme": "Tema del panel de control del servidor:", + "LabelCustomRating": "Calificación personalizada:", + "LabelCustomDeviceDisplayNameHelp": "Proporcione un nombre personalizado para mostrar o déjalo vacío para usar el nombre reportado por el dispositivo.", + "LabelCustomDeviceDisplayName": "Nombre a mostrar:", + "LabelCustomCssHelp": "Aplica tu propio estilo personalizado a la interfaz web.", + "LabelCustomCss": "CSS personalizado:", + "LabelCustomCertificatePathHelp": "Ruta a un archivo PKCS #12 que contiene un certificado y una clave privada para habilitar el soporte TLS en un dominio personalizado.", + "LabelCustomCertificatePath": "Ruta del certificado SSL personalizado:", + "LabelCurrentPassword": "Contraseña actual:", + "LabelCriticRating": "Calificación de los críticos:", + "LabelCountry": "País:", + "LabelCorruptedFrames": "Cuadros corruptos:", + "LabelContentType": "Tipo de contenido:", + "LabelCommunityRating": "Calificación de la comunidad:", + "LabelCollection": "Colección:", + "LabelChannels": "Canales:", + "LabelCertificatePasswordHelp": "Si tu certificado requiere una contraseña, por favor, introdúcela aquí.", + "LabelCertificatePassword": "Contraseña del certificado:", + "TabArtists": "Artistas", + "TabAlbums": "Álbumes", + "TabAlbumArtists": "Artistas del álbum", + "TabAdvanced": "Avanzado", + "TabAccess": "Acceso", + "TV": "TV", + "SystemDlnaProfilesHelp": "Los perfiles del sistema son de solo lectura. Los cambios a un perfil del sistema serán guardados en un nuevo perfil personalizado.", + "SyncPlayAccessHelp": "Selecciona el nivel de acceso que este usuario tiene a la función SyncPlay. SyncPlay permite sincronizar la reproducción con otros dispositivos.", + "MessageDeleteTaskTrigger": "¿Estás seguro de querer eliminar este disparador de tarea?", + "MessageCreateAccountAt": "Crear una cuenta en {0}", + "MessageContactAdminToResetPassword": "Por favor, contacta a tu administrador para restablecer tu contraseña.", + "MessageConfirmShutdown": "¿Estás seguro de que deseas apagar el servidor?", + "MessageConfirmRevokeApiKey": "¿Estás seguro de querer revocar esta clave API? La conexión de la aplicación con el servidor Jellyfin será terminada abruptamente.", + "MessageConfirmRestart": "¿Estás seguro de que deseas reiniciar el servidor Jellyfin?", + "MessageConfirmRemoveMediaLocation": "¿Estás seguro de querer remover esta ubicación?", + "MessageConfirmRecordingCancellation": "¿Cancelar grabación?", + "MessageConfirmProfileDeletion": "¿Estás seguro de querer eliminar este perfil?", + "MessageConfirmDeleteTunerDevice": "¿Estás seguro de querer eliminar este dispositivo?", + "MessageConfirmDeleteGuideProvider": "¿Estás seguro de querer eliminar este proveedor de guía?", + "MessageConfirmAppExit": "¿Deseas salir?", + "MessageAreYouSureYouWishToRemoveMediaFolder": "¿Estás seguro de querer remover esta carpeta de medios?", + "MessageAreYouSureDeleteSubtitles": "¿Estás seguro de querer eliminar este subtítulo?", + "MessageAlreadyInstalled": "Esta versión ya se encuentra instalada.", + "Menu": "Menú", + "MediaIsBeingConverted": "Los medios están siendo convertidos a un formato compatible con el dispositivo que está reproduciendo el medio.", + "MediaInfoStreamTypeVideo": "Video", + "MediaInfoStreamTypeSubtitle": "Subtítulo", + "MediaInfoStreamTypeEmbeddedImage": "Imagen incrustada", + "MediaInfoStreamTypeData": "Dato", + "MediaInfoStreamTypeAudio": "Audio", + "MediaInfoSoftware": "Software", + "MediaInfoTimestamp": "Fecha y hora", + "MediaInfoSize": "Tamaño", + "MediaInfoSampleRate": "Tasa de muestreo", + "MediaInfoResolution": "Resolución", + "MediaInfoRefFrames": "Tramas de referencia", + "MediaInfoProfile": "Perfil", + "MediaInfoPixelFormat": "Formato de pixel", + "MediaInfoPath": "Ruta", + "MediaInfoLevel": "Nivel", + "MediaInfoLayout": "Esquema", + "MediaInfoLanguage": "Idioma", + "MediaInfoInterlaced": "Entrelazado", + "MediaInfoFramerate": "Velocidad de cuadros", + "MediaInfoForced": "Forzado", + "MediaInfoExternal": "Externo", + "MediaInfoDefault": "Por defecto", + "MediaInfoContainer": "Contenedor", + "MediaInfoCodecTag": "Etiqueta de códec", + "MediaInfoCodec": "Códec", + "MediaInfoChannels": "Canales", + "MediaInfoBitrate": "Velocidad de bits", + "MediaInfoBitDepth": "Profundidad de bit", + "MediaInfoAspectRatio": "Relación de aspecto", + "MediaInfoAnamorphic": "Anamórfico", + "MaxParentalRatingHelp": "El contenido con una calificación más alta será ocultado a este usuario.", + "MarkUnplayed": "Marcar como no reproducido", + "MarkPlayed": "Marcar como reproducido", + "MapChannels": "Mapear canales", + "ManageRecording": "Administrar grabación", + "ManageLibrary": "Administrar biblioteca", + "Logo": "Logo", + "LiveTV": "TV en vivo", + "LiveBroadcasts": "Emisiones en vivo", + "Live": "En vivo", + "LabelCancelled": "Cancelado", + "LabelCachePathHelp": "Especifica una ubicación personalizada para los archivos caché del servidor como las imágenes. Dejar en blanco para utilizar la configuración por defecto del servidor.", + "LabelCachePath": "Ruta de la caché:", + "LabelCache": "Caché:", + "LabelBurnSubtitles": "Quemar subtítulos:", + "LabelBlockContentWithTags": "Bloquear elementos con las etiquetas:", + "LabelBlastMessageIntervalHelp": "Determina la duración en segundos del intervalo entre mensajes de vida.", + "LabelBlastMessageInterval": "Intervalo de mensajes de vida (segundos)", + "LabelBitrate": "Velocidad de bits:", + "LabelBirthYear": "Año de nacimiento:", + "LabelBirthDate": "Fecha de nacimiento:", + "LabelBindToLocalNetworkAddressHelp": "Opcional. Sobrescribe la dirección IP local a la que se vincula el servidor http. Si se deja vacío, el servidor se vinculará a todas las direcciones disponibles. Cambiar este valor requiere reiniciar el servidor Jellyfin.", + "LabelBindToLocalNetworkAddress": "Vincular a la dirección de red local:", + "LabelAutomaticallyRefreshInternetMetadataEvery": "Actualizar automáticamente los metadatos desde Internet:", + "LabelAuthProvider": "Proveedor de autenticación:", + "LabelAudioSampleRate": "Frecuencia de muestreo de audio:", + "LabelAudioLanguagePreference": "Idioma preferido de audio:", + "LabelAudioCodec": "Códec de audio:", + "LabelAudioChannels": "Canales de audio:", + "LabelAudioBitrate": "Velocidad de bits de audio:", + "LabelAudioBitDepth": "Profundidad de bits de audio:", + "LabelAudio": "Audio", + "LabelArtistsHelp": "Separar múltiples empleando ;", + "LabelArtists": "Artistas:", + "LabelAppNameExample": "Ejemplo: Sickbeard, Sonarr", + "LabelAppName": "Nombre de la aplicación", + "LabelAllowedRemoteAddressesMode": "Modo de filtrado de direcciones IP remotas:", + "LabelAllowedRemoteAddresses": "Filtro de direcciones IP remotas:", + "LabelAllowServerAutoRestartHelp": "El servidor solo se reiniciará durante los períodos de inactividad cuando no haya usuarios activos.", + "LabelAllowServerAutoRestart": "Permite al servidor reiniciarse automáticamente para aplicar actualizaciones", + "LabelAllowHWTranscoding": "Permitir transcodificación por hardware", + "LabelAll": "Todos", + "LabelAlbumArtists": "Artistas del álbum:", + "LabelAlbumArtPN": "PN del arte del álbum:", + "LabelAlbumArtMaxWidthHelp": "Resolución máxima del arte del álbum expuesta vía upnp:albumArtURI.", + "LabelAlbumArtMaxWidth": "Ancho máximo del arte del álbum:", + "LabelAlbumArtMaxHeightHelp": "Resolución máxima del arte del álbum expuesta vía upnp:albumArtURI.", + "LabelAlbumArtMaxHeight": "Altura máxima del arte del álbum:", + "LabelAlbumArtHelp": "PN usado para el arte del álbum, dentro del atributo dlna:profileID en upnp:albumArtURI. Algunos dispositivos requieren valores específicos, independientemente del tamaño de la imagen.", + "LabelAlbum": "Álbum:", + "LabelAirsBeforeSeason": "Transmisión antes de la temporada:", + "LabelAirsBeforeEpisode": "Transmisión antes del episodio:", + "LabelAirsAfterSeason": "Transmisión después de la temporada:", + "LabelAirTime": "Duración:", + "LabelAirDays": "Se emite los días:", + "LabelAccessStart": "Hora de inicio:", + "LabelAccessEnd": "Hora de finalización:", + "LabelAccessDay": "Día de la semana:", + "LabelAbortedByServerShutdown": "(Abortado por apagado del servidor)", + "Label3DFormat": "Formato 3D:", + "Kids": "Niños", + "Items": "Elementos", + "ItemCount": "{0} elementos", + "InstantMix": "Mix instantáneo", + "InstallingPackage": "Instalando {0} (versión {1})", + "ImportMissingEpisodesHelp": "Si se habilita, la información sobre los episodios faltantes se importará a la base de datos de Jellyfin y se mostrarán dentro de las temporadas y series. Esto puede causar escaneos de biblioteca significativamente más largos.", + "ImportFavoriteChannelsHelp": "Si se habilita, solo los canales marcados como favoritos en el dispositivo sintonizador serán importados.", + "Images": "Imágenes", + "Identify": "Identificar", + "HttpsRequiresCert": "Para habilitar las conexiones seguras, necesitarás proporcionar un certificado SSL de confianza, como el de Let's Encrypt. Por favor, proporciona un certificado o desactiva las conexiones seguras.", + "Horizontal": "Horizontal", + "Home": "Inicio", + "HideWatchedContentFromLatestMedia": "Ocultar contenido ya visto de últimos medios", + "Hide": "Ocultar", + "Help": "Ayuda", + "HeadersFolders": "Carpetas", + "HeaderYears": "Años", + "HeaderXmlSettings": "Configuración XML", + "HeaderXmlDocumentAttributes": "Atributos del documento XML", + "HeaderXmlDocumentAttribute": "Atributo del documento XML", + "HeaderVideoTypes": "Tipos de video", + "HeaderVideoType": "Tipo de video", + "HeaderVideoQuality": "Calidad de video", + "HeaderUsers": "Usuarios", + "HeaderUploadImage": "Subir imagen", + "HeaderUpcomingOnTV": "Próximamente en TV", + "HeaderTypeText": "Introducir texto", + "HeaderTypeImageFetchers": "Recolectores de imágenes para {0}", + "HeaderTuners": "Sintonizador", + "HeaderTunerDevices": "Sintonizadores", + "HeaderTranscodingProfileHelp": "Agrega perfiles de transcodificación para indicar qué formatos deben ser usados cuando se requiere transcodificar.", + "HeaderTranscodingProfile": "Perfil de transcodificación", + "HeaderTracks": "Pistas", + "HeaderThisUserIsCurrentlyDisabled": "Este usuario se encuentra actualmente deshabilitado", + "HeaderTaskTriggers": "Disparadores de tarea", + "HeaderTags": "Etiquetas", + "HeaderSystemDlnaProfiles": "Perfiles del sistema", + "HeaderSyncPlayEnabled": "SyncPlay habilitado", + "HeaderSyncPlaySelectGroup": "Unirse a un grupo", + "HeaderSubtitleProfilesHelp": "Los perfiles de subtítulo describen los formatos de subtítulo soportados por el dispositivo.", + "HeaderSubtitleProfiles": "Perfiles de subtítulo", + "HeaderSubtitleProfile": "Perfil de subtítulo", + "HeaderSubtitleDownloads": "Descarga de subtítulos", + "HeaderSubtitleAppearance": "Apariencia de subtítulos", + "HeaderStopRecording": "Detener grabación", + "HeaderStatus": "Estado", + "HeaderStartNow": "Iniciar ahora", + "HeaderSpecialFeatures": "Características especiales", + "HeaderSpecialEpisodeInfo": "Información del episodio especial", + "HeaderSortOrder": "Clasificar ordenado", + "HeaderSortBy": "Ordenar por", + "HeaderShutdown": "Apagar", + "HeaderSetupLibrary": "Configura tus bibliotecas de medios", + "HeaderSettings": "Configuración", + "HeaderServerSettings": "Configuración del servidor", + "HeaderSelectServerCachePathHelp": "Explora o introduce la ruta a utilizar para los archivos caché del servidor. Se debe tener permisos de escritura en dicha carpeta.", + "HeaderSelectServerCachePath": "Seleccionar ruta para la caché del servidor", + "HeaderSelectServer": "Seleccionar servidor", + "HeaderSelectPath": "Seleccionar ruta", + "HeaderSelectMetadataPathHelp": "Explora o introduce la ruta donde deseas almacenar los metadatos. Se debe tener permisos de escritura en dicha carpeta.", + "HeaderSelectMetadataPath": "Selecciona la ruta para los metadatos", + "HeaderSelectCertificatePath": "Selecciona la ruta del certificado", + "HeaderSecondsValue": "{0} segundos", + "HeaderSeasons": "Temporadas", + "HeaderSchedule": "Programación", + "HeaderScenes": "Escenas", + "HeaderRunningTasks": "Tareas en ejecución", + "HeaderRevisionHistory": "Historial de versiones", + "HeaderRestartingServer": "Reiniciando servidor", + "HeaderRestart": "Reiniciar", + "HeaderResponseProfileHelp": "Los perfiles de respuesta proporcionan un medio para personalizar la información enviada al dispositivo cuando se reproducen ciertos tipos de medios.", + "HeaderResponseProfile": "Perfil de respuesta", + "HeaderRemoveMediaLocation": "Remover ubicación de medios", + "HeaderRemoveMediaFolder": "Remover carpeta de medios", + "HeaderRemoteControl": "Control remoto", + "HeaderRemoteAccessSettings": "Opciones de acceso remoto", + "HeaderRecordingPostProcessing": "Post procesado de las grabaciones", + "HeaderRecordingOptions": "Opciones de grabación", + "HeaderRecentlyPlayed": "Reproducido recientemente", + "HeaderProfileServerSettingsHelp": "Estos valores controlan como el servidor Jellyfin se presentará al dispositivo.", + "HeaderProfileInformation": "Información del perfil", + "HeaderProfile": "Perfil", + "HeaderPreferredMetadataLanguage": "Idioma preferido para los metadatos", + "HeaderPluginInstallation": "Instalación de complemento", + "HeaderPleaseSignIn": "Por favor, inicia sesión", + "HeaderPlaybackError": "Error de reproducción", + "HeaderPlayback": "Reproducción de medios", + "HeaderPlayOn": "Reproducir en", + "HeaderPlayAll": "Reproducir todo", + "HeaderPinCodeReset": "Restablecer código PIN", + "HeaderPhotoAlbums": "Álbumes de fotos", + "HeaderPeople": "Personas", + "HeaderPendingInvitations": "Invitaciones pendientes", + "HeaderPaths": "Rutas", + "HeaderPasswordReset": "Restablecer contraseña", + "HeaderPassword": "Contraseña", + "HeaderParentalRatings": "Clasificación parental", + "HeaderOtherItems": "Otros elementos", + "HeaderOnNow": "Transmitiendo ahora", + "HeaderNextVideoPlayingInValue": "El siguiente video se reproducirá en {0}", + "HeaderNextEpisodePlayingInValue": "El siguiente episodio se reproducirá en {0}", + "HeaderNewDevices": "Nuevos dispositivos", + "HeaderNewApiKey": "Nueva clave API", + "HeaderNavigation": "Navegación", + "HeaderMyMediaSmall": "Mis medios (pequeño)", + "HeaderMyMedia": "Mis medios", + "HeaderMyDevice": "Mi dispositivo", + "HeaderMusicVideos": "Videos musicales", + "HeaderMusicQuality": "Calidad de la música", + "HeaderMovies": "Películas", + "HeaderMoreLikeThis": "Más como esto", + "HeaderMetadataSettings": "Configuración de metadatos", + "HeaderMediaInfo": "Info del medio", + "HeaderMediaFolders": "Carpetas de medios", + "HeaderMedia": "Medios", + "HeaderLoginFailure": "Falló el inicio de sesión", + "HeaderLiveTvTunerSetup": "Configuración del sintonizador de TV", + "HeaderLiveTv": "TV en vivo", + "HeaderLibrarySettings": "Configuraciones de biblioteca", + "HeaderLibraryOrder": "Orden de las bibliotecas", + "HeaderLibraryFolders": "Carpetas de bibliotecas", + "HeaderLibraryAccess": "Acceso a bibliotecas", + "HeaderLibraries": "Bibliotecas", + "HeaderLatestRecordings": "Últimas grabaciones", + "HeaderLatestMusic": "Última música", + "HeaderLatestMovies": "Últimas películas", + "HeaderLatestMedia": "Últimos medios", + "HeaderLatestEpisodes": "Últimos episodios", + "HeaderKodiMetadataHelp": "Para habilitar o deshabilitar los metadatos NFO, edite una biblioteca en la configuración de bibliotecas de Jellyfin y ubica la sección grabadores de metadatos.", + "HeaderKeepSeries": "Conservar serie", + "HeaderKeepRecording": "Conservar grabación", + "HeaderItems": "Elementos", + "HeaderInstantMix": "Mix instantáneo", + "HeaderInstall": "Instalar", + "HeaderImageSettings": "Configuración de imagen", + "HeaderImageOptions": "Opciones de imagen", + "HeaderIdentifyItemHelp": "Introduce uno o más criterios de búsqueda. Elimina criterios para expandir los resultados.", + "HeaderIdentificationHeader": "Encabezado de identificación", + "HeaderIdentificationCriteriaHelp": "Introduzca, al menos, un criterio de identificación.", + "HeaderIdentification": "Identificación", + "HeaderHttpsSettings": "Opciones HTTPS", + "HeaderHttpHeaders": "Encabezados HTTP", + "HeaderHome": "Inicio", + "HeaderGuideProviders": "Proveedores de Guías de TV", + "HeaderGenres": "Géneros", + "HeaderFrequentlyPlayed": "Reproducido frecuentemente", + "HeaderForgotPassword": "Olvidé mi contraseña", + "HeaderForKids": "Para niños", + "HeaderFilters": "Filtros", + "HeaderFetcherSettings": "Configuración del recolector", + "HeaderFetchImages": "Obtener imágenes:", + "HeaderFeatures": "Características", + "HeaderFeatureAccess": "Acceso a características", + "HeaderFavoritePlaylists": "Listas de reproducción favoritas", + "HeaderFavoriteVideos": "Videos favoritos", + "LabelMetadataPath": "Ruta para los metadatos:", + "LabelMetadataDownloadersHelp": "Habilita y prioriza tus recolectores de metadatos preferidos. Los recolectores de metadatos de menor prioridad solo serán utilizados para llenar información faltante.", + "LabelMetadataDownloadLanguage": "Idioma preferido para las descargas:", + "LabelMetadata": "Metadatos:", + "LabelMessageTitle": "Título del mensaje:", + "LabelMessageText": "Texto del mensaje:", + "LabelMaxStreamingBitrateHelp": "Especifique una velocidad de bits máxima cuando se transmite.", + "LabelMaxStreamingBitrate": "Calidad máxima de transmisión:", + "LabelMaxScreenshotsPerItem": "Número máximo de capturas de pantalla por elemento:", + "LabelMaxResumePercentageHelp": "Los medios se consideran totalmente reproducidos si se detienen después de este tiempo.", + "LabelMaxResumePercentage": "Porcentaje máximo para la reanudación:", + "LabelMaxParentalRating": "Máxima clasificación parental permitida:", + "LabelMaxChromecastBitrate": "Calidad de transmisión de Chromecast:", + "LabelMaxBackdropsPerItem": "Número máximo de imágenes de fondo por elemento:", + "LabelMatchType": "Tipo de coincidencia:", + "LabelManufacturerUrl": "URL del fabricante", + "LabelManufacturer": "Fabricante:", + "LabelLogs": "Registros:", + "LabelLoginDisclaimerHelp": "Un mensaje que se mostrará en la parte inferior de la página de inicio de sesión.", + "LabelLoginDisclaimer": "Aviso legal:", + "LabelLockItemToPreventChanges": "Bloquear este elemento para evitar cambios futuros", + "LabelLocalHttpServerPortNumberHelp": "El número de puerto TCP al que el servidor HTTP de Jellyfin debería enlazar.", + "LabelLocalHttpServerPortNumber": "Número de puerto local HTTP:", + "LabelLineup": "Programación:", + "LabelLibraryPageSizeHelp": "Establece el número de elementos a mostrar en una página de biblioteca. Establece en 0 para deshabilitar el paginado.", + "LabelLibraryPageSize": "Tamaño de las páginas de las bibliotecas:", + "LabelLanguage": "Idioma:", + "LabelLanNetworks": "Redes LAN:", + "LabelKodiMetadataUserHelp": "Guarda los datos de visto en archivos NFO para que otras aplicaciones los utilicen.", + "LabelKodiMetadataUser": "Guardar los datos de visto del usuario en archivos NFO para:", + "LabelKodiMetadataSaveImagePathsHelp": "Esto se recomienda si tienes nombres de imágenes que no se ajustan a los lineamientos de Kodi.", + "LabelKodiMetadataSaveImagePaths": "Guardar las rutas de las imágenes en los archivos NFO", + "LabelKodiMetadataEnablePathSubstitutionHelp": "Habilita la sustitución de rutas de imágenes usando la configuración de sustitución de rutas del servidor.", + "LabelKodiMetadataEnablePathSubstitution": "Habilitar sustitución de ruta", + "LabelKodiMetadataEnableExtraThumbsHelp": "Cuando se descargan imágenes estas pueden ser almacenadas tanto en extrafanart como extrathumb para maximizar la compatibilidad con las pieles de Kodi.", + "LabelKodiMetadataEnableExtraThumbs": "Copiar extrafanart al campo extrathumbs", + "LabelKodiMetadataDateFormatHelp": "Todas las fechas dentro de los archivos NFO serán analizadas usando este formato.", + "LabelKodiMetadataDateFormat": "Formato de fecha de estreno:", + "HeaderFavoritePeople": "Personas favoritas", + "HeaderFavoriteMovies": "Películas favoritas", + "HeaderFavoriteBooks": "Libros favoritos", + "HeaderExternalIds": "IDs externos:", + "HeaderError": "Error", + "HeaderEpisodes": "Episodios", + "HeaderEnabledFieldsHelp": "Desmarca un campo para bloquearlo y prevenir que sus datos sean cambiados.", + "HeaderEnabledFields": "Campos habilitados", + "HeaderEditImages": "Editar imágenes", + "HeaderEasyPinCode": "Código PIN sencillo", + "HeaderDVR": "DVR", + "HeaderDownloadSync": "Descargar y sincronizar", + "HeaderDisplay": "Pantalla", + "HeaderDirectPlayProfileHelp": "Agrega perfiles de reproducción directa para indicar qué formatos puede manejar el dispositivo de forma nativa.", + "HeaderDirectPlayProfile": "Perfil de reproducción directa", + "HeaderDevices": "Dispositivos", + "HeaderDeviceAccess": "Acceso a dispositivos", + "HeaderDeveloperInfo": "Info del desarrollador", + "HeaderDetectMyDevices": "Detectar mis dispositivos", + "HeaderDeleteTaskTrigger": "Borrar disparador de tarea", + "HeaderDeleteProvider": "Eliminar proveedor", + "HeaderDeleteItems": "Eliminar elementos", + "HeaderDeleteItem": "Eliminar elemento", + "HeaderDeleteDevice": "Eliminar dispositivo", + "HeaderDefaultRecordingSettings": "Configuración predeterminada de grabaciones", + "HeaderDateIssued": "Fecha de emisión", + "HeaderCustomDlnaProfiles": "Perfiles personalizados", + "HeaderContinueListening": "Continuar escuchando", + "HeaderContainerProfileHelp": "Los perfiles de contenedor indican las limitaciones de un dispositivo al reproducir formatos específicos. Si una limitación se aplica entonces el medio será transcodificado, incluso si el formato ha sido configurado para reproducción directa.", + "HeaderContainerProfile": "Perfil del contenedor", + "HeaderConnectionFailure": "Falla de conexión", + "HeaderConnectToServer": "Conectarse al servidor", + "HeaderConfirmRevokeApiKey": "Revocar clave API", + "HeaderConfirmProfileDeletion": "Confirmar eliminación de perfil", + "HeaderConfirmPluginInstallation": "Confirmar instalación de complemento", + "HeaderConfigureRemoteAccess": "Configurar acceso remoto", + "HeaderCodecProfileHelp": "Los perfiles de códecs indican las limitaciones de un dispositivo al reproducir códecs específicos. Si una limitación se aplica entonces el medio será transcodificado, incluso si el códec ha sido configurado para reproducción directa.", + "HeaderCodecProfile": "Perfil de códec", + "HeaderChapterImages": "Imágenes de los capítulos", + "HeaderChannels": "Canales", + "HeaderChannelAccess": "Acceso a los canales", + "HeaderCastCrew": "Reparto y equipo", + "HeaderCastAndCrew": "Reparto y equipo", + "HeaderCancelSeries": "Cancelar serie", + "HeaderCancelRecording": "Cancelar grabación", + "HeaderBranding": "Establecer marca", + "HeaderBooks": "Libros", + "HeaderBlockItemsWithNoRating": "Bloquear elementos sin clasificación o con información de clasificación desconocida:", + "HeaderAutomaticUpdates": "Actualizaciones automáticas", + "HeaderAudioSettings": "Configuración de audio", + "HeaderAudioBooks": "Audiolibros", + "HeaderAppearsOn": "Aparece en", + "HeaderApp": "Aplicación", + "ApiKeysCaption": "Lista de claves API actualmente habilitadas", + "HeaderApiKeysHelp": "Las aplicaciones externas deben tener una clave API para poder comunicarse con el servidor Jellyfin. Las claves se emiten al iniciar sesión con una cuenta Jellyfin, o al otorgar manualmente una clave a la aplicación.", + "HeaderApiKeys": "Claves API", + "HeaderApiKey": "Clave API", + "HeaderAllowMediaDeletionFrom": "Permitir eliminación de medios de", + "HeaderAlert": "Alerta", + "HeaderAlbums": "Álbumes", + "HeaderAdmin": "Administrador", + "HeaderAdditionalParts": "Partes adicionales", + "HeaderAddUser": "Agregar usuario", + "HeaderAddUpdateImage": "Agregar/Actualizar Imagen", + "HeaderAddToPlaylist": "Agregar a lista de reproducción", + "HeaderAddToCollection": "Agregar a colección", + "HeaderAddScheduledTaskTrigger": "Agregar disparador", + "HeaderActivity": "Actividad", + "HeaderActiveRecordings": "Grabaciones activas", + "HeaderActiveDevices": "Dispositivos activos", + "HeaderAccessScheduleHelp": "Crea una programación de acceso para limitar el acceso a ciertos horarios.", + "HeaderAccessSchedule": "Programación de acceso", + "HardwareAccelerationWarning": "Habilitar la aceleración por hardware podría causar inestabilidad en algunos entornos. Asegúrate de que tu sistema operativo y controladores de video están actualizados. Si tienes dificultades reproduciendo videos después de habilitar esto, necesitarás volver a cambiar la configuración a Ninguno.", + "HDPrograms": "Programas en HD", + "EncoderPresetHelp": "Elige un valor más rápido para mejorar el rendimiento, o uno más lento para mejorar la calidad.", + "H264CrfHelp": "El «Factor de transferencia constante» (CRF) es la configuración de calidad por defecto para el codificador x264. Puedes establecer valores entre 0 y 51, donde los valores más bajos dan como resultado mejor calidad (a expensas de archivos más grandes). Los valores comunes son entre 18 y 28. El valor por defecto para x264 es 23, así que puedes usar este valor como punto de referencia.", + "GuideProviderSelectListings": "Elegir listados", + "GuideProviderLogin": "Iniciar sesión", + "Guide": "Guía", + "GuestStar": "Estrella invitada", + "GroupVersions": "Agrupar versiones", + "GroupBySeries": "Agrupar por series", + "Genre": "Género", + "General": "General", + "Fullscreen": "Pantalla completa", + "Friday": "Viernes", + "FormatValue": "Formato: {0}", + "FolderTypeUnset": "Contenido variado", + "FolderTypeTvShows": "Series de TV", + "FolderTypeMusicVideos": "Videos musicales", + "FolderTypeMusic": "Música", + "FolderTypeMovies": "Películas", + "FolderTypeBooks": "Libros", + "Filters": "Filtros", + "FileReadError": "Ha ocurrido un error al leer el archivo.", + "FileReadCancelled": "La lectura del archivo ha sido cancelada.", + "FileNotFound": "Archivo no encontrado.", + "File": "Archivo", + "FetchingData": "Obteniendo datos adicionales", + "Features": "Características", + "Favorite": "Favorito", + "FastForward": "Avance rápido", + "FFmpegSavePathNotFound": "No fue posible localizar FFmpeg usando la ruta que has introducido. FFprobe también es requerido y debe de estar en la misma carpeta. Estos componentes normalmente están empaquetados en la misma descarga. Por favor, verifica la ruta e inténtalo de nuevo.", + "Extras": "Extras", + "ExtractChapterImagesHelp": "La extracción de imágenes de capítulos permitirá a los clientes mostrar menús gráficos de selección de escenas. El proceso puede ser lento, intensivo en recursos y puede requerir varios gigabytes de espacio. Se ejecuta cuando se descubren los videos y también como una tarea programada cada noche. La programación es configurable en el área de tareas programadas. No se recomienda ejecutar esta tarea durante las horas de mayor uso.", + "ExtraLarge": "Extra grande", + "ExitFullscreen": "Salir de la pantalla completa", + "EveryNDays": "Cada {0} días", + "ErrorSavingTvProvider": "Hubo un error al guardar el proveedor de TV. Por favor, asegúrate de que sea accesible e inténtalo de nuevo.", + "ErrorPleaseSelectLineup": "Por favor, selecciona una programación e inténtalo de nuevo. Si no hay disponible ninguna, entonces, por favor, verifica que tu nombre de usuario, contraseña, y código postal sean correctos.", + "ErrorMessageStartHourGreaterThanEnd": "La hora de finalización debe ser mayor que la hora de inicio.", + "ErrorGettingTvLineups": "Hubo un error al descargar la programación de TV. Por favor, asegúrate de que tu información sea correcta e inténtalo de nuevo.", + "ErrorDeletingItem": "Hubo un error eliminando el elemento del servidor Jellyfin. Por favor, verifica que el servidor Jellyfin tiene permisos de escritura en la carpeta del medio e inténtalo de nuevo.", + "ErrorAddingXmlTvFile": "Hubo un error accediendo al archivo XMLTV. Por favor, asegúrate de que el archivo existe e inténtalo de nuevo.", + "ErrorAddingTunerDevice": "Hubo un error al agregar el dispositivo sintonizador. Por favor, asegúrate de que esté disponible e inténtalo de nuevo.", + "ErrorAddingMediaPathToVirtualFolder": "Hubo un error agregando la ruta de medios. Por favor, asegúrate de que la ruta es válida y que el proceso del servidor Jellyfin tiene acceso a ese destino.", + "ErrorAddingListingsToSchedulesDirect": "Hubo un error agregando la programación de tu cuenta de Schedules Direct. Schedules Direct solo permite un numero limitado de programaciones por cuenta. Tal vez necesites acceder al sitio web de Schedules Direct y eliminar otras programaciones de tu cuenta antes de continuar.", + "Episodes": "Episodios", + "Episode": "Episodio", + "EndsAtValue": "Termina a las {0}", + "Ended": "Finalizado", + "EnableDetailsBannerHelp": "Mostrar una imagen banner en la parte superior de la página de detalles del elemento.", + "EnableDetailsBanner": "Banner de detalles", + "EnableThemeVideosHelp": "Reproducir videos temáticos en el fondo mientras se navega por la biblioteca.", + "EnableThemeVideos": "Videos temáticos", + "EnableThemeSongsHelp": "Reproducir canciones temáticas en el fondo mientras se navega por la biblioteca.", + "EnableThemeSongs": "Canciones temáticas", + "EnableStreamLoopingHelp": "Habilita esta opción si las transmisiones en vivo contienen solo unos pocos segundos de datos y necesitan ser solicitadas continuamente. Habilitar esto cuando no es requerido puede causar problemas.", + "EnableStreamLooping": "Repetir automáticamente las transmisiones en vivo", + "EnablePhotosHelp": "Las imágenes serán detectadas y mostradas junto con otros archivos multimedia.", + "EnablePhotos": "Mostrar fotografías", + "EnableNextVideoInfoOverlayHelp": "Al finalizar un video, mostrar información sobre el siguiente video a reproducir en la lista de reproducción actual.", + "EnableNextVideoInfoOverlay": "Mostrar la información del siguiente video durante la reproducción", + "EnableHardwareEncoding": "Habilitar codificación por hardware", + "EnableExternalVideoPlayersHelp": "Un menú de reproductor externo se mostrara cuando inicie la reproducción de un video.", + "EnableExternalVideoPlayers": "Reproductores de video externos", + "EnableDisplayMirroring": "Duplicado de pantalla", + "EnableColorCodedBackgrounds": "Fondos de colores codificados", + "EnableCinemaMode": "Modo cine", + "EnableBackdropsHelp": "Muestra imágenes de fondo en el fondo de algunas páginas mientras se navega por la biblioteca.", + "EnableBackdrops": "Imágenes de fondo", + "EditSubtitles": "Editar subtítulos", + "EditMetadata": "Editar metadatos", + "EditImages": "Editar imágenes", + "Edit": "Editar", + "EasyPasswordHelp": "Tu código PIN fácil se utiliza para el acceso sin conexión en los clientes soportados y también puede utilizarse para acceder fácilmente cuando se está en la misma red.", + "DropShadow": "Sombra paralela", + "DrmChannelsNotImported": "Los canales con DRM no serán importados.", + "DownloadsValue": "{0} descargas", + "Download": "Descargar", + "Down": "Abajo", + "DoNotRecord": "No grabar", + "DisplayModeHelp": "Selecciona el estilo de diseño que desea para la interfaz.", + "DisplayMissingEpisodesWithinSeasonsHelp": "Esto también debe estar habilitado para las bibliotecas de TV en la configuración del servidor.", + "DisplayMissingEpisodesWithinSeasons": "Mostrar episodios faltantes en las temporadas", + "DisplayInOtherHomeScreenSections": "Mostrar en las secciones de la pantalla de inicio como recientes o continuar viendo", + "DisplayInMyMedia": "Mostrar en la pantalla de inicio", + "Display": "Pantalla", + "Dislike": "No me gusta", + "Disconnect": "Desconectar", + "Disc": "DIsco", + "Disabled": "Desactivado", + "Directors": "Directores", + "DirectStreaming": "Transmisión directa", + "DirectStreamHelp2": "Transmitir directamente un archivo usa muy poco poder de procesamiento sin ninguna perdida en la calidad de video.", + "DirectStreamHelp1": "El medio es compatible con el dispositivo en cuanto a la resolución y tipo de medio (H.264, AC3, etc.), pero está en un contenedor de archivo incompatible (mkv, avi, wmv, etc.). El video será reempaquetado sobre la marcha antes de transmitirlo al dispositivo.", + "DirectPlaying": "Reproducción directa", + "DeviceAccessHelp": "Esto solo se aplica a los dispositivos que pueden ser identificados de manera única y no impedirá el acceso desde navegadores. Filtrar el acceso a los dispositivos de los usuarios les impedirá usar nuevos dispositivos hasta que hayan sido aprobados aquí.", + "DetectingDevices": "Detectando dispositivos", + "Desktop": "Escritorio", + "Descending": "Descendente", + "DeleteUserConfirmation": "¿Estás seguro de querer eliminar este usuario?", + "DeleteUser": "Eliminar usuario", + "DeleteMedia": "Eliminar medios", + "DeleteImageConfirmation": "¿Estás seguro de querer eliminar esta imagen?", + "DeleteImage": "Eliminar imagen", + "DeleteDeviceConfirmation": "¿Estás seguro de querer eliminar este dispositivo? Volverá a aparecer la siguiente vez que un usuario inicie sesión con él.", + "Delete": "Eliminar", + "DeinterlaceMethodHelp": "Seleccione el método de desentrelazado que se usará al transcodificar contenido entrelazado.", + "DefaultSubtitlesHelp": "Los subtítulos se cargan basados en los indicadores «por defecto» y «forzado» incluidos en los metadatos. Las preferencias de idioma son consideradas cuando hay múltiples opciones disponibles.", + "DefaultMetadataLangaugeDescription": "Estos son sus valores por defecto y pueden ser personalizados en cada biblioteca.", + "DefaultErrorMessage": "Ha ocurrido un error al procesar la solicitud. Por favor, inténtalo de nuevo más tarde.", + "Default": "Por defecto", + "DeathDateValue": "Falleció: {0}", + "DatePlayed": "Fecha de reproducción", + "DateAdded": "Fecha de adición", + "CustomDlnaProfilesHelp": "Crear un perfil personalizado para un nuevo dispositivo o reemplazar un perfil del sistema.", + "CriticRating": "Calificación de los críticos", + "CopyStreamURLError": "Hubo un error al copiar la URL.", + "CopyStreamURLSuccess": "URL copiada con éxito.", + "CopyStreamURL": "Copiar la URL de la transmisión", + "Continuing": "Continuando", + "ContinueWatching": "Continuar viendo", + "Connect": "Conectar", + "ConfirmEndPlayerSession": "¿Deseas apagar Jellyfin en {0}?", + "ConfirmDeletion": "Confirmar eliminación", + "HeaderServerAddressSettings": "Configuración de la dirección del servidor", + "HeaderSeriesStatus": "Estado de la serie", + "HeaderSeriesOptions": "Opciones de serie", + "HeaderSeries": "Series", + "HeaderSendMessage": "Enviar mensaje", + "HeaderSelectTranscodingPathHelp": "Explora o introduce la ruta a utilizar para los archivos temporales de transcodificación. Se debe tener permisos de escritura en dicha carpeta.", + "HeaderSelectTranscodingPath": "Selecciona la ruta para los archivos temporales de transcodificación", + "ConfirmDeleteItems": "Eliminar estos elementos los eliminará tanto del sistema como de tu biblioteca de medios. ¿Estás seguro de querer continuar?", + "ConfirmDeleteItem": "Eliminar este elemento lo eliminará tanto del sistema como de tu biblioteca de medios. ¿Estás seguro de querer continuar?", + "ConfirmDeleteImage": "¿Eliminar imagen?", + "ConfigureDateAdded": "Configura cómo se determina la fecha de adición en el panel de control del servidor Jellyfin en la configuración de la biblioteca", + "Composer": "Compositor", + "CommunityRating": "Calificación de la comunidad", + "ColorTransfer": "Transferencia de color", + "ColorSpace": "Espacio de color", + "ColorPrimaries": "Colores primarios", + "ClientSettings": "Configuración del cliente", + "CinemaModeConfigurationHelp": "El modo cine lleva la experiencia del cine directamente a tu sala de estar con la capacidad de reproducir trailers e introducciones personalizadas antes de la función principal.", + "ChannelNumber": "Número de canal", + "ChannelNameOnly": "Canal {0} solamente", + "ChannelAccessHelp": "Selecciona los canales a compartir con este usuario. Los administradores podrán editar todos los canales empleando el administrador de metadatos.", + "ChangingMetadataImageSettingsNewContent": "Cambios en las configuraciones de descarga de metadatos o arte solo se aplicarán a contenido nuevo agregado a tu biblioteca. Para aplicar los cambios a los títulos existentes, necesitarás actualizar sus metadatos manualmente.", + "Categories": "Categorías", + "CancelSeries": "Cancelar serie", + "CancelRecording": "Cancelar grabación", + "ButtonWebsite": "Sitio web", + "ButtonViewWebsite": "Ver sitio web", + "ButtonUp": "Arriba", + "ButtonUninstall": "Desinstalar", + "ButtonTrailer": "Trailer", + "ButtonTogglePlaylist": "Lista de reproducción", + "ButtonToggleContextMenu": "Más", + "ButtonSubtitles": "Subtítulos", + "ButtonSubmit": "Enviar", + "ButtonSplit": "Dividir", + "ButtonStop": "Detener", + "ButtonStart": "Iniciar", + "ButtonSort": "Ordenar", + "ButtonSignOut": "Cerrar sesión", + "ButtonSignIn": "Iniciar sesión", + "ButtonShutdown": "Apagar", + "ButtonShuffle": "Aleatorio", + "ButtonSettings": "Configuración", + "ButtonSend": "Enviar", + "ButtonSelectView": "Seleccionar vista", + "ButtonSelectServer": "Seleccionar servidor", + "ButtonSelectDirectory": "Seleccionar directorio", + "ButtonSearch": "Búsqueda", + "ButtonScanAllLibraries": "Escanear todas las bibliotecas", + "ButtonSave": "Guardar", + "ButtonRevoke": "Revocar", + "ButtonResume": "Continuar", + "ButtonRestart": "Reiniciar", + "ButtonResetPassword": "Restablecer contraseña", + "ButtonResetEasyPassword": "Restablecer código PIN sencillo", + "ButtonRepeat": "Repetir", + "ButtonRename": "Renombrar", + "ButtonRemove": "Remover", + "ButtonRefreshGuideData": "Actualizar datos de la guía", + "ButtonRefresh": "Actualizar", + "ButtonQuickStartGuide": "Guía de inicio rápido", + "ButtonProfile": "Perfil", + "ButtonPreviousTrack": "Pista anterior", + "ButtonPlay": "Reproducir", + "ButtonPause": "Pausar", + "ButtonParentalControl": "Control parental", + "ButtonOpen": "Abrir", + "ButtonOk": "OK", + "ButtonOff": "Apagar", + "ButtonNextTrack": "Pista siguiente", + "ButtonNew": "Nuevo", + "ButtonNetwork": "Red", + "ButtonMore": "Más", + "ButtonManualLogin": "Inicio de sesión manual", + "ButtonLibraryAccess": "Acceso a biblioteca(s)", + "ButtonLearnMore": "Aprender más", + "ButtonInfo": "Info", + "ButtonHome": "Inicio", + "ButtonHelp": "Ayuda", + "ButtonGuide": "Guía", + "ButtonGotIt": "Hecho", + "ButtonFullscreen": "Pantalla completa", + "ButtonForgotPassword": "Olvidé mi contraseña", + "ButtonFilter": "Filtro", + "ButtonEditOtherUserPreferences": "Editar el perfil, la imagen y las preferencias personales de este usuario.", + "ButtonEditImages": "Editar imágenes", + "ButtonEdit": "Editar", + "ButtonDownload": "Descargar", + "ButtonDown": "Abajo", + "ButtonDeleteImage": "Eliminar imagen", + "ButtonDelete": "Eliminar", + "ButtonConnect": "Conectar", + "ButtonChangeServer": "Cambiar servidor", + "ButtonCancel": "Cancelar", + "ButtonBack": "Atrás", + "ButtonAudioTracks": "Pistas de audio", + "ButtonArrowUp": "Arriba", + "ButtonArrowRight": "Derecha", + "ButtonArrowLeft": "Izquierda", + "ButtonArrowDown": "Abajo", + "ButtonAddUser": "Agregar usuario", + "ButtonAddServer": "Agregar servidor", + "ButtonAddScheduledTaskTrigger": "Agregar disparador", + "ButtonAddMediaLibrary": "Agregar biblioteca de medios", + "ButtonAddImage": "Agregar imagen", + "ButtonAdd": "Agregar", + "BurnSubtitlesHelp": "Determina si el servidor debería quemar los subtítulos al transcodificar videos. Evitar esto mejorará altamente el rendimiento del servidor. Seleccione Auto para grabar formatos basados en imágenes (VOBSUB, PGS, SUB, IDX...) y ciertos subtítulos ASS o SSA.", + "BrowsePluginCatalogMessage": "Explora nuestro catálogo de complementos para ver los complementos disponibles.", + "Browse": "Explorar", + "BoxRear": "Caja (parte trasera)", + "Box": "Caja", + "BookLibraryHelp": "Los libros de texto y audiolibros están soportados. Revisa la {0} guía de nombrado de libros {1}.", + "Blacklist": "Lista negra", + "BirthPlaceValue": "Lugar de nacimiento: {0}", + "BirthLocation": "Lugar de nacimiento", + "BirthDateValue": "Nacimiento: {0}", + "Banner": "Banner", + "Backdrops": "Imágenes de fondo", + "Backdrop": "Imagen de fondo", + "AutoBasedOnLanguageSetting": "Auto (basado en la configuración del idioma)", + "Auto": "Auto", + "AuthProviderHelp": "Selecciona un proveedor de autenticación que se utilizará para autenticar la contraseña de este usuario.", + "Audio": "Audio", + "AttributeNew": "Nuevo", + "AspectRatio": "Relación de aspecto", + "AskAdminToCreateLibrary": "Pide a un administrador crear una biblioteca.", + "Ascending": "Ascendente", + "AsManyAsPossible": "Tantos como sea posible", + "Artist": "Artista", + "Art": "Arte", + "AroundTime": "Alrededor de", + "Anytime": "En cualquier momento", + "AnyLanguage": "Cualquier idioma", + "AlwaysPlaySubtitlesHelp": "Los subtítulos que coincidan con el idioma preferido serán cargados independientemente del idioma del audio.", + "AlwaysPlaySubtitles": "Siempre reproducir", + "AllowedRemoteAddressesHelp": "Lista separada por comas de direcciones IP/máscaras de red para las redes a las que se les permitirá conectarse remotamente. Si se deja en blanco, se les permitirá a todas las direcciones remotas.", + "AllowRemoteAccessHelp": "Si no se marca, se bloquearán todas las conexiones remotas.", + "AllowRemoteAccess": "Permitir conexiones remotas a este servidor Jellyfin.", + "AllowFfmpegThrottlingHelp": "Cuando una transcodificación o remuxeado se adelanta lo suficiente de la posición de reproducción actual, se pausa el proceso para que consuma menos recursos. Esto es más útil cuando se mira sin buscar con frecuencia. Apaga esto si experimentas problemas de reproducción.", + "AllowFfmpegThrottling": "Regular transcodificaciones", + "AllowOnTheFlySubtitleExtractionHelp": "Los subtítulos incrustados pueden extraerse de los videos y entregarse a los clientes en texto plano para ayudar a evitar la transcodificación de video. En algunos sistemas, esto puede tardar mucho tiempo y provocar que la reproducción de video se detenga durante el proceso de extracción. Deshabilite esta opción para que los subtítulos incrustados se graben con transcodificación de video cuando no estén soportados de forma nativa por el dispositivo cliente.", + "AllowOnTheFlySubtitleExtraction": "Permitir la extracción de subtítulos sobre la marcha", + "AllowMediaConversionHelp": "Permitir o denegar acceso a la función de convertir medios.", + "AllowMediaConversion": "Permitir conversión de medios", + "AllowHWTranscodingHelp": "Permite al sintonizador transcodificar las transmisiones sobre la marcha. Esto puede ayudar a reducir la transcodificación requerida por el servidor.", + "AllLibraries": "Todas las bibliotecas", + "AllLanguages": "Todos los idiomas", + "AllEpisodes": "Todos los episodios", + "AllComplexFormats": "Todos los formatos complejos (ASS, SSA, VOBSUB, PGS, SUB, IDX...)", + "AllChannels": "Todos los canales", + "All": "Todo", + "Alerts": "Alertas" +} diff --git a/src/strings/fa.json b/src/strings/fa.json index 3a737a6be0..f8e2cf8207 100644 --- a/src/strings/fa.json +++ b/src/strings/fa.json @@ -1,8 +1,8 @@ { "All": "همه", - "AllowMediaConversion": "اجازه تبدیل رسانه ها", - "AllowMediaConversionHelp": "دادن یا ندادن دسترسی به ویژگی تبدیل رسانه ها", - "AllowRemoteAccess": "اجازه دادن اتصال از راه دور به سرور Jellyfin", + "AllowMediaConversion": "اجازه تبدیل رسانه‌ها", + "AllowMediaConversionHelp": "دادن یا ندادن دسترسی به ویژگی تبدیل رسانه‌ها.", + "AllowRemoteAccess": "اجازه بدهید اتصال‌های از راه دور به این سرور Jellyfin متصل شوند.", "AllowRemoteAccessHelp": "اگرانتخاب نشود، تمامی اتصال های از راه دور بلوکه می شوند.", "Browse": "مرور کردن", "ButtonAddUser": "افزودن کاربر", @@ -19,11 +19,11 @@ "ButtonSort": "مرتب سازی", "DeleteMedia": "حذف رسانه", "Disabled": "غیرفعال شده", - "FolderTypeBooks": "کتاب ها", - "FolderTypeMovies": "سینمایی ها", - "FolderTypeMusic": "موسیقی", - "FolderTypeMusicVideos": "موزیک ویدئوها", - "FolderTypeTvShows": "سریال تلویزیونی", + "FolderTypeBooks": "کتاب‌ها", + "FolderTypeMovies": "فیلم‌ها", + "FolderTypeMusic": "موسیقی‌ها", + "FolderTypeMusicVideos": "موزیک ویدیوها", + "FolderTypeTvShows": "سریال‌های تلویزیونی", "HeaderAddUser": "اضافه کردن کاربر", "HeaderContinueWatching": "ادامه تماشا", "HeaderCustomDlnaProfiles": "پروفایل های سفارشی", @@ -45,15 +45,15 @@ "HeaderTypeImageFetchers": "{0} هماهنگ کننده تصویر", "HeaderUsers": "کاربران", "LabelAudioLanguagePreference": "اولویت زبان صدا:", - "LabelContentType": "نوع محتوی", - "LabelCountry": "کشور", + "LabelContentType": "نوع محتوا:", + "LabelCountry": "کشور:", "LabelCurrentPassword": "رمز فعلی:", - "LabelCustomCertificatePath": "مسیر اختصصای گواهینامه SSL:", + "LabelCustomCertificatePath": "مسیر اختصاصی گواهینامه SSL:", "LabelCustomCertificatePathHelp": "پچ به فایل PKCS #12 حاوی یک گواهینامه و کلید خصوصی است تا پشتیبانی از TLS را در یک دامنه شخصی فعال کند.", "LabelDisplayMissingEpisodesWithinSeasons": "نمایش قسمت های ناموجود در بین فصل ها", "LabelFinish": "پایان", - "LabelLanguage": "زبان", - "LabelMaxParentalRating": "حداکثر درجه سنی مجاز والدین", + "LabelLanguage": "زبان:", + "LabelMaxParentalRating": "حداکثر امتیاز سنی مجاز والدین:", "LabelNewPassword": "رمز جدید:", "LabelNewPasswordConfirm": "تایید رمز جدید:", "LabelNext": "بعدی", @@ -64,7 +64,7 @@ "LabelSecureConnectionsMode": "حالت اتصال ایمن:", "LabelSelectUsers": "انتخاب کاربران:", "LabelTimeLimitHours": "محدودیت زمان (ساعت):", - "LabelTypeMetadataDownloaders": "{0} دانلود کننده ی متاداده:", + "LabelTypeMetadataDownloaders": "{0} دانلود کننده فراداده:", "LabelYourFirstName": "اسم کوچک شما:", "LabelYoureDone": "به پایان رسید!", "LibraryAccessHelp": "انتخاب پوشه های رسانه برای اشتراک گذاری با این کاربر. مدیر سیستم میتواند با استفاده از مدیریت متاداده همه ی پوشه ها را ویرایش کند.", @@ -95,9 +95,9 @@ "TabArtists": "هنرمندان", "TabEpisodes": "قسمت ها", "TabGenres": "ژانرها", - "TabLatest": "آخرین", - "TabMetadata": "اطلاعات محتوی", - "TabMusicVideos": "موزیک ویدیو ها", + "TabLatest": "جدیدترین‌ها", + "TabMetadata": "فراداده", + "TabMusicVideos": "موزیک ویدیوها", "TabNetworks": "شبکه ها", "TabNotifications": "اعلان ها", "TabPassword": "رمز عبور", @@ -106,13 +106,13 @@ "TabProfiles": "پروفایل ها", "TabShows": "سریال ها", "TabSongs": "آهنگ ها", - "TabSuggestions": "پیشنهادات", + "TabSuggestions": "پیشنهادها", "TabUpcoming": "بزودی", "TellUsAboutYourself": "در مورد خودتان به ما بگویید", - "ThisWizardWillGuideYou": "این ویزارد برای انجام تنظیمات به شما کمک می کند. برای شروع، لطفا زبان مورد نظر خود را انتخاب فرمایید", + "ThisWizardWillGuideYou": "این عمل برای انجام تنظیمات به شما کمک می‌کند. برای شروع، لطفا زبان مورد نظر خود را انتخاب کنید.", "UserProfilesIntro": "Jellyfin دارای پشتیبانی داخلی از پروفایل کاربران می باشد. با فعال سازی هر کاربر، او می تواند تنظیمات ، وضعیت پخش و کنترل والدین خاص خودش را داشته باشد.", "WelcomeToProject": "به Jellyfin خوش آمدید!", - "WizardCompleted": "همه چیزی که فعلا می خواهیم همین است.جمع آوری اطلاعات کتابخانه های شما هم اکنون توسط Jellyfin آغاز شده است. اپلیکیشن های ما را امتحان کنید و سپس بر روی پایان کلیک کنید تا پیشخوان سرور را مشاهده نمایید.", + "WizardCompleted": "همه چیزی که فعلا می‌خواهیم همین است. جمع آوری اطلاعات کتابخانه‌های شما هم اکنون توسط Jellyfin آغاز شده است. اپلیکیشن‌های ما را امتحان کنید و سپس بر روی پایان کلیک کنید تا پیشخوان سرور را مشاهده نمایید.", "Albums": "آلبوم‌ها", "Artists": "هنرمندان", "Books": "کتاب‌ها", @@ -127,7 +127,7 @@ "HeaderFavoriteAlbums": "آلبوم‌های مورد علاقه", "HeaderFavoriteArtists": "هنرمندان مورد علاقه", "HeaderFavoriteSongs": "آهنگ‌های مورد علاقه", - "HeaderLiveTV": "پخش زنده تلویزیون", + "HeaderLiveTV": "پخش زنده", "Movies": "فیلم‌ها", "Photos": "عکس‌ها", "Playlists": "لیست‌های پخش", @@ -135,11 +135,11 @@ "Songs": "موسیقی‌ها", "Sync": "همگام‌سازی", "ValueSpecialEpisodeName": "ویژه - {0}", - "AllEpisodes": "تمام قسمت ها", - "AllLanguages": "تمام زبان ها", - "AllLibraries": "تمام کتابخانه ها", + "AllEpisodes": "تمام قسمت‌ها", + "AllLanguages": "تمام زبان‌ها", + "AllLibraries": "تمام کتابخانه‌ها", "AllowHWTranscodingHelp": "اگر فعال شود, اجازه می‌دهید تبدیل کیفیت در لحظه انجام شود. این ممکن است به کاهش کدگذاری لازم برای Jellyfin منجر بشود.", - "AllowOnTheFlySubtitleExtraction": "اجازه میدهد در لحظه زیرنویس بازشود", + "AllowOnTheFlySubtitleExtraction": "اجازه می‌دهد در لحظه زیرنویس بازشود", "Add": "افزودن", "Actor": "بازیگر", "AccessRestrictedTryAgainLater": "دسترسی در حال حاضر محدود شده است. لطفا دوباره تلاش کنید.", @@ -149,7 +149,7 @@ "ButtonSelectView": "انتخاب نما", "ButtonSelectServer": "انتخاب سرور", "ButtonSearch": "جستجو", - "ButtonScanAllLibraries": "پویش تمام کتابخانه‌ها", + "ButtonScanAllLibraries": "اسکن تمام کتابخانه‌ها", "ButtonRevoke": "ابطال", "ButtonResume": "ادامه", "ButtonRestart": "راه اندازی مجدد", @@ -163,7 +163,7 @@ "ButtonNextTrack": "ترانه پسین", "ButtonPreviousTrack": "ترانه پیشین", "ButtonPause": "مکث", - "ButtonParentalControl": "کنترل والدین", + "ButtonParentalControl": "رتبه بندی والدین", "ButtonOpen": "باز", "ButtonOff": "خاموش", "ButtonNetwork": "شبکه", @@ -212,12 +212,12 @@ "AskAdminToCreateLibrary": "از کاربر مدیر بخواهید که یک کتابخانه ایجاد کند.", "Ascending": "بالا رونده", "AsManyAsPossible": "تا حدی که ممکن است", - "AroundTime": "حدود {0}", + "AroundTime": "حدود", "Anytime": "هر زمانی", "AnyLanguage": "هر زبانی", - "AlwaysPlaySubtitles": "همیشه زیرنویس را نمایش بده", + "AlwaysPlaySubtitles": "همیشه پخش کن", "AllowFfmpegThrottling": "گلوگاه تبدیل کیفیت", - "AllChannels": "همه‌ی کانال‌ها", + "AllChannels": "همه کانال‌ها", "Alerts": "هشدارها", "Aired": "پخش شده", "AirDate": "تاریخ پخش", @@ -239,11 +239,11 @@ "EnableHardwareEncoding": "فعال سازی رمزگذاری سخت افزاری", "EnableExternalVideoPlayersHelp": "یک منوی پخش کننده ویدیوی خارجی، زمانی که شروع به پخش ویدیو می‌شود نمایش داده خواهد شد.", "EnableExternalVideoPlayers": "پخش کننده ویدیوی خارجی", - "EnableDisplayMirroring": "نمایش حالت آینه", + "EnableDisplayMirroring": "نمایش آینه", "EnableCinemaMode": "حالت سینما", "EnableBackdrops": "پشت‌زمینه‌ها", "EditSubtitles": "ویرایش زیرنویس‌ها", - "EditMetadata": "ویرایش ابرداده", + "EditMetadata": "ویرایش فراداده", "EditImages": "ویرایش عکس‌ها", "Edit": "ویرایش", "DropShadow": "سایه پشت زمینه", @@ -252,7 +252,7 @@ "Download": "بارگیری", "Down": "پایین", "DoNotRecord": "ضبط نکن", - "DisplayModeHelp": "نوع صفحه نمایشی که Jellyfin را اجرا می‌کنید را انتخاب کنید‌‌.", + "DisplayModeHelp": "سبک رابط کاربری مورد نظر خود را انتخاب کنید.", "DisplayMissingEpisodesWithinSeasons": "قسمت‌های ناموجود در فصل‌ها را نمایش بده", "DisplayInMyMedia": "نمایش در صفحه‌ی خانه", "Display": "نمایش", @@ -358,13 +358,13 @@ "HeaderLibraryFolders": "پوشه‌های کتابخانه", "HeaderLibraryAccess": "دسترسی به کتابخانه", "HeaderLibraries": "کتابخانه‌ها", - "HeaderLatestRecordings": "آخرین ضبط‌ها", - "HeaderLatestMusic": "آخرین آهنگ‌ها", - "HeaderLatestMovies": "آخرین فیلم‌ها", - "HeaderLatestMedia": "آخرین رسانه‌ها", + "HeaderLatestRecordings": "جدیدترین‌ ضبط‌ها", + "HeaderLatestMusic": "جدیدترین‌ آهنگ‌ها", + "HeaderLatestMovies": "جدیدترین‌ فیلم‌ها", + "HeaderLatestMedia": "جدیدترین‌ رسانه‌ها", "HeaderKeepSeries": "سریال ادامه دهید", "HeaderKeepRecording": "ضبط را ادامه دهید", - "HeaderItems": "موارد", + "HeaderItems": "آیتم‌ها", "HeaderInstall": "نصب", "HeaderImageSettings": "تنظیمات عکس", "HeaderIdentifyItemHelp": "یک یا بیشتر مورد برای جستجو وارد کنید. موارد را حذف کنید تا نتیجه جستجو را افزایش دهید.", @@ -415,5 +415,389 @@ "HeaderCancelSeries": "لغو سریال", "HeaderCancelRecording": "لغو ضبط", "HeaderBooks": "کتاب‌ها", - "HeaderBlockItemsWithNoRating": "موارد مسدود شده با نقص یا عدم وجود اطلاعات امتیاز:" + "HeaderBlockItemsWithNoRating": "موارد مسدود شده با نقص یا عدم وجود اطلاعات امتیاز:", + "LabelSkipIfAudioTrackPresentHelp": "این گزینه را عدم انتخاب کنید تا اطمینان حاصل کنید که همه ویدیوها فارغ از زبان صوت، زیرنویس دارند.", + "LabelSkipIfAudioTrackPresent": "اگر صدای پیش‌فرض با زبان دانلودی یکسان است پرش کن", + "LabelSkipForwardLength": "میزان رفتن به جلو:", + "LabelSkipBackLength": "میزان بازگشت به عقب:", + "LabelSkin": "پوسته:", + "LabelSize": "سایز:", + "LabelSimultaneousConnectionLimit": "محدودیت پخش همزمان:", + "LabelServerName": "نام سرور:", + "LabelServerHostHelp": "192.168.1.100:8096 یا https://myserver.com", + "LabelServerHost": "میزبان:", + "LabelSerialNumber": "شماره سریال", + "LabelSendNotificationToUsers": "ارسال اعلان به:", + "LabelSelectVersionToInstall": "نسخه مورد نظر برای نصب را انتخاب کنید:", + "LabelVersionInstalled": "{0} نصب شده است", + "EncoderPresetHelp": "یک مقدار سریع‌تر انتخاب کنید تا کارایی بهبود پیدا کند یا یک مقدار کُندتر انتخاب کنید تا کیفیت بهبود پیدا کند.", + "ShowYear": "نمایش سال", + "ShowTitle": "نمایش عنوان", + "ButtonAudioTracks": "آهنگ‌ها", + "AlbumArtist": "هنرمند آلبوم", + "Album": "آلبوم", + "HeaderAddScheduledTaskTrigger": "افزودن فعال‌ساز", + "HeaderActivity": "فعالیت‌ها", + "HeaderActiveRecordings": "ضبط‌های فعال", + "HeaderActiveDevices": "دستگاه‌های فعال", + "HeaderAccessScheduleHelp": "یک زمان‌بندی دسترسی ایجاد کنید تا دسترسی به ساعاتی مشخص محدود شود.", + "HeaderAccessSchedule": "زمان‌بندی دسترسی", + "HandledByProxy": "توسط reverse proxy مدیریت می‌شود", + "HDPrograms": "برنامه‌های HD", + "Filters": "صافی‌ها", + "FileReadError": "خطایی هنگام خواندن فایل رخ داد.", + "FileReadCancelled": "خواندن فایل لغو شد.", + "FileNotFound": "فایل پیدا نشد.", + "File": "فایل", + "FetchingData": "در حال دریافت داده‌های اضافی", + "Features": "برجسته‌ها", + "Favorite": "مورد علاقه", + "FastForward": "سریع جلو", + "Extras": "موارد اضافی", + "ExtraLarge": "فوق العاده بزرگ", + "BoxSet": "جعبه ست", + "Art": "هنر", + "Artist": "هنرمند", + "AllComplexFormats": "کلیه فرمت‌های پیچیده (ASS ، SSA ، VOBSUB ، PGS ، SUB ، IDX ، ...)", + "GuideProviderLogin": "ورود", + "Guide": "راهنما", + "GuestStar": "ستاره‌ی مهمان", + "GroupVersions": "نسخه‌های گروه", + "GroupBySeries": "گروه بندی بر اساس سریال‌ها", + "Genre": "ژانر", + "General": "عمومی", + "Fullscreen": "تمام صفحه", + "Friday": "جمعه", + "FormatValue": "قالب‌ها: {0}", + "FolderTypeUnset": "محتواهای مخلوط", + "TabMyPlugins": "افزونه‌های من", + "TabMusic": "موسیقی‌ها", + "TabMovies": "فیلم‌ها", + "TabLogs": "واقعه نگار‌ها", + "TabLiveTV": "تلویزیون زنده", + "LatestFromLibrary": "جدیدترین‌های {0}", + "Large": "بزرگ", + "LabelffmpegPath": "مسیر FFmpeg:", + "LabelZipCode": "کدپستی:", + "LabelYear": "سال:", + "LabelWeb": "وب:", + "LabelVideoResolution": "کیفیت ویدیو:", + "LabelVideo": "ویدیو", + "DashboardArchitecture": "معماری: {0}", + "DashboardOperatingSystem": "سیستم عامل: {0}", + "DashboardServerName": "سرور: {0}", + "DashboardVersionNumber": "نسخه: {0}", + "LabelVersion": "نسخه:", + "LabelValue": "مقدار:", + "LabelVaapiDevice": "دستگاه VA API:", + "LabelUsername": "نام کاربری:", + "LabelUserLibrary": "کتابخانه کاربر:", + "LabelUserAgent": "عامل کاربر:", + "LabelUser": "کاربر:", + "LabelUseNotificationServices": "استفاده از سرویس‌های زیر:", + "LabelTypeText": "متن", + "LabelType": "نوع:", + "LabelKodiMetadataEnablePathSubstitution": "فعال سازی تعویض مسیر", + "LabelKodiMetadataDateFormatHelp": "تمام تاریخ‌های موجود در فایل‌های NFO با استفاده از این قالب تجزیه می‌شوند.", + "LabelKodiMetadataDateFormat": "قالب تاریخ انتشار:", + "LabelKidsCategories": "دسته‌بندی‌های کودکان:", + "OnApplicationStartup": "هنگام شروع برنامه", + "EveryXHours": "هر {0} ساعت", + "EveryHour": "هر ساعت", + "EveryXMinutes": "هر {0} دقیقه", + "OnWakeFromSleep": "هنگام بیداری از خواب", + "WeeklyAt": "{0} در {1}", + "DailyAt": "روزانه در {0}", + "LastSeen": "آخرین بازدید {0}", + "PersonRole": "در نقش {0}", + "ListPaging": "{0}-{1} از {2}", + "Yesterday": "دیروز", + "Yes": "بلی", + "YadifBob": "Yadif Bob", + "Yadif": "Yadif", + "ValueConditions": "شرایط: {0}", + "ValueCodec": "کدک: {0}", + "ValueAudioCodec": "کدک صدا: {0}", + "ValueAlbumCount": "{0} آلبوم", + "Upload": "آپلود", + "Up": "بالا", + "Unrated": "بدون امتیاز", + "Unplayed": "پخش نشده", + "Unmute": "صدادار", + "UninstallPluginHeader": "حذف نصب افزونه", + "UninstallPluginConfirmation": "آیا اطمینان دارید که می‌خواهید {0} را حذف نصب کنید؟", + "Uniform": "یکپارچه", + "Tuesday": "سه‌شنبه", + "Transcoding": "کدگذاری", + "Trailers": "تریلرها", + "Track": "آهنگ", + "TrackCount": "{0} آهنگ", + "TitlePlayback": "پخش", + "TitleHostingSettings": "تنظیمات میزبانی", + "TitleHardwareAcceleration": "تسریع کننده سخت افزاری", + "Thursday": "پنج‌شنبه", + "Thumb": "بندانگشتی", + "ThemeVideos": "تم ویدیوها", + "ThemeSongs": "آهنگ‌های تم", + "TagsValue": "برچسب‌ها: {0}", + "Tags": "برچسب‌ها", + "TabUsers": "کاربران", + "Absolute": "کامل", + "Writer": "نویسنده", + "Whitelist": "لیست سفید", + "Wednesday": "چهارشنبه‌ها", + "Watched": "مشاهده شده", + "ViewPlaybackInfo": "مشاهده اطلاعات پخش", + "ViewArtist": "مشاهده هنرمند", + "ViewAlbum": "مشاهده آلبوم", + "VideoRange": "محدوده ویدیو", + "Vertical": "عمودی", + "ValueVideoCodec": "کدک ویدیو: {0}", + "ValueTimeLimitSingleHour": "محدودیت زمانی: 1 ساعت", + "ValueTimeLimitMultiHour": "محدودیت زمانی: {0} ساعت", + "ValueSongCount": "{0} آهنگ", + "ValueSeriesCount": "{0} سریال", + "ValueSeconds": "{0} ثانیه", + "ValueOneSong": "1 آهنگ", + "ValueOneSeries": "1 سریال", + "ValueOneMusicVideo": "1 موزیک ویدیو", + "ValueOneMovie": "1 فیلم", + "ValueOneEpisode": "1 قسمت", + "ValueOneAlbum": "1 آلبوم", + "ValueMusicVideoCount": "{0} موزیک ویدیو", + "ValueMovieCount": "{0} فیلم", + "ValueMinutes": "{0} دقیقه", + "ValueEpisodeCount": "{0} قسمت", + "ValueDiscNumber": "دیسک {0}", + "LabelImportOnlyFavoriteChannels": "محدود کردن کانال‌هایی که به عنوان مورد علاقه انتخاب شده‌اند", + "LabelDateAdded": "تاریخ اضافه شده:", + "LabelDashboardTheme": "تم داشبورد سرور:", + "LabelCustomRating": "امتیازدهی سفارشی:", + "LabelCustomDeviceDisplayName": "نام نمایشی:", + "LabelCustomCssHelp": "ظاهر سفارشی مورد نظر خود را در رابط وب اعمال کنید.", + "LabelCustomCss": "CSS سفارشی:", + "LabelCriticRating": "امتیاز منتقدان:", + "LabelCorruptedFrames": "فریم‌های خراب شده:", + "LabelImageType": "نوع عکس:", + "LabelIconMaxWidth": "حداکثر عرض آیکن:", + "LabelIconMaxHeight": "حداکثر ارتفاع آیکن:", + "LabelHttpsPort": "شماره پورت HTTPS محلی:", + "LabelHomeNetworkQuality": "کیفیت شبکه خانگی:", + "LabelHardwareAccelerationTypeHelp": "تسریع کننده سخت افزاری نیاز به پیکربندی اضافی دارد.", + "LabelSupportedMediaTypes": "نوع‌ رسانه‌های پشتیبانی شده:", + "LabelSubtitles": "زیرنویس‌ها", + "LabelSubtitlePlaybackMode": "حالت زیرنویس:", + "LabelSubtitleFormatHelp": "مثال: srt", + "LabelSubtitleDownloaders": "دانلود کننده زیرنویس:", + "LabelStreamType": "نوع پخش:", + "LabelStopping": "در حال توقف", + "LabelStopWhenPossible": "هنگامی که ممکن است متوقف شود:", + "LabelStatus": "وضعیت:", + "LabelStartWhenPossible": "هنگامی که ممکن است شروع شود:", + "LabelSportsCategories": "دسته‌بندی‌های ورزشی:", + "LabelSpecialSeasonsDisplayName": "نام نمایشی فصل مخصوص:", + "LabelSource": "منبع:", + "LabelSoundEffects": "جلوه‌های صدا:", + "LabelSortTitle": "مرتب‌سازی عنوان:", + "LabelSortOrder": "ترتیب مرتب‌سازی:", + "LabelSortBy": "مرتب‌سازی بر اساس:", + "LabelSonyAggregationFlags": "پرچم‌های جمع‌آوری سونی:", + "LabelSkipIfGraphicalSubsPresent": "صرف نظر کردن اگر ویدیو پیش از این زیرنویس چسبیده در خود دارد", + "EnableColorCodedBackgrounds": "پشت‌ زمینه‌های کدگذاری شده رنگی", + "DisplayMissingEpisodesWithinSeasonsHelp": "این مورد همچنین باید برای کتابخانه های تلویزیون در پیکربندی سرور فعال شود.", + "DisplayInOtherHomeScreenSections": "در بخش‌های صفحه اصلی مانند آخرین رسانه‌ها و ادامه تماشا نمایش بده", + "Desktop": "دسکتاپ", + "CustomDlnaProfilesHelp": "برای هدف قرار دادن یک دستگاه جدید یا سرپوش گذاشتن روی نمایه سیستم ، یک نمایه سفارشی ایجاد کنید.", + "ConfirmDeleteItems": "حذف این مورد، آن را هم از فایل سیستمی و هم از کتابخانه رسانه شما حذف می کند. آیا اطمینان دارید که می‌خواهید ادامه دهید؟", + "ConfirmDeleteItem": "حذف این مورد، آن را هم از فایل سیستمی و هم از کتابخانه رسانه شما حذف می کند. آیا اطمینان دارید که می‌خواهید ادامه دهید؟", + "AlwaysPlaySubtitlesHelp": "زیرنویس‌های متناسب با توجه به اولویت زبان بدون در نظر گرفتن زبان صوتی ویدیو پخش می شوند.", + "AllowedRemoteAddressesHelp": "لیستی از آدرس های IP یا ورودی‌های IP/‌netmask برای شبکه هایی که به آنها امکان ارتباط از راه دور داده می‌شود ، با کاما از هم جدا شدند. در صورت خالی ماندن ، تمام آدرسهای راه دور مجاز خواهند بود.", + "AllowOnTheFlySubtitleExtractionHelp": "زیرنویس های جاسازی شده را می‌توان از ویدئو ها استخراج کرد و به منظور جلوگیری از کدگذاری فیلم ، به صورت متن ساده به بازدید کننده ارسال کرد. در بعضی از سیستم‌ها این می‌تواند مدت زیادی طول بکشد و باعث شود پخش فیلم در طول فرآیند استخراج متوقف شود. این گزینه را غیرفعال کنید تا زیرنویس‌های جاسازی شده با استفاده از رمزگذاری ویدیو در حالی که به طور محلی توسط دستگاه بازدیدکننده پشتیبانی نمی‌شوند ارسال شود.", + "AdditionalNotificationServices": "برای نصب سرویس‌های اعلان اضافی، در فروشگاه افزونه‌ها جستجو کنید.", + "OptionThumbCard": "کارت بندانگشتی", + "OptionThumb": "بندانگشتی", + "OptionThursday": "پنجشنبه", + "OptionSunday": "یکشنبه", + "OptionSubstring": "زیررشته", + "OptionSpecialEpisode": "ویژه‌ها", + "OptionSaveMetadataAsHidden": "ذخیره فراداده‌ها و عکس‌ها به عنوان فایل‌های پنهان", + "OptionSaturday": "شنبه", + "OptionRuntime": "زمان اجرا", + "OptionResumable": "قابل از سرگیری", + "OptionResElement": "عنصر res", + "OptionReleaseDate": "تاریخ انتشار", + "OptionRegex": "عبارت منظم", + "OptionRandom": "تصادفی", + "OptionProtocolHttp": "HTTP", + "OptionProtocolHls": "پخش مستقیم HTTP", + "OptionProfileVideoAudio": "صوتی تصویری", + "OptionProfilePhoto": "عکس", + "OptionProfileAudio": "صدا", + "OptionPremiereDate": "تاریخ پخش", + "OptionPosterCard": "کارتِ پوستر", + "OptionPoster": "پوستر", + "OptionPlayCount": "تعداد پخش", + "OptionPlainVideoItems": "نمایش همه فیلم‌ها به عنوان موارد ویدیویی ساده", + "OptionPlainStorageFolders": "نمایش همه پوشه‌ها به عنوان پوشه‌های ذخیره سازی ساده", + "OptionParentalRating": "رتبه بندی والدین", + "OptionOnInterval": "در یک فاصله", + "BookLibraryHelp": "کتاب‌های صوتی و متنی پشتیبانی می‌شوند. {0} راهنمای نامگذاری کتاب {1} را مرور کنید.", + "TabInfo": "اطلاعات", + "TabGuide": "راهنما", + "TabFavorites": "مورد علاقه‌ها", + "TabDisplay": "نمایش", + "TabDirectPlay": "پخش مستقیم", + "TabDevices": "دستگاه‌ها", + "TabDashboard": "داشبورد", + "TabCollections": "مجموعه‌ها", + "TabCodecs": "کدک‌ها", + "TabChannels": "کانال‌ها", + "TabCatalog": "فهرست", + "TV": "تلویزیون", + "Sunday": "یکشنبه", + "TabTranscoding": "کدگذاری", + "TabTrailers": "تریلرها", + "Suggestions": "پیشنهادها", + "Subtitles": "زیرنویس‌ها", + "Studios": "استودیو‌ها", + "StopRecording": "توقف ضبط", + "Sports": "ورزش‌ها", + "SortName": "مرتب سازی نام", + "SortChannelsBy": "مرتب سازی کانال‌ها بر اساس:", + "SortByValue": "مرتب شده بر اساس {0}", + "Sort": "مرتب سازی", + "Smart": "باهوش", + "Smaller": "کوچکتر", + "Small": "کوچک", + "ButtonTogglePlaylist": "لیست پخش", + "ButtonToggleContextMenu": "بیشتر", + "TheseSettingsAffectSubtitlesOnThisDevice": "این تنظیمات روی زیرنویس‌ها در این دستگاه تأثیر می‌گذارد", + "TabStreaming": "در حال پخش", + "TabSettings": "تنظیمات", + "TabServer": "سرور", + "TabSeries": "سریال‌ها", + "TabScheduledTasks": "وظایف زمان بندی شده", + "TabResumeSettings": "ادامه", + "TabResponses": "پاسخ‌ها", + "TabRecordings": "ضبط‌ها", + "TabPlugins": "افزونه‌ها", + "TabPlaylists": "لیست‌های پخش", + "TabPlayback": "پخش", + "TabParentalControl": "رتبه بندی والدین", + "TabOther": "سایر", + "TabNfoSettings": "تنظیمات NFO", + "TabNetworking": "شبکه سازی", + "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "این تنظیمات همچنین در مورد هر پخش Chromecast که توسط این دستگاه شروع شده است اعمال می شود.", + "SmartSubtitlesHelp": "زیرنویس‌های متناسب با توجه به اولویت زبان بدون در نظر گرفتن زبان صوتی ویدیو پخش می شوند.", + "SkipEpisodesAlreadyInMyLibrary": "قسمت‌هایی که هم اکنون در کتابخانه من موجود است را ضبط نکن", + "SimultaneousConnectionLimitHelp": "حداکثر تعداد پخش‌های مجاز همزمان. ۰ را برای بدون محدودیت وارد کنید.", + "MessagePluginConfigurationRequiresLocalAccess": "برای پیکربندی این افزونه، لطفاً مستقیماً به سرور محلی خود وارد شوید.", + "MessagePleaseWait": "لطفا صبر کنید. این ممکن است چند دقیقه طول بکشد.", + "MessageNoServersAvailable": "هیچ سروری با استفاده از کشف خودکار سرور یافت نشد.", + "MessageNoPluginsInstalled": "هیچ افزونه‌ی نصب نشده است.", + "MessageNoMovieSuggestionsAvailable": "هیچ پیشنهادی برای فیلم در دسترس نیست. شروع به تماشای و رتبه بندی فیلم‌های خود بکنید ، و سپس دوباره به دیدن پیشنهاد‌های خود بیایید.", + "MessageNoAvailablePlugins": "افزونه‌ای موجود نیست.", + "MessageItemsAdded": "آیتم‌ها اضافه شدند.", + "MessageItemSaved": "آیتم ذخیره شد.", + "MessageUnauthorizedUser": "در حال حاضر مجاز به دسترسی به سرور نیستید. لطفا برای اطلاعات بیشتر با مدیر سرور خود تماس بگیرید.", + "MessageInvalidUser": "نام کاربری یا گذرواژه نامعتبر است. لطفا دوباره تلاش کنید.", + "MessageInvalidForgotPasswordPin": "کد پین نامعتبر یا منقضی شده وارد شد. لطفا دوباره تلاش کنید.", + "MessageInstallPluginFromApp": "این افزونه باید از داخل برنامه‌ای که قصد استفاده از آن را دارید نصب شود.", + "PasswordResetHeader": "بازنشانی گذرواژه", + "PasswordResetConfirmation": "آیا واقعا تمایل به بازنشانی گذرواژه دارید؟", + "PasswordResetComplete": "گذرواژه بازنشانی شد.", + "PasswordMatchError": "گذرواژه و تکرار گذرواژه باید یکسان باشند.", + "PackageInstallFailed": "{0} (نسخه {1}) نصب به مشکل برخورد.", + "PackageInstallCompleted": "{0} (نسخه {1})نصب به پایان رسید.", + "PackageInstallCancelled": "{0} ( نسخه {1})نصب لغو شد.", + "Overview": "بررسی اجمالی", + "OtherArtist": "هنرمند دیگر", + "OriginalAirDateValue": "زمان پخش اصلی : {0}", + "OptionWeekly": "هفتگی", + "OptionWeekends": "آخر هفته ها", + "OptionWeekdays": "روز های هفته", + "OptionWednesday": "چهارشنبه", + "OptionWakeFromSleep": "از خواب بیدار شدن", + "OptionUnairedEpisode": "قسمت های پخش نشده", + "OptionTvdbRating": "نمره TVDB", + "OptionTuesday": "سه شنبه", + "OptionTrackName": "نام ترک", + "OptionRequirePerfectSubtitleMatchHelp": "نتیجه کامل زیرنویس ها را به صورتی فیلتر می کند که فقط مواردی را که دقیقا با فایل تصویری شما آزمایش و تأیید شده اند ،شامل شود. حذف این گزینه احتمال بارگیری زیرنویس ها را افزایش می دهد ، اما شانس متن زیرنویس ناهماهنگ یا غلط نیز افزایش می یابد.", + "ServerNameIsShuttingDown": "سرور جلی فین - {0} در حال خاموش شدن می باشد.", + "ServerNameIsRestarting": "سرور جلی فین - {0} در حال راه اندازی مجدد می باشد.", + "SeriesYearToPresent": "{0}- در حال حاضر", + "SeriesSettings": "تنظیمات سریال", + "SeriesRecordingScheduled": "ضبط کردن سریال زمانبندی شد.", + "SeriesDisplayOrderHelp": "قسمت ها را بر اساس تاریخ پخش شدن، ترتیب دی وی دی یا فقط شماره مرتب کنید.", + "SeriesCancelled": "سریال متوقف شده است.", + "Series": "سریال ها", + "SelectAdminUsername": "لطفا یک نام کاربری برای حساب مدیر انتخاب کنید.", + "Season": "فصل", + "SearchResults": "نتایج جستجو", + "SearchForSubtitles": "زیرنویس ها را جستجو کنید", + "SearchForMissingMetadata": "فراداده های گم شده را جستجو کنید", + "SearchForCollectionInternetMetadata": "در اینترنت برای پیدا کردن فراداده و عکس جستجو کنید", + "Search": "جستجو کردن", + "Screenshots": "نماگرفت ها", + "Screenshot": "نماگرفت", + "Schedule": "برنامه زمانی", + "ScanLibrary": "کتابخانه را اسکن کنید", + "ScanForNewAndUpdatedFiles": "فایل های جدید و به روز رسانی شده را اسکن کنید", + "ChannelAccessHelp": "کانال هایی که قصد اشتراک گذاری با این کاربر را دارید انتخاب کنید. مدیران توانایی ویرایش همه ی کانال ها را با استفاده از مدیریت فراداده دارند.", + "ChangingMetadataImageSettingsNewContent": "تغییر تنظیمات دانلود فراداده ها و عکس ها فقط بر روی محتواهای جدیدی که به کتابخانه اضافه می شوند، اعمال می شوند. برای اعمال تغییرات بر روی محتوای موجود، شما باید فراداده را به صورت دستی به روزرسانی کنید.", + "Refresh": "به‌روز‌رسانی", + "Recordings": "ضبط شده ها", + "RecordingScheduled": "ضبط برنامه ریزی شد.", + "RecordingPathChangeMessage": "با تغییر محل ذخیره فایل های ضبط شده، فایل های موجود به صورت خودکار منتقل نمی شوند. در صورت نیاز، شما باید خودتان این کار را انجام دهید.", + "RecordingCancelled": "ضبط شدن لغو شد.", + "RecordSeries": "ضبط کردن سریال ها", + "Record": "ضبط کردن", + "RecommendationStarring": "با هنرمندی {0}", + "RecommendationDirectedBy": "کارگردانی شده توسط {0}", + "RecommendationBecauseYouWatched": "برای اینکه تو {0} مشاهده کردی", + "RecommendationBecauseYouLike": "برای اینکه تو {0} را نیز دوست داری", + "RecentlyWatched": "اخیرا مشاهده شده", + "Rate": "ارزیابی کن", + "Raised": "مطرح شده", + "QueueAllFromHere": "همه را از اینجا در صف قرار بده", + "Quality": "کیفیت", + "Programs": "برنامه ها", + "ProductionLocations": "محل تولید", + "Producer": "تولید کننده", + "Primary": "اصلی", + "Previous": "قبلی", + "Premieres": "برتر ها", + "Premiere": "برتر", + "PreferEmbeddedEpisodeInfosOverFileNames": "اطلاعات تعبیه شده در فراداده را به اسم فایل ترجیح بده", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "این از اطلاعات قسمت در فراداده های تعبیه شده در صورت موجود استفاده می کند.", + "PreferEmbeddedTitlesOverFileNamesHelp": "این عنوان نمایش را به صورت پیش فرض تعیین می کند، زمانی که فراداده اینترنتی یا محلی موجود نباشند.", + "PreferEmbeddedTitlesOverFileNames": "عنوان های تعبیه شده را به نام فایل ترجیح بده", + "PluginInstalledMessage": "افزونه با موفقیت نصب شد. برای اعمال تغییرات سرور جلیفین نیاز به بارگذاری مجدد دارد.", + "PleaseSelectTwoItems": "لطفا حداقل دو مورد را انتخاب کنید.", + "PleaseRestartServerName": "لطفا سرور جلیفین را دوباره بارگذاری کنید - {0}.", + "PleaseEnterNameOrId": "لطفا یک نام یا شناسه خارجی را وارد کنید.", + "PleaseConfirmPluginInstallation": "لطفا با کلیک بر روی OK تایید کنید که شما متن بالا را خوانده اید و می خواهید که افزونه را نصب کنید.", + "PleaseAddAtLeastOneFolder": "لطفا حداقل یک پوشه به این کتابخانه با کلیک بر روی \"افزودن\" اضافه کنید.", + "Played": "اجرا شده", + "PlaybackErrorNoCompatibleStream": "سرویس گیرنده با مدیا سازگاری ندارد و سرور یک فرمت سازگار را ارسال نمی کند.", + "PlayNextEpisodeAutomatically": "قسمت بعدی را به صورت خودکار اجرا کن", + "PlayNext": "بعدی را اجرا کن", + "PlayFromBeginning": "از ابتدا اجرا کن", + "PlaybackData": "اجرای مجدد داده", + "PlayAllFromHere": "همه را از اینجا اجرا کن", + "Play": "اجرا کردن", + "PinCodeResetConfirmation": "آیا مطمئن هستید که می خواهید کد پین را باز نشانی کنید؟", + "PinCodeResetComplete": "کد پین بازنشانی شد.", + "PictureInPicture": "تصویر در تصویر", + "Person": "فرد", + "PerfectMatch": "همتای کامل", + "People": "افراد", + "MillisecondsUnit": "میلی‌ثانیه", + "LabelSyncPlayTimeOffset": "اختلاف زمانی با سرور:", + "LabelSeriesRecordingPath": "مسیر ضبط سریال‌ها (اختیاری):", + "LabelSelectFolderGroups": "به طور خودکار محتواهای پوشه‌های زیر را به فیلم ، موسیقی و تلویزیون گروه بندی شود:", + "LabelSeasonNumber": "شماره فصل:", + "ConfigureDateAdded": "تنظیم کنید که چگونه تاریخ اضافه شده در داشبورد سرور Jellyfin تحت تنظیمات کتابخانه تعیین می‌شود", + "CinemaModeConfigurationHelp": "حالت سینما تجربه تئاتر گونه را مستقیم به اتاق نشیمن شما می‌آورد با قابلیت پخش تریلرها و پیش نمایش‌ها قبل از سایر ویژگی‌های اصلی.", + "LaunchWebAppOnStartup": "نمای وب هنگامی که سرور آغاز به کار می‌کند باز بشود" } diff --git a/src/strings/fi.json b/src/strings/fi.json index 55c5a1fbcd..c3ca9d81d9 100644 --- a/src/strings/fi.json +++ b/src/strings/fi.json @@ -4,7 +4,7 @@ "ButtonAddUser": "Lisää Käyttäjä", "ButtonCancel": "Peruuta", "ButtonDeleteImage": "Poista Kuva", - "ButtonResetPassword": "Uusi Salasana", + "ButtonResetPassword": "Nollaa salasana", "ButtonSave": "Tallenna", "ButtonSignOut": "Sign out", "Delete": "Poista", @@ -15,41 +15,41 @@ "FileNotFound": "Tiedostoa ei löydy.", "FileReadCancelled": "Tiedoston luku on peruutettu.", "FileReadError": "Virhe tiedoston luvun aikana.", - "FolderTypeTvShows": "TV", + "FolderTypeTvShows": "TV-sarjat", "HeaderCreatePassword": "Luo Salasana:", "HeaderParentalRating": "Parental Rating", - "HeaderSeries": "Jaksot", + "HeaderSeries": "Sarjat", "HeaderYear": "Year:", "LabelAudioLanguagePreference": "Äänen ensisijainen kieli:", "LabelConfigureSettings": "Muuta asetuksia", "LabelCountry": "Maa:", "LabelCurrentPassword": "Tämän hetkinen salsana:", - "LabelDisplayMissingEpisodesWithinSeasons": "Näytä puuttuvat jaksot tuotantokausissa", + "LabelDisplayMissingEpisodesWithinSeasons": "Näytä puuttuvat jaksot kausien sisällä", "LabelDownloadInternetMetadata": "Lataa kuvamateriaali ja metadata internetistä", "LabelFinish": "Valmis", "LabelFolderType": "Kansion tyyppi:", "LabelLanguage": "Kieli:", - "LabelMaxParentalRating": "Suurin sallittu vanhempien arvostelu:", + "LabelMaxParentalRating": "Suurin sallittu ikäraja:", "LabelNewPassword": "Uusi salasana:", "LabelNewPasswordConfirm": "Uuden salasanan varmistus:", "LabelNext": "Seuraava", "LabelPrevious": "Edellinen", - "LabelSaveLocalMetadata": "Tallenna kuvamateriaali ja metadata media kansioihin.", + "LabelSaveLocalMetadata": "Tallenna kuvamateriaali mediakansioihin", "LabelSaveLocalMetadataHelp": "Kuvamateriaalin ja metadatan tallentaminen suoraan kansioihin missä niitä on helppo muuttaa.", "LabelSubtitleLanguagePreference": "Tekstityksien ensisijainen kieli:", "LabelUnairedMissingEpisodesWithinSeasons": "Näytä julkaisemattomat jaksot tuotantokausissa", - "LabelYourFirstName": "Sinun ensimmäinen nimi:", - "LabelYoureDone": "Olet valmis!", - "LibraryAccessHelp": "Valitse media kansiot jotka haluat jakaa tämän käyttäjän kanssa. Järjestelmänvalvoja pystyy muokkaamaan kaikkia kansioita käyttäen metadata hallintaa.", - "MaxParentalRatingHelp": "Suuremman arvosanan takia, sisältö tulla piilottamaan käyttäjältä.", - "MoreUsersCanBeAddedLater": "Käyttäjiä voi lisätä lisää myöhemmin Dashboardista", + "LabelYourFirstName": "Etunimesi:", + "LabelYoureDone": "Valmista!", + "LibraryAccessHelp": "Valitse kirjastot, jotka haluat jakaa tämän käyttäjän kanssa. Järjestelmänvalvoja pystyy muokkaamaan kaikkia kansioita käyttäen metadatan hallintatyökalua.", + "MaxParentalRatingHelp": "Suuremman luokituksen sisältö piilotetaan käyttäjältä.", + "MoreUsersCanBeAddedLater": "Käyttäjiä voidaan lisätä myöhemmin lisää päänäkymästä.", "NoPluginsInstalledMessage": "Sinulla ei ole mitään lisäosia asennettuna.", "OptionRelease": "Virallinen Julkaisu", - "ParentalRating": "Parental Rating", + "ParentalRating": "Ikäraja", "Password": "Salasana", - "PasswordMatchError": "Salasana ja salasanan vahvistuksen pitää olla samat.", - "PasswordResetComplete": "Salasana on palauttettu.", - "PasswordResetConfirmation": "Oletko varma, että haluat palauttaa salasanan?", + "PasswordMatchError": "Salasanan ja salasanan vahvistuksen on oltava samat.", + "PasswordResetComplete": "Salasana on nollattu.", + "PasswordResetConfirmation": "Haluatko varmasti nollata salasanan?", "PasswordSaved": "Salasana tallennettu.", "Save": "Tallenna", "SettingsSaved": "Asetukset tallennettu.", @@ -59,7 +59,7 @@ "TabProfile": "Profiili", "TabProfiles": "Profiilit", "TellUsAboutYourself": "Kerro meille itsestäsi", - "ThisWizardWillGuideYou": "Tämä työkalu auttaa sinua asennus prosessin aikana. loittaaksesi valitse kieli.", + "ThisWizardWillGuideYou": "Tämä työkalu auttaa sinua asennusprosessin aikana. Valitse kieli aloittaaksesi.", "UninstallPluginConfirmation": "Oletko varma, että haluat poistaa {0}?", "UninstallPluginHeader": "Poista Lisäosa", "Users": "Käyttäjät", @@ -70,7 +70,6 @@ "AddToCollection": "Lisää kokoelmaan", "AddToPlayQueue": "Lisää toistojonoon", "AddToPlaylist": "Lisää toistolistalle", - "AddUserByManually": "Lisää paikallinen käyttäjä lisäämällä käyttäjän tiedot manuaalisesti.", "AddedOnValue": "Lisätty {0}", "AdditionalNotificationServices": "Selaa lisäosakatalogia asentaaksesi lisää ilmoituspalveluita.", "AirDate": "Ensiesityspäivä", @@ -88,35 +87,32 @@ "AddGuideProviderHelp": "Lisää lähde ohjelmaoppaalle.", "AddItemToCollectionHelp": "Lisää nimikkeitä etsimällä niitä ja käyttämällä hiiren oikeaa nappia tai valikkoa lisätäksesi ne kokoelmaan.", "Aired": "Esityspäivä", - "AllowHWTranscodingHelp": "Salli virittimen muuntaa bittivirtaa lennossa. Tämä voi vähentää tarvetta muunnokseen palvelimella.", + "AllowHWTranscodingHelp": "Salli virittimen muuntaa bittivirtaa lennossa. Tämä voi vähentää muunnoksen tarvetta Jellyfin-palvelimella.", "AllowMediaConversion": "Salli median muunto", "AllowMediaConversionHelp": "Salli tai kiellä pääsy median muunnostoimintoon.", "AllowOnTheFlySubtitleExtractionHelp": "Sisäiset tekstitykset voidaan lähettää päätelaitteille ilmitekstinä, jotta videota ei tarvitsisi uudelleenkoodata. Joissain järjestelmissä tämä voi viedä paljon aikaa ja aiheuttaa toiston pysähtymisen purun ajaksi. Poista tämä käytöstä polttaaksesi tekstiykset suoraan videoon, mikäli päätelaite ei tue tekstityksiä.", "AllowRemoteAccess": "Salli etäyhteydet tähän Jellyfin palvelimeen.", "AllowRemoteAccessHelp": "Jos merkki puuttuu, kaikki ulkopuoliset yhteydet estetään.", - "AllowSeasonalThemes": "Salli automaattiset vuodenaikateemat", - "AllowSeasonalThemesHelp": "Jos asetettu, vuodenaikateemat satunnaisesti yliajavat teema-asetuksesi.", "AllowedRemoteAddressesHelp": "Pilkuilla eroteltu lista IP-osoitteista tai IP/verkonpeite merkinnöistä verkoille, joille sallitaan etäyhteys palvelimeen. Tyhjäksi jätetty lista tarkoittaa, että kaikki osoitteet sallitaan.", "AlwaysPlaySubtitles": "Näytä aina tekstitykset", "AlwaysPlaySubtitlesHelp": "Oletuskieliasetusta vastaava tekstitys otetaan käyttöön ääniraidan kielestä huolimatta.", "AnamorphicVideoNotSupported": "Anamorfinen video ei ole tuettu", - "AndroidUnlockRestoreHelp": "Palauttaaksesi aikaisemman ostoksesi, varmista, että olet kirjautuneena samalla Google (tai Amazon) tunnuksella, jolla teit alkuperäisen oston. Varmista, että sovelluskauppa on päällä eikä sitä ole rajoitettu vanhempien lukolla. Varmista myös, että sinulla on toimiva internet -yhteys. Sinun tarvitsee tehdä taämä vain kerran.", - "AnyLanguage": "Mikä tahansa kieli", + "AnyLanguage": "Mikä tahansa", "Anytime": "Milloin tahansa", "AroundTime": "Noin {0}", "Art": "Taide", - "Artists": "Esiintyjät", + "Artists": "Artistit", "AsManyAsPossible": "Niin monta kuin mahdollista", "Ascending": "Nousevassa järjestyksessä", "AspectRatio": "Kuvasuhde", "AttributeNew": "Uusi", "Audio": "Ääni", "AuthProviderHelp": "Valitse todentamispalvelu, jota käytetään tämän käyttäjän salasanan todentamisessa.", - "Auto": "Automaattinen", + "Auto": "Auto", "AutoBasedOnLanguageSetting": "Automaattinen (perustuu kieliasetukseen)", "Backdrop": "Tausta", "Backdrops": "Taustat", - "Banner": "Juliste", + "Banner": "Lippu", "BirthDateValue": "Syntynyt: {0}", "BirthLocation": "Syntymäpaikka", "BirthPlaceValue": "Syntymäpaikka: {0}", @@ -145,9 +141,9 @@ "ButtonEdit": "Muokkaa", "ButtonEditImages": "Muokkaa kuvia", "ButtonEditOtherUserPreferences": "Muokkaa tämän käyttäjän profiilia, kuvaa ja henkilökohtaisia asetuksia.", - "ButtonFilter": "Suodin", - "ButtonForgotPassword": "Unohdin salasanani", - "ButtonFullscreen": "Täysi kuvaruutu", + "ButtonFilter": "Suodata", + "ButtonForgotPassword": "Unohtuiko salasana", + "ButtonFullscreen": "Kokonäyttötila", "ButtonGotIt": "Selvä", "ButtonGuide": "Opas", "ButtonHelp": "Apua", @@ -163,8 +159,8 @@ "ButtonNextTrack": "Seuraava raita", "ButtonOff": "Pois päältä", "ButtonOk": "Ok", - "ButtonOpen": "Avoin", - "BurnSubtitlesHelp": "Määrittää jos palvelimen pitäisi upottaa tekstitykset suoraan videotiedostoon muuntamisvaiheessa tekstitysformaatista riippuen. Upottamisen välttäminen parantaa palvelimen suorituskykyä. Valitse Automaattinen upottaaksesi sekä kuvapohjaiset- (esim. VOBSUB, PGS, SUB/IDX, jne.) että ASS/SSA tekstitysmuodot", + "ButtonOpen": "Avaa", + "BurnSubtitlesHelp": "Määrittää mikäli palvelimen pitäisi polttaa tekstitykset suoraan videoon muunnoksen aikana riippuen tekstitysten formaatista. Tekstitysten polttamisen välttäminen parantaa palvelimen suorituskykyä. Valitse Automaattinen polttaaksesi sekä kuva- (esim. VOBSUB, PGS, SUB/IDX, jne.) että tekstipohjaiset (ASS/SSA) formaatit.", "ButtonParentalControl": "Lapsilukko", "ButtonPause": "Tauko", "ButtonPlay": "Toista", @@ -249,14 +245,14 @@ "DisplayInOtherHomeScreenSections": "Näytä kotinäytöllä osastoja kuten viimeisin media ja jatka katselua", "DisplayMissingEpisodesWithinSeasons": "Näytä puuttuvat jaksot tuotantokausissa", "DisplayMissingEpisodesWithinSeasonsHelp": "Tämän pitää aktivoida TV-kirjastoille myös palvelimen asetuksissa.", - "DisplayModeHelp": "Valitse näyttölaitteen tyyppi jolla pyörität Jellyfiniä.", + "DisplayModeHelp": "Valitse ulkonäkö, jonka haluat käyttöliittymälle.", "DoNotRecord": "Älä tallenna", "Down": "Alas", "Download": "Lataa", "DownloadsValue": "{0} latausta", "DrmChannelsNotImported": "Kanavia joissa on tekijänoikeusesto-ohjelmia, ei ladata.", - "DropShadow": "Tiputa varjo", - "EasyPasswordHelp": "Sinun helppoa PIN-koodia käytetään offline-käytössä tuetuissa Jellyfin-sovelluksissa, ja voi myös nopeuttaa lan yhteyden kautta kirjautumista.", + "DropShadow": "Varjostus", + "EasyPasswordHelp": "Helppoa PIN-koodiasi käytetään offline-käyttöä tukevissa sovelluksissa, ja sitä voidaan myös käyttää verkossa kirjautumiseen.", "Edit": "Muokkaa", "EditImages": "Muokkaa kuvia", "EditMetadata": "Muokkaa metadataa", @@ -267,7 +263,7 @@ "ButtonAddImage": "Lisää kuva", "Movies": "Elokuvat", "HeaderNextUp": "Seuraavaksi", - "HeaderLiveTV": "TV-lähetykset", + "HeaderLiveTV": "Live-TV", "HeaderFavoriteSongs": "Lempikappaleet", "HeaderFavoriteShows": "Lempisarjat", "HeaderFavoriteEpisodes": "Lempijaksot", @@ -284,16 +280,16 @@ "ValueSpecialEpisodeName": "Erikois - {0}", "Sync": "Synkronoi", "Songs": "Kappaleet", - "Shows": "Ohjelmat", + "Shows": "Sarjat", "CopyStreamURLSuccess": "Osoite kopioitu onnistuneesti.", - "DeathDateValue": "Kuoli: {0}", - "CustomDlnaProfilesHelp": "Luo mukautettu profiili uutta laitetta varten, tai ohita järjestelmäprofiili.", + "DeathDateValue": "Kuoli: {}", + "CustomDlnaProfilesHelp": "Luo uusi profiili kohdistaaksesi uuteen laitteeseen tai ohittaaksesi järjestelmäprofiilin.", "EnableBackdrops": "Taustat", "ErrorAddingMediaPathToVirtualFolder": "Media-polkua lisätessä ilmeni ongelma. Varmista, että polku on kirjoitettu oikein ja Jellyfin Palvelimella pääsy sijaintiin.", "Episodes": "Jaksot", "EndsAtValue": "Päättyy {0}", "Ended": "Päättynyt", - "EnableThemeSongsHelp": "Soita tunnuslaulut taustalla, selatessasi kirjastoa.", + "EnableThemeSongsHelp": "Soita tunnussäveliä taustalla selatessasi kirjastoa.", "EnableThemeSongs": "Tunnuslaulut", "EnableStreamLoopingHelp": "Laita tämä päälle, jos suoratoistot sisältävät vain muutaman sekuntin verran dataa jota tarvitsee pyytää jatkuvasti. Tämän päälle laittaminen ilman toiminnon tarvetta voi aiheuttaa ongelmia.", "EnablePhotosHelp": "Kuvat tunnistetaan ja näytetään muiden media-tiedostojen ohessa.", @@ -307,11 +303,11 @@ "EnableBackdropsHelp": "Näytä taustat tietyillä sivuilla selatessasi kirjastoa.", "EnableExternalVideoPlayersHelp": "Videota soitettaessa näytetään erillinen valikko.", "Depressed": "Painettu", - "CopyStreamURLError": "Verkko-osoitteen kopioinnissa tapahtui virhe.", + "CopyStreamURLError": "Osoitteen kopioidessa tapahtui virhe.", "ButtonSplit": "jaa", "AskAdminToCreateLibrary": "Pyydä järjestelmän ylläpitäjää luomaan kirjasto.", - "EnableStreamLooping": "Auto-toista suoralähetykset", - "EnableNextVideoInfoOverlayHelp": "Videon lopussa, näytä soittolistassa seuraavaksi toistettavan videon tiedot.", + "EnableStreamLooping": "Looppaa suoralähetykset", + "EnableNextVideoInfoOverlayHelp": "Näytä videon lopussa tietoja seuraavasta videosta soittolistalla.", "ClientSettings": "Pääte-asetukset", "AllowFfmpegThrottlingHelp": "Kun uudelleenkoodaus tai remux ehtii tarpeeksi toiston edelle, keskeytä laskenta jotta laskentaresursseja kuluu vähemmän. Tämä on hyödyllistä jos katselet hyppimättä eri kohtiin. Älä käytä jos toiston kanssa ilmenee ongelmia.", "AllowFfmpegThrottling": "Rajoita uudelleenkoodaus", @@ -319,5 +315,935 @@ "ErrorAddingXmlTvFile": "XMLTV-tiedostoa käyttäessä tapahtui virhe. Varmista, että tiedosto on olemassa ja kokeile uudestaan.", "ErrorAddingTunerDevice": "Viritintä lisätessä ilmeni ongelma. Varmista, että se on kytketty oikein ja kokeile uudestaan.", "EnableThemeVideosHelp": "Soita tunnusvideoita taustalla, selatessasi kirjastoa.", - "EnableThemeVideos": "Teeman videot" + "EnableThemeVideos": "Tunnusvideot", + "AlbumArtist": "Albumin Artisti", + "Album": "Albumi", + "Played": "Toistetut", + "PlayFromBeginning": "Toista alusta", + "PlayNext": "Toista seuraava", + "Play": "Toista", + "PinCodeResetConfirmation": "Haluatko varmasti nollata PIN-koodin?", + "People": "Ihmiset", + "PasswordResetHeader": "Nollaa salasana", + "OriginalAirDateValue": "Alkuperäinen esityspäivä: {0}", + "OptionWeekly": "Viikottainen", + "OptionWeekends": "Viikonloput", + "OptionWeekdays": "Arkipäivät", + "OptionTvdbRating": "TVDB luokitus", + "OptionTrackName": "Raidan nimi", + "OptionThumbCard": "Pienoiskuvakortti", + "OptionThumb": "Pienoiskuva", + "OptionSubstring": "Substring", + "OptionSpecialEpisode": "Erikoisjaksot", + "OptionSaveMetadataAsHidden": "Tallenna metadata ja kuvat piilotettuina tiedostoina", + "OptionRuntime": "Kesto", + "OptionResumable": "Jatkettavissa oleva", + "OptionResElement": "res element", + "OptionReleaseDate": "Julkaisupäivä", + "OptionRegex": "Regex", + "OptionRandom": "Satunnainen", + "OptionProtocolHttp": "HTTP", + "OptionProtocolHls": "HTTP Suoratoisto", + "OptionProfileVideoAudio": "Video Audio", + "OptionProfileVideo": "Video", + "OptionProfilePhoto": "Kuva", + "OptionProfileAudio": "Audio", + "OptionPremiereDate": "Ensi-iltapäivä", + "OptionPosterCard": "Julistekortti", + "OptionPoster": "Juliste", + "OptionPlayCount": "Toistokerrat", + "OptionPlayed": "Toistettu", + "OptionOnAppStartup": "Käynnistyksen yhteydessä", + "OptionNew": "Uusi...", + "OptionNameSort": "Nimi", + "OptionMonday": "Maanantai", + "OptionMissingEpisode": "Puuttuvat jaksot", + "OptionMax": "Maksimi", + "OptionList": "Lista", + "OptionLikes": "Likes", + "OptionIsSD": "SD", + "OptionIsHD": "HD", + "OptionImdbRating": "IMDb Luokitus", + "OptionHomeVideos": "Kuvat", + "OptionHideUser": "Piilota tämä käyttäjä kirjautumisnäkymästä", + "OptionHasTrailer": "Traileri", + "OptionHasThemeVideo": "Tunnusvideo", + "OptionHasThemeSong": "Tunnuskappale", + "OptionHasSubtitles": "Tekstitykset", + "OptionHasSpecialFeatures": "Erikoisominaisuudet", + "OptionFriday": "Perjantai", + "OptionFavorite": "Suosikit", + "OptionExtractChapterImage": "Ota käyttöön kappalekuvien luonti", + "OptionExternallyDownloaded": "Ulkoinen lataus", + "OptionEveryday": "Joka päivä", + "OptionEnded": "Loppuneet", + "OptionEnableM2tsMode": "Ota käyttöön M2ts tila", + "OptionEnableForAllTuners": "Ota käyttöön kaikille viritinlaitteille", + "OptionEnableAccessToAllLibraries": "Salli pääsy kaikkiin kirjastoihin", + "OptionEnableAccessToAllChannels": "Salli pääsy kaikille kanaville", + "OptionEnableAccessFromAllDevices": "Salli pääsy kaikista laitteista", + "OptionDvd": "DVD", + "OptionDownloadThumbImage": "Pienoiskuva", + "OptionDownloadPrimaryImage": "Ensisijainen", + "OptionDownloadMenuImage": "Valikko", + "OptionDownloadLogoImage": "Logo", + "OptionDownloadImagesInAdvance": "Lataa kuvat etukäteen", + "OptionDislikes": "Disliket", + "OptionCustomUsers": "Mukautettu", + "OptionCriticRating": "Kriitikoiden luokitus", + "OptionContinuing": "Jatkuvat", + "OptionCommunityRating": "Yhteisön luokitus", + "OptionBlockLiveTvChannels": "Live-TV kanavat", + "OptionBanner": "Lippu", + "OnlyForcedSubtitlesHelp": "Vain pakotetuiksi merkityt tekstitykset ladataan.", + "OnlyImageFormats": "Vain kuvaformaatit (VOBSUB, PGS, SUB)", + "OnlyForcedSubtitles": "Vain pakotetut", + "NoSubtitlesHelp": "Tekstityksiä ei ladata oletuksena. Ne voidaan silti kytkeä päälle manuaalisesti toiston aikana.", + "News": "Uutiset", + "Never": "Ei koskaan", + "MessageReenableUser": "Ottaaksesi uudelleen käyttöön, katso alempaa", + "MessagePluginConfigurationRequiresLocalAccess": "Kirjaudu suoraan paikalliselle palvelimellesi muokataksesi tätä liitännäistä.", + "MessagePleaseEnsureInternetMetadata": "Varmista, että metadatan lataus on käytössä.", + "MessageNoServersAvailable": "Automaattisen palvelintunnistuksen avulla ei löydy palvelimia.", + "MessageUnauthorizedUser": "Sinulla ei ole lupaa käyttää palvelinta tällä hetkellä. Ota yhteyttä palvelimen järjestelmänvalvojaan saadaksesi lisätietoja.", + "MessageInvalidForgotPasswordPin": "PIN-koodi on kelpaa tai vanhentunut. Yritä uudelleen.", + "MessageImageTypeNotSelected": "Valitse kuvatyyppi pudotusvalikosta.", + "MessageImageFileTypeAllowed": "Vain JPEG ja PNG tiedostomuotoja tuetaan.", + "MessageContactAdminToResetPassword": "Ota yhteyttä järjestelmänvalvojaan nollataksesi salasanasi.", + "MessageConfirmShutdown": "Haluatko varmasti sammuttaa palvelimen?", + "LabelUserLibrary": "Käyttäjän kirjasto:", + "LabelTranscodingProgress": "Transkoodauksen edistyminen:", + "LabelTranscodingFramerate": "Transkoodauksen ruudunpäivitysnopeus:", + "LabelTranscodes": "Transkoodaukset:", + "LabelTrackNumber": "Raidan numero:", + "LabelTitle": "Nimi:", + "LabelTagline": "Tunnisterivi:", + "LabelSubtitlePlaybackMode": "Tekstitystila:", + "LabelSortOrder": "Lajittelujärjestys:", + "LabelSerialNumber": "Sarjanumero", + "LabelSendNotificationToUsers": "Lähetä ilmoitus:", + "LabelSelectVersionToInstall": "Valitse asennettava versio:", + "LabelPublicHttpsPortHelp": "Paikalliseen HTTPS-porttiin liitettävä julkisen portin numero.", + "LabelPublicHttpPortHelp": "Paikalliseen HTTP-porttiin liitettävä julkisen portin numero.", + "LabelPreferredDisplayLanguage": "Ensisijainen näyttökieli:", + "LabelOriginalTitle": "Alkuperäinen nimi:", + "LabelOriginalAspectRatio": "Alkuperäinen kuvasuhde:", + "LabelEnableAutomaticPortMapHelp": "Yritä automaattisesti yhdistää julkinen ja paikallinen portti UPnP:n kautta. Tämä ei välttämättä toimi kaikkien reitittimien kanssa. Muutokset tulevat voimaan vasta palvelimen uudelleenkäynnistyksen yhteydessä.", + "LabelEnableAutomaticPortMap": "Salli reitittimen porttien automaattinen avaaminen (UPnP)", + "LabelDownloadLanguages": "Latauskielet:", + "LabelDisplaySpecialsWithinSeasons": "Näytä erityiset jaksot kausien sisällä, jolloin ne ilmestyivät", + "LabelDisplayOrder": "Näyttöjärjestys:", + "LabelDisplayName": "Näyttönimi:", + "LabelDisplayMode": "Näyttötila:", + "LabelDateTimeLocale": "Päivämäärä ja aika:", + "LabelCustomRating": "Mukautettu luokitus:", + "LabelCustomDeviceDisplayName": "Näyttönimi:", + "LabelCustomCss": "Mukautettu CSS:", + "LabelCertificatePassword": "Sertifikaatin salasana:", + "LabelAudio": "Audio", + "LabelArtistsHelp": "Erota useita käyttämällä ;", + "LabelAppNameExample": "Esimerkiksi: Sickbeard, Sonarr", + "LabelAppName": "Sovelluksen nimi", + "LabelAllowedRemoteAddressesMode": "Etä-IP-osoitesuodattimen tila:", + "LabelAllowedRemoteAddresses": "Etä-IP-osoitesuodatin:", + "LabelAllowServerAutoRestartHelp": "Palvelin käynnistyy uudelleen vain hiljaisina aikoina, kun yksikään käyttäjä ei ole aktiivinen.", + "LabelAllowServerAutoRestart": "Salli palvelimen automaattinen uudelleenkäynnistys päivitysten asentamiseksi", + "LabelAllowHWTranscoding": "Salli laitteistolla transkoodaus", + "LabelAlbumArtMaxWidth": "Albumin kuvan maksimileveys:", + "LabelAlbumArtMaxHeight": "Albumin kuvan maksimikorkeus:", + "LabelAbortedByServerShutdown": "(Keskeytetty palvelimen sammutuksen takia)", + "Identify": "Tunnista", + "Horizontal": "Horisontaalinen", + "HideWatchedContentFromLatestMedia": "Piilota toistettu sisältö \"uusin media\"-luettelosta", + "HeaderUpcomingOnTV": "Tulossa televisiossa", + "HeaderTypeImageFetchers": "{0} Kuvien hakijat", + "HeaderTranscodingProfile": "Transkoodausprofiili", + "HeaderTracks": "Raidat", + "HeaderThisUserIsCurrentlyDisabled": "Tämä käyttäjä on poistettu käytöstä", + "HeaderSystemDlnaProfiles": "Järjestelmäprofiilit", + "HeaderSubtitleDownloads": "Tekstitysten lataukset", + "HeaderSpecialFeatures": "Lisäominaisuudet", + "HeaderSpecialEpisodeInfo": "Erikoisjakson tiedot", + "HeaderSortOrder": "Lajittelujärjestys", + "HeaderSetupLibrary": "Aseta mediakirjastosi", + "HeaderSeriesStatus": "Sarjan status", + "HeaderSeriesOptions": "Sarjan asetukset", + "HeaderSelectTranscodingPath": "Valitse transkoodauksen väliaikainen polku", + "HeaderSchedule": "Ajastus", + "HeaderScenes": "Kohtaukset", + "HeaderResponseProfile": "Vastausprofiili", + "HeaderRemoveMediaLocation": "Poista mediasijainti", + "HeaderRecordingOptions": "Tallennusasetukset", + "HeaderRecentlyPlayed": "Äskettäin toistetut", + "HeaderProfileServerSettingsHelp": "Nämä arvot mukauttavat sitä, kuinka Jellyfin-palvelin esittää itsensä laitteelle.", + "HeaderProfileInformation": "Profiili-informaatio", + "HeaderPreferredMetadataLanguage": "Ensisijainen metadatan kieli", + "HeaderPinCodeReset": "Nollaa PIN-koodi", + "HeaderPhotoAlbums": "Kuva-albumit", + "HeaderPendingInvitations": "Odottavat kutsut", + "HeaderPaths": "Polut", + "HeaderPasswordReset": "Salasanan nollaus", + "HeaderNextVideoPlayingInValue": "Seuraava video alkaa {0}", + "HeaderNextEpisodePlayingInValue": "Seuraava jakso alkaa {0}", + "HeaderNewDevices": "Uudet laitteet", + "HeaderMyMediaSmall": "Minun mediani (pieni)", + "HeaderMyMedia": "Minun mediani", + "HeaderMyDevice": "Minun laitteeni", + "HeaderLoginFailure": "Kirjautumisvirhe", + "HeaderIdentifyItemHelp": "Anna yksi tai useampi hakukriteeri. Poista kriteerejä lisätäksesi hakutuloksia.", + "HeaderIdentificationCriteriaHelp": "Lisää ainakin yksi tunnistuskriteeri.", + "HeaderFeatures": "Ominaisuudet", + "HeaderFavoriteVideos": "Suosikkivideot", + "HeaderFavoritePeople": "Suosikki-ihmiset", + "HeaderFavoriteMovies": "Suosikkielokuvat", + "HeaderFavoriteBooks": "Suosikkikirjat", + "HeaderExternalIds": "Ulkoiset IDt:", + "HeaderDirectPlayProfile": "Suoratoistoprofiili", + "HeaderEasyPinCode": "Helppo PIN-koodi", + "HeaderDownloadSync": "Lataus ja synkronointi", + "HeaderDeveloperInfo": "Kehittäjäinfo", + "HeaderDetectMyDevices": "Havaitse laitteitani", + "HeaderDeleteProvider": "Poista tarjoaja", + "HeaderDefaultRecordingSettings": "Oletus tallennusasetukset", + "HeaderDateIssued": "Antopäivä", + "HeaderCustomDlnaProfiles": "Mukautetut profiilit", + "HeaderConfirmRevokeApiKey": "Peru API-avain", + "HeaderConfirmProfileDeletion": "Vahvista profiilin poisto", + "HeaderConfirmPluginInstallation": "Vahvista liitännäisen asennus", + "HeaderConfigureRemoteAccess": "Määritä etäkäyttö", + "HeaderChapterImages": "Kappalekuvat", + "HeaderChannels": "Kanavat", + "HeaderApp": "Sovellus", + "HeaderAllowMediaDeletionFrom": "Salli median poisto", + "HeaderAlert": "Hälytys", + "HeaderActivity": "Toiminta", + "HandledByProxy": "Reverse proxyn hoitama", + "HDPrograms": "HD-ohjelmat", + "OptionDownloadArtImage": "Taide", + "OptionDownloadDiscImage": "Levy", + "OptionDownloadBoxImage": "Laatikko", + "OptionDownloadBannerImage": "Lippu", + "OptionDownloadBackImage": "Tausta", + "OptionDisableUser": "Poista tämä käyttäjä käytöstä", + "OptionDescending": "Laskeva", + "OptionDatePlayed": "Toistopäivä", + "OptionDateAddedImportTime": "Käytä kirjastoon skannauspäivää", + "OptionDateAddedFileTime": "Käytä tiedoston luontipäivää", + "OptionDateAdded": "Lisäyspäivä", + "OptionDaily": "Päivittäinen", + "OptionBluray": "Blu-ray", + "TabTrailers": "Trailerit", + "OptionBlockTvShows": "TV-sarjat", + "OptionBlockTrailers": "Trailerit", + "OptionBlockMusic": "Musiikki", + "OptionBlockMovies": "Elokuvat", + "HeaderMovies": "Elokuvat", + "HeaderMoreLikeThis": "Lisää tällaista", + "HeaderMetadataSettings": "Metadata-asetukset", + "MoreMediaInfo": "Mediainfo", + "HeaderMediaInfo": "Mediainfo", + "HeaderMediaFolders": "Mediakansiot", + "HeaderMedia": "Media", + "HeaderLiveTv": "Live-TV", + "HeaderLibraryFolders": "Kirjaston kansiot", + "HeaderLatestMedia": "Uusin media", + "HeaderLatestRecordings": "Uusimmat tallenteet", + "HeaderLatestMusic": "Uusin musiikki", + "HeaderLatestMovies": "Uusimmat elokuvat", + "HeaderLatestEpisodes": "Uusimmat jaksot", + "HeaderInstall": "Asenna", + "HeaderGenres": "Tyylilajit", + "HeaderFrequentlyPlayed": "Usein toistetut", + "HeaderFetcherSettings": "Hakijan asetukset", + "HeaderFetchImages": "Hae kuvia:", + "HeaderFilters": "Suodattimet", + "OptionBlockBooks": "Kirjat", + "Filters": "Suodattimet", + "FastForward": "Hyppää eteenpäin", + "YadifBob": "YADIF Bob", + "MessageInvalidUser": "Väärä käyttäjätunnus tai salasana. Yritä uudelleen.", + "MessageConfirmRestart": "Haluatko varmasti uudelleenkäynnistää Jellyfin-palvelimen?", + "MessageConfirmProfileDeletion": "Haluatko varmasti poistaa tämän profiilin?", + "MessageConfirmDeleteTunerDevice": "Haluatko varmasti poistaa tämän laitteen?", + "MessageConfirmAppExit": "Haluatko poistua?", + "MessageAreYouSureYouWishToRemoveMediaFolder": "Haluatko varmasti poistaa tämän mediakansion?", + "MessageAreYouSureDeleteSubtitles": "Haluatko varmasti poistaa tämän tekstitystiedoston?", + "MessageAlreadyInstalled": "Tämä versio on jo asennettu.", + "Menu": "Valikko", + "MediaInfoStreamTypeVideo": "Video", + "MediaInfoStreamTypeSubtitle": "Tekstitys", + "MediaInfoStreamTypeData": "Data", + "MediaInfoStreamTypeAudio": "Audio", + "MediaInfoSoftware": "Ohjelmisto", + "MediaInfoTimestamp": "Aikaleima", + "MediaInfoResolution": "Resoluutio", + "MediaInfoSize": "Koko", + "MediaInfoProfile": "Profiili", + "MediaInfoLevel": "Taso", + "MediaInfoPath": "Polku", + "MediaInfoLanguage": "Kieli", + "MediaInfoInterlaced": "Lomiteltu", + "MediaInfoFramerate": "Ruudunpäivitysnopeus", + "MediaInfoForced": "Pakotettu", + "MediaInfoDefault": "Oletus", + "MediaInfoExternal": "Ulkoinen", + "MediaInfoChannels": "Kanavat", + "MediaInfoAspectRatio": "Kuvasuhde", + "MarkUnplayed": "Merkitse toistamattomaksi", + "MarkPlayed": "Merkitse toistetuksi", + "ManageRecording": "Hallitse tallennusta", + "ManageLibrary": "Hallitse kirjastoa", + "Logo": "Logo", + "LiveTV": "Live-TV", + "LiveBroadcasts": "Suorat lähetykset", + "Live": "Suora", + "List": "Lista", + "LinksValue": "Linkkejä: {0}", + "LearnHowYouCanContribute": "Katso, miten voit auttaa.", + "Large": "Suuri", + "LabelffmpegPath": "FFmpeg polku:", + "LabelZipCode": "Postinumero:", + "LabelYear": "Vuosi:", + "LabelVideoResolution": "Videon resoluutio:", + "LabelVideo": "Video", + "DashboardArchitecture": "Arkkitehtuuri: {0}", + "DashboardOperatingSystem": "Käyttöjärjestelmä: {0}", + "DashboardServerName": "Palvelin: {0}", + "DashboardVersionNumber": "Versio: {0}", + "LabelVersionInstalled": "{0} asennettu", + "LabelVersion": "Versio:", + "LabelValue": "Arvo:", + "LabelUsername": "Käyttäjätunnus:", + "LabelUser": "Käyttäjä:", + "LabelUseNotificationServices": "Käytä seuraavia palveluita:", + "LabelTypeText": "Teksti", + "LabelTypeMetadataDownloaders": "{0} metadatan lataajat:", + "LabelType": "Tyyppi:", + "LabelTunerType": "Virittimen tyyppi:", + "LabelTunerIpAddress": "Virittimen IP-osoite:", + "LabelTranscodingThreadCountHelp": "Valitse enimmäismäärä säikeitä, joita käytetään transkoodatessa. Säikeiden vähentäminen laskee prosessorin käyttöä, mutta myös lisää riskiä, että uudelleenkoodaus ei tapahdu riittävän nopeasti virheetöntä toistoa varten.", + "LabelTranscodingThreadCount": "Transkoodaus säikeidein lukumäärä:", + "LabelTranscodePath": "Transkoodauksen polku:", + "LabelTimeLimitHours": "Aikaraja (tunteina):", + "LabelTime": "Aika:", + "LabelTheme": "Teema:", + "LabelTextSize": "Tekstin koko:", + "LabelTextColor": "Tekstin väri:", + "LabelTextBackgroundColor": "Tekstin taustaväri:", + "LabelSupportedMediaTypes": "Tuetut mediatyypit:", + "LabelTag": "Tunniste:", + "LabelSubtitles": "Tekstitykset", + "LabelSubtitleFormatHelp": "Esimerkki: srt", + "LabelStatus": "Status:", + "LabelSource": "Lähde:", + "LabelSize": "Koko:", + "LabelServerName": "Palvelimen nimi:", + "LabelServerHostHelp": "192.168.1.100:8096 tai https://myserver.com", + "LabelSelectUsers": "Valitse käyttäjät:", + "LabelSeasonNumber": "Kauden numero:", + "LabelScreensaver": "Näytönsäästäjä:", + "LabelReasonForTranscoding": "Transkoodauksen syy:", + "LabelReadHowYouCanContribute": "Katso, miten voit auttaa.", + "LabelPublicHttpsPort": "Julkinen HTTPS-porttinumero:", + "LabelPublicHttpPort": "Julkinen HTTP-porttinumero:", + "LabelProtocolInfo": "Protokollan info:", + "LabelProtocol": "Protokolla:", + "LabelPreferredSubtitleLanguage": "Ensisijainen tekstityksen kieli:", + "LabelPreferredDisplayLanguageHelp": "Jellyfinin kääntäminen on käynnissä oleva projekti.", + "LabelPlayerDimensions": "Soittimen mitat:", + "LabelPlayer": "Soitin:", + "LabelPlaylist": "Soittolista:", + "LabelPlaceOfBirth": "Synnyinpaikka:", + "LabelPersonRoleHelp": "Esimerkki: Jäätelöauton ajaja", + "LabelPersonRole": "Rooli:", + "LabelPath": "Polku:", + "LabelPasswordRecoveryPinCode": "PIN-koodi:", + "LabelPasswordConfirm": "Salasanan varmistus:", + "LabelPassword": "Salasana:", + "LabelOptionalNetworkPath": "(Valinnainen) Jaettu verkkokansio:", + "LabelNumber": "Numero:", + "LabelNotificationEnabled": "Ota tämä ilmoitus käyttöön", + "LabelNewName": "Uusi nimi:", + "LabelName": "Nimi:", + "LabelMovieCategories": "Elokuvakategoriat:", + "LabelMinScreenshotDownloadWidth": "Pienin kuvakaappauksen latauksen leveys:", + "LabelMinResumePercentageHelp": "Kohteita pidetään toistamattomina, jos toisto keskeytetään ennen tätä aikaa.", + "LabelMinResumeDuration": "Minimi jatkamisen kesto (sekuntia):", + "LabelMinResumePercentage": "Vähimmäisaika jatkoa varten (%):", + "LabelMinResumeDurationHelp": "Kohteiden, joiden toistoaika on tätä lyhyempi, ei voi jatkaa.", + "LabelMethod": "Metodi:", + "LabelMetadataSaversHelp": "Valitse tiedostomuodot, joihin metadata tallennetaan.", + "LabelMetadataSavers": "Metadatan tallentajat:", + "LabelMetadataDownloadLanguage": "Ensisijainen latauskieli:", + "LabelMetadataReaders": "Metadatan lukijat:", + "LabelMetadataPath": "Metadatan polku:", + "LabelMetadata": "Metadata:", + "LabelMessageTitle": "Viestin otsikko:", + "LabelCachePath": "Välimuistin polku:", + "LabelCache": "Välimuisti:", + "LabelBurnSubtitles": "Polta tekstitykset:", + "LabelAutomaticallyRefreshInternetMetadataEvery": "Päivitä metadata automaattisesti:", + "LabelAuthProvider": "Todennuksen tarjoaja:", + "ExtraLarge": "Suurin", + "EveryNDays": "Joka {0} päivä", + "Raised": "Korotettu", + "TabShows": "Sarjat", + "Yesterday": "Eilen", + "Yes": "Kyllä", + "Unplayed": "Toistamattomat", + "Unmute": "Lopeta vaimennus", + "Tuesday": "Tiistai", + "Transcoding": "Transkoodaus", + "Trailers": "Trailerit", + "TitlePlayback": "Toistaminen", + "Thursday": "Torstai", + "TheseSettingsAffectSubtitlesOnThisDevice": "Nämä asetukset vaikuttavat tekstityksiin tällä laitteella", + "ThemeVideos": "Tunnusvideot", + "ThemeSongs": "Tunnuslaulut", + "TagsValue": "Tunnisteet: {0}", + "Tags": "Tunnisteet", + "TabUsers": "Käyttäjät", + "TabUpcoming": "Tulevat", + "TabTranscoding": "Transkoodaus", + "TabSuggestions": "Ehdotukset", + "TabSongs": "Kappaleet", + "TabSettings": "Asetukset", + "TabServer": "Palvelin", + "TabSeries": "Sarjat", + "TabScheduledTasks": "Ajastetut tehtävät", + "TabResumeSettings": "Jatka", + "TabResponses": "Vastaukset", + "TabRecordings": "Tallennukset", + "TabPlugins": "Liitännäiset", + "TabPlaylists": "Soittolistat", + "TabPlaylist": "Soittolista", + "TabPlayback": "Toistaminen", + "TabNfoSettings": "NFO-asetukset", + "TabNetworks": "Verkot", + "TabMyPlugins": "Omat liittännäiseni", + "TabMusicVideos": "Musiikkivideot", + "TabMusic": "Musiikki", + "TabMovies": "Elokuvat", + "TabMetadata": "Metadata", + "TabLogs": "Lokit", + "TabLiveTV": "Live-TV", + "TabLatest": "Uusimmat", + "TabInfo": "Tiedot", + "TabGenres": "Tyylilajit", + "TabFavorites": "Suosikit", + "TabEpisodes": "Jaksot", + "TabDisplay": "Näyttö", + "TabDirectPlay": "Suoratoisto", + "TabDevices": "Laitteet", + "TabDashboard": "Päänäkymä", + "TabCollections": "Kokoelmat", + "TabChannels": "Kanavat", + "TabCatalog": "Luettelo", + "TabArtists": "Artistit", + "TabAlbums": "Albumit", + "TabAlbumArtists": "Albumin artistit", + "TabAdvanced": "Edistynyt", + "TV": "TV", + "Sunday": "Sunnuntai", + "Subtitles": "Tekstitykset", + "Studios": "Studiot", + "StopRecording": "Lopeta tallennus", + "Sort": "Järjestä", + "Smart": "Älykäs", + "SkipEpisodesAlreadyInMyLibrary": "Älä tallenna jaksoja, jotka ovat jo kirjastossani", + "Shuffle": "Satunnaistoisto", + "ShowTitle": "Näytä nimi", + "ShowYear": "Näytä vuosi", + "ShowAdvancedSettings": "Näytä edistyneet asetukset", + "Share": "Jaa", + "Settings": "Asetukset", + "ServerRestartNeededAfterPluginInstall": "Jellyfin palvelin täytyy uudelleenkäynnistää liitännäisen asennuksen jälkeen.", + "ServerNameIsShuttingDown": "Jellyfin palvelin - {0} on sammumassa.", + "ServerNameIsRestarting": "Jellyfin palvelin - {0} uudelleenkäynnistyy.", + "Series": "Sarjat", + "SendMessage": "Lähetä viesti", + "SelectAdminUsername": "Valitse käyttäjätunnus järjestelmänvalvojan tilille.", + "SearchForCollectionInternetMetadata": "Etsi kuvamateriaalia ja metadataa internetistä", + "SearchForMissingMetadata": "Etsi puuttuvaa metadataa", + "Season": "Kausi", + "SearchResults": "Tulokset", + "SearchForSubtitles": "Etsi tekstityksiä", + "Search": "Etsi", + "Screenshots": "Kuvakaappaukset", + "Screenshot": "Kuvakaappaus", + "Schedule": "Ajasta", + "ScanLibrary": "Skannaa kirjasto", + "SaveSubtitlesIntoMediaFolders": "Tallenna tekstitykset mediakansioihin", + "Saturday": "Lauantai", + "ResumeAt": "Jatka kohdasta {0}", + "RestartPleaseWaitMessage": "Odota kunnes Jellyfin palvelin sammuu ja käynnistyy uudelleen. Tämä voi kestää hetken aikaa.", + "RequiredForAllRemoteConnections": "Vaadittu kaikille etäyhteyksille", + "ReplaceExistingImages": "Korvaa olemassaolevat kuvat", + "ReplaceAllMetadata": "Korvaa kaikki metadata", + "RepeatEpisodes": "Toista jaksot uudelleen", + "RepeatAll": "Toista kaikki uudelleen", + "Repeat": "Toista uudelleen", + "RemoveFromPlaylist": "Poista soittolistalta", + "RemoveFromCollection": "Poista kokoelmasta", + "RememberMe": "Muista minut", + "ReleaseDate": "Julkaisupäivä", + "RefreshMetadata": "Päivitä metadata", + "RefreshDialogHelp": "Metadata päivitetään asetuksien ja Internet palveluiden perusteella, jotka ovat kytkettynä päälle Jellyfin palvelimen päänäkymässä.", + "Refresh": "Päivitä", + "Recordings": "Tallennukset", + "RecordingScheduled": "Tallennus ajastettu.", + "RecordingCancelled": "Tallennus peruttu.", + "RecordSeries": "Tallenna sarja", + "Record": "Tallenna", + "OptionAutomatic": "Auto", + "OptionAuto": "Auto", + "OptionAscending": "Nousevassa järjestyksessä", + "OptionArtist": "Artisti", + "OptionAllowVideoPlaybackTranscoding": "Salli transkoodausta vaativan videon toistaminen", + "OptionAllowVideoPlaybackRemuxing": "Salli videon toistaminen, joka vaatii muuntamista ilman koodausta", + "OptionAllowMediaPlaybackTranscodingHelp": "Transkoodauksen estäminen voi aiheuttaa toistovirheitä Jellyfin-sovelluksissa ei-tuettujen mediaformaattien takia.", + "OptionAllowLinkSharingHelp": "Vain mediatietoja sisältävät web-sivut jaetaan. Mediatiedostoja ei koskaan jaeta julkisesti. Jaot ovat määräaikaisia ja päättyvät {0} päivän kuluttua.", + "OptionAllowUserToManageServer": "Salli tämän käyttäjän hallita palvelinta", + "OptionAllowSyncTranscoding": "Salli transkoodausta vaativan median lataaminen ja synkronointi", + "OptionAllowRemoteSharedDevicesHelp": "DLNA-laitteet katsotaan jaetuiksi kunnes käyttäjä alkaa ohjata niitä.", + "OptionAllowRemoteSharedDevices": "Salli jaettujen laitteiden etäohjaaminen", + "OptionAllowRemoteControlOthers": "Salli muiden käyttäjien etäohjaaminen", + "OptionAllowManageLiveTv": "Salli Live-TV tallenteiden hallinta", + "OptionAllowMediaPlayback": "Salli median toisto", + "OptionAllowContentDownloading": "Salli median lataaminen ja synkronointi", + "OptionAllowBrowsingLiveTv": "Salli Live-TV käyttöoikeus", + "HeaderPluginInstallation": "Liitännäisen asennus", + "HeaderPlaybackError": "Toistovirhe", + "HeaderPlayback": "Median toisto", + "HeaderPlayOn": "Toista laitteella", + "OptionAllowLinkSharing": "Salli jakaminen sosiaaliseen mediaan", + "OptionAllowAudioPlaybackTranscoding": "Salli äänen toistaminen joka vaatii uudelleenpakkausta", + "OptionAllUsers": "Kaikki käyttäjät", + "OptionAlbumArtist": "Albumin artisti", + "OptionAlbum": "Albumi", + "OptionAdminUsers": "Järjestelmänvalvojat", + "Option3D": "3D", + "MusicVideo": "Musiikkivideo", + "MoveRight": "Siirry oikealle", + "MoveLeft": "Siirry vasemmalle", + "Mobile": "Mobiili", + "EveryXHours": "Joka {0} tunti", + "EveryXMinutes": "Joka {0} minuutti", + "EveryHour": "Joka tunti", + "LastSeen": "Viimeksi nähty {0}", + "Yadif": "YADIF", + "Writer": "Kirjoittaja", + "WelcomeToProject": "Tervetuloa Jellyfiniin!", + "Wednesday": "Keskiviikko", + "ViewArtist": "Näytä artisti", + "ViewAlbum": "Näytä albumi", + "Vertical": "Vertikaalinen", + "ValueSongCount": "{0} kappaletta", + "ValueSeriesCount": "{0} sarjaa", + "ValueSeconds": "{0} sekuntia", + "ValueOneSong": "1 kappale", + "ValueOneSeries": "1 sarja", + "ValueOneMusicVideo": "1 musiikkivideo", + "ValueOneMovie": "1 elokuva", + "ValueOneEpisode": "1 jakso", + "ValueOneAlbum": "1 albumi", + "ValueMusicVideoCount": "{0} musiikkivideota", + "ValueMovieCount": "{0} elokuvaa", + "ValueMinutes": "{0} min", + "ValueEpisodeCount": "{0} jaksoa", + "ValueDiscNumber": "Levy {0}", + "ValueAlbumCount": "{0} albumia", + "Up": "Ylös", + "OnApplicationStartup": "Käynnistyksen yhteydessä", + "NumLocationsValue": "{0} kansiota", + "NoSubtitleSearchResultsFound": "Ei tuloksia.", + "NoPluginConfigurationMessage": "Tällä liitännäisellä ei ole asetuksia muokattavaksi.", + "NoCreatedLibraries": "Vaikuttaa siltä, ettet ole luonut vielä yhtään kirjastoa. {0} Haluaisitko luoda sellaisen nyt?{1}", + "No": "Ei", + "NextUp": "Seuraavana", + "Next": "Seuraava", + "NewEpisodesOnly": "Vain uudet jaksot", + "NewEpisodes": "Uusia jaksoja", + "NewCollectionNameExample": "Esimerkki: Star Wars Kokoelma", + "NewCollectionHelp": "Kokoelmat mahdollistavat elokuvien ja muun kirjastosisällön personalisoidun ryhmittämisen.", + "NewCollection": "Uusi kokoelma", + "Mute": "Vaimenna", + "Name": "Nimi", + "MySubtitles": "Minun tekstitykseni", + "MusicArtist": "Musiikkiartisti", + "MusicAlbum": "Musiikkialbumi", + "Movie": "Elokuva", + "Monday": "Maanantai", + "MetadataManager": "Metadatan hallintatyökalu", + "Metadata": "Metadata", + "MessageYouHaveVersionInstalled": "Sinulla on versio {0} asennettuna.", + "MessageSettingsSaved": "Asetukset tallennettu.", + "MessagePleaseWait": "Ole hyvä ja odota. Tämä voi kestää hetken.", + "MessageNothingHere": "Täällä ei ole mitään.", + "MessageNoPluginsInstalled": "Sinulla ei ole asennettuna yhtään liitännäistä.", + "MessageNoAvailablePlugins": "Ei saatavilla olevia liitännäisiä.", + "InstallingPackage": "Asennetaan {0} (versio {1})", + "HeaderVideoTypes": "Videotyypit", + "HeaderVideoType": "Videotyyppi", + "HeaderUploadImage": "Lataa kuva", + "HeaderTypeText": "Kirjoita teksti", + "HeaderTunerDevices": "Viritinlaitteet", + "HeaderTuners": "Virittimet", + "HeaderTaskTriggers": "Tehtävän laukaisijat", + "HeaderSubtitleProfilesHelp": "Tekstitysprofiilit kuvaavat tämän laitteen tukemia tekstitysformaatteja.", + "HeaderSubtitleProfiles": "Tekstitysprofiilit", + "HeaderSubtitleProfile": "Tekstitysprofiili", + "HeaderStartNow": "Aloita nyt", + "HeaderSortBy": "Lajittele", + "HeaderSelectServerCachePath": "Valitse palvelimen välimuistin polku", + "HeaderSelectPath": "Valitse polku", + "HeaderSelectCertificatePath": "Valitse sertifikaatin polku", + "HeaderSelectMetadataPath": "Valitse metadatan polku", + "HeaderSecondsValue": "{0} Sekuntia", + "HeaderRunningTasks": "Käynnissä olevat tehtävät", + "HeaderRevisionHistory": "Versiohistoria", + "HeaderRestartingServer": "Uudelleenkäynnistetään palvelinta", + "HeaderRemoveMediaFolder": "Poista mediakansio", + "HeaderRemoteControl": "Etäohjaus", + "HeaderPleaseSignIn": "Ole hyvä ja kirjaudu sisään", + "BoxSet": "Laatikkosarja", + "LabelManufacturerUrl": "Valmistajan verkko-osoite", + "LabelManufacturer": "Valmistaja:", + "LabelLogs": "Lokit:", + "LabelLanNetworks": "Lähiverkot:", + "LabelKodiMetadataDateFormat": "Julkaisupäivämäärän muoto:", + "LabelImageType": "Kuvan tyyppi:", + "LabelIconMaxWidth": "Ikonin enimmäisleveys:", + "LabelIconMaxHeight": "Ikonin enimmäiskorkeus:", + "LabelGroupMoviesIntoCollections": "Ryhmitä elokuvat kokoelmiin", + "LabelFormat": "Muoto:", + "LabelFont": "Kirjasinlaji:", + "LabelFolder": "Kansio:", + "LabelEpisodeNumber": "Jaksonumero:", + "LabelDropShadow": "Varjostus:", + "LabelDeathDate": "Kuolinpäivä:", + "LabelDay": "Päivä:", + "LabelDateAdded": "Lisäyspäivämäärä:", + "LabelCollection": "Kokoelma:", + "LabelBirthYear": "Syntymävuosi:", + "LabelBirthDate": "Syntymäaika:", + "LabelArtists": "Artistit:", + "LabelAll": "Kaikki", + "LabelAlbum": "Albumi:", + "LabelAirTime": "Lähetysaika:", + "LabelAccessDay": "Viikonpäivä:", + "Label3DFormat": "3D-formaatti:", + "Kids": "Lapset", + "Images": "Kuvat", + "Hide": "Piilota", + "HeadersFolders": "Kansiot", + "HeaderYears": "Vuodet", + "HeaderVideos": "Videot", + "HeaderVideoQuality": "Kuvanlaatu", + "HeaderUsers": "Käyttäjät", + "HeaderUser": "Käyttäjä", + "HeaderTags": "Tunnisteet", + "HeaderSubtitleAppearance": "Tekstityksen ulkonäkö", + "HeaderStatus": "Tila", + "HeaderShutdown": "Sammuta", + "HeaderServerSettings": "Palvelimen asetukset", + "HeaderSettings": "Asetukset", + "HeaderSendMessage": "Lähetä viesti", + "HeaderSelectServer": "Valitse palvelin", + "HeaderSeasons": "Kaudet", + "HeaderRestart": "Uudelleenkäynnistys", + "HeaderProfile": "Profiili", + "HeaderPlayAll": "Toista kaikki", + "HeaderPeople": "Ihmiset", + "HeaderPassword": "Salasana", + "HeaderNewApiKey": "Uusi API-avain", + "HeaderNavigation": "Navigaatio", + "HeaderMusicVideos": "Musiikkivideot", + "HeaderMusicQuality": "Musiikin laatu", + "HeaderLibraries": "Kirjastot", + "HeaderIdentification": "Tunnistautuminen", + "HeaderForgotPassword": "Unohtuiko salasana", + "HeaderForKids": "Lapsille", + "HeaderError": "Virhe", + "HeaderEpisodes": "Jaksot", + "HeaderEditImages": "Muokkaa kuvia", + "HeaderDisplay": "Näyttö", + "HeaderDevices": "Laitteet", + "HeaderDeleteItems": "Poista valitut", + "HeaderDeleteItem": "Poista valittu", + "HeaderDeleteDevice": "Poista laite", + "HeaderContinueListening": "Jatka kuuntelua", + "HeaderConnectionFailure": "Yhteys epäonnistui", + "HeaderConnectToServer": "Yhdistä palvelimeen", + "HeaderAudioSettings": "Ääniasetukset", + "GroupBySeries": "Ryhmitä sarjan perusteella", + "Fullscreen": "Kokonäyttötila", + "HeaderBooks": "Kirjat", + "HeaderAutomaticUpdates": "Automaattiset päivitykset", + "HeaderAudioBooks": "Äänikirjat", + "HeaderApiKeys": "API-avaimet", + "HeaderApiKey": "API-avain", + "HeaderAdmin": "Ylläpitäjä", + "HeaderAlbums": "Albumit", + "HeaderAddUser": "Lisää käyttäjä", + "HeaderAddUpdateImage": "Lisää/Päivitä kuva", + "HeaderAddToPlaylist": "Lisää soittolistaan", + "HeaderAddToCollection": "Lisää kokoelmaan", + "HeaderActiveDevices": "Aktiiviset laitteet", + "Friday": "Perjantai", + "Premiere": "Ensiesitys", + "Producer": "Tuottaja", + "Quality": "Laatu", + "LabelMessageText": "Viestin sisältö:", + "LabelMaxScreenshotsPerItem": "Kuvakaappausten enimmäismäärä per kohde:", + "LabelLoginDisclaimerHelp": "Viesti, joka näytetään kirjautumissivun alareunassa.", + "LabelLockItemToPreventChanges": "Lukitse tämä kohde estääksesi tulevat muutokset", + "LabelLocalHttpServerPortNumberHelp": "TCP-portin numero, jota Jellyfinin HTTP-palvelimen tulee kuunnella.", + "LabelLocalHttpServerPortNumber": "Paikallisen HTTP-portin numero:", + "LabelKodiMetadataSaveImagePaths": "Tallenna kuvien polut NFO-tiedostojen sisälle", + "LabelKidsCategories": "Lasten kategoriat:", + "LabelHttpsPortHelp": "TCP-portin numero, jota Jellyfinin HTTPS-palvelimen tulee kuunnella.", + "LabelHttpsPort": "Paikallisen HTTPS-portin numero:", + "LabelHardwareAccelerationTypeHelp": "Laitteistokiihdytys vaatii ylimääräistä määritystä.", + "LabelHardwareAccelerationType": "Laitteistokiihdytys:", + "LabelEncoderPreset": "H264 and H265 encoding preset:", + "LabelH264Crf": "H264 encoding CRF:", + "LabelForgotPasswordUsernameHelp": "Anna käyttäjätunnuksesi, jos muistat sen.", + "LabelEveryXMinutes": "Joka:", + "LabelEndDate": "Päättymispäivä:", + "LabelEnableDlnaClientDiscoveryIntervalHelp": "Määrittää, kuinka usein Jellyfin etsii SSDP-protokollaa käyttäviä laitteita.", + "LabelEnableDlnaDebugLoggingHelp": "Luo suuria lokitiedostoja ja tulisi käyttää vain tarvittaessa vianmääritystä varten.", + "LabelEnableDlnaServerHelp": "Sallii verkon UPnP-laitteiden selata ja toistaa sisältöä tältä palvelimelta.", + "LabelEnableDlnaServer": "Ota DLNA-palvelin käyttöön", + "LabelEnableDlnaPlayTo": "Salli DLNA toisto", + "LabelEnableDlnaDebugLogging": "Ota DLNA:n virheenjäljitys käyttöön", + "LabelEnableBlastAliveMessages": "Lähetä hereilläolo -viesti", + "LabelEnableBlastAliveMessagesHelp": "Ota tämä käyttöön, jos muilla verkon UPnP-laitteilla on ongelmia palvelimen havaitsemisessa.", + "LabelEnableDlnaClientDiscoveryInterval": "Asiakaslaitteiden havaintoväli (sekunteina)", + "LabelEasyPinCode": "Helppo PIN-koodi:", + "LabelDynamicExternalId": "{0} Id:", + "LabelDisplayLanguageHelp": "Jellyfinin kääntäminen on käynnissä oleva projekti.", + "LabelDisplayLanguage": "Näytön kieli:", + "LabelDiscNumber": "Levynumero:", + "LabelDeviceDescription": "Laitteen kuvaus", + "LabelDefaultScreen": "Oletusnäyttö:", + "LabelDefaultUser": "Oletuskäyttäjä:", + "LabelDashboardTheme": "Palvelimen päänäkymän teema:", + "LabelCustomCertificatePathHelp": "Polku PKCS # 12-tiedostoon, joka sisältää sertifikaatin ja yksityisen avaimen, jotta TLS-tuki voidaan sallia henkilökohtaiselle verkkotunnukselle.", + "LabelCustomCertificatePath": "Mukautetun SSL-sertifikaatin polku:", + "LabelContentType": "Sisältötyyppi:", + "LabelChannels": "Kanavat:", + "LabelCertificatePasswordHelp": "Jos sertifikaattisi vaatii salasanaa, laita se tähän.", + "OptionWednesday": "Keskiviikko", + "OptionTuesday": "Tiistai", + "OptionThursday": "Torstai", + "OptionSunday": "Sunnuntai", + "OptionSaturday": "Lauantai", + "LabelRuntimeMinutes": "Pituus (minuutteja):", + "LabelReleaseDate": "Julkaisupäivä:", + "Genre": "Tyylilaji", + "FolderTypeBooks": "Kirjat", + "FolderTypeMusicVideos": "Musiikkivideot", + "FolderTypeMusic": "Musiikki", + "FolderTypeMovies": "Elokuvat", + "File": "Tiedosto", + "Favorite": "Suosikki", + "Extras": "Extrat", + "ExitFullscreen": "Poistu kokonäyttötilasta", + "Episode": "Jakso", + "ButtonTogglePlaylist": "Soittolista", + "ButtonToggleContextMenu": "Lisää", + "Artist": "Artisti", + "RefreshQueued": "Päivitys odottamassa.", + "SeriesCancelled": "Sarja peruttu.", + "MediaInfoRefFrames": "Ref frames", + "LabelXDlnaDoc": "X-DLNA doc:", + "LabelXDlnaCap": "X-DLNA cap:", + "Guide": "Opas", + "DailyAt": "Päivittäin klo {0}", + "Uniform": "Yhtenäinen", + "TrackCount": "{0} raitaa", + "Track": "Raita", + "TitleHardwareAcceleration": "Laitteistokiihdytys", + "Thumb": "Pikkukuva", + "TabStreaming": "Suoratoisto", + "TabParentalControl": "Lapsilukko", + "TabOther": "Muut", + "TabNotifications": "Ilmoitukset", + "TabNetworking": "Verkkoasetukset", + "TabGuide": "Opas", + "TabContainers": "Säiliöt", + "TabCodecs": "Codecit", + "Suggestions": "Ehdotukset", + "Sports": "Urheilu", + "Smaller": "Pienin", + "SmallCaps": "Small Caps", + "Small": "Pieni", + "Filter": "Suodata", + "New": "Uusi", + "Runtime": "Kesto", + "PlayAllFromHere": "Toista kaikki tästä lähtien", + "PlayNextEpisodeAutomatically": "Toista seuraava jakso automaattisesti", + "PlayCount": "Toistokerrat", + "PlaybackData": "Toistamisen tiedot", + "PinCodeResetComplete": "PIN-koodi on nollattu.", + "Overview": "Yleiskatsaus", + "OptionWakeFromSleep": "Herätä lepotilasta", + "OptionUnairedEpisode": "Julkaisemattomat jaksot", + "OptionParentalRating": "Ikäraja", + "OptionNone": "Ei mitään", + "OptionIgnoreTranscodeByteRangeRequests": "Ohita transkoodauksen tavualuepyynnöt", + "OptionHlsSegmentedSubtitles": "HLS segmentoidut tekstitykset", + "OptionEnableExternalContentInSuggestions": "Ota käyttöön ulkoinen sisältö ehdotuksissa", + "OptionEmbedSubtitles": "Upota säiliöön", + "OptionForceRemoteSourceTranscoding": "Pakota etämedialähteiden transkoodaus (kuten Live-TV)", + "OneChannel": "Yksi kanava", + "Off": "Poissa päältä", + "Normal": "Normaali", + "None": "Ei mitään", + "NoSubtitles": "Ei mitään", + "MessageTheFollowingLocationWillBeRemovedFromLibrary": "Seuraavat mediasijainnit tullaan poistamaan kirjastostasi:", + "MessageItemsAdded": "Kohteet lisätty.", + "MessageItemSaved": "Kohde tallennettu.", + "MessageEnablingOptionLongerScans": "Tämän vaihtoehdon käyttöönottaminen voi johtaa huomattavasti pidempiin kirjastojen skannauksiin.", + "MessageDownloadQueued": "Lataus asetettu jonoon.", + "MessageConfirmRevokeApiKey": "Haluatko varmasti perua tämän API-avaimen? Sovelluksen yhteys Jellyfin-palvelimeen lopetetaan äkillisesti.", + "MessageConfirmRemoveMediaLocation": "Haluatko varmasti poistaa tämän sijainnin?", + "MessageConfirmRecordingCancellation": "Peruuta tallennus?", + "MessageConfirmDeleteGuideProvider": "Haluatko varmasti poistaa tämän ohjelmaoppaan tarjoajan?", + "MediaInfoStreamTypeEmbeddedImage": "Upotettu kuva", + "MediaInfoSampleRate": "Näytteenottotaajuus", + "MediaInfoPixelFormat": "Pikseliformaatti", + "MediaInfoLayout": "Asettelu", + "MediaInfoContainer": "Säiliö", + "MediaInfoCodecTag": "Codec-tunniste", + "MediaInfoCodec": "Codec", + "MediaInfoBitrate": "Bitrate", + "Like": "Tykkää", + "LabelVideoBitrate": "Videon bitrate:", + "LabelWeb": "Web:", + "LabelVideoCodec": "Videon codec:", + "LabelTranscodingVideoCodec": "Video codec:", + "LabelSkipIfGraphicalSubsPresent": "Ohita, jos video sisältää upotetut tekstitykset", + "LabelInternetQuality": "Verkkoyhteyden laatu:", + "LabelEmbedAlbumArtDidl": "Upota albumin kuvamateriaali Didl:iin", + "LabelDeinterlaceMethod": "Lomituksen poistamismenetelmä:", + "LabelAudioBitDepth": "Audion bittisyvyys:", + "LabelAlbumArtists": "Albumin artistit:", + "Items": "Kohteet", + "ItemCount": "{0} kohdetta", + "Home": "Koti", + "Help": "Apua", + "HeaderXmlSettings": "Xml-asetukset", + "HeaderXmlDocumentAttributes": "Xml-dokumentin attribuutit", + "HeaderXmlDocumentAttribute": "Xml-dokumentin attribuutti", + "HeaderStopRecording": "Lopeta tallennus", + "HeaderRecordingPostProcessing": "Tallennuksen jälkikäsittely", + "HeaderParentalRatings": "Ikärajat", + "HeaderOtherItems": "Muut kohteet", + "HeaderLiveTvTunerSetup": "Live-TV virittimen määritys", + "HeaderLibrarySettings": "Kirjaston asetukset", + "HeaderItems": "Kohteet", + "HeaderGuideProviders": "TV-ohjelmaoppaiden tarjoajat", + "HeaderFavoritePlaylists": "Suosikki soittolistat", + "HeaderEnabledFields": "Käytössä olevat kentät", + "HeaderCodecProfile": "Codec-profiili", + "HeaderContainerProfile": "Säiliöprofiili", + "PleaseSelectTwoItems": "Valitse ainakin kaksi kohdetta.", + "PleaseRestartServerName": "Ole hyvä ja uudelleenkäynnistä Jellyfin-palvelin - {0}.", + "PleaseEnterNameOrId": "Anna nimi tai ulkoinen ID.", + "PreferEmbeddedTitlesOverFileNames": "Suosi upotettuja otsikoita tiedostonimien sijasta", + "PreferredNotRequired": "Suositeltava, mutta ei vaadittu", + "Premieres": "Ensiesitys", + "Primary": "Ensisijainen", + "Previous": "Edellinen", + "RecommendationBecauseYouWatched": "Koska katsoit {0}", + "RecommendationBecauseYouLike": "Koska pidät {0}", + "RecentlyWatched": "Äskettäin katsotut", + "Programs": "Ohjelmat", + "LabelServerNameHelp": "Tätä nimeä käytetään palvelimen tunnistamiseen. Jos tyhjä, käytetään tietokoneen nimeä.", + "LabelEnableRealtimeMonitorHelp": "Tiedostojen muutokset käsitellään välittömästi tuetuissa tiedostojärjestelmissä.", + "LabelEnableRealtimeMonitor": "Ota reaaliaikainen seuranta käyttöön", + "LabelDropImageHere": "Pudota kuva tähän, tai paina selataksesi.", + "LabelDroppedFrames": "Pudotettuja kuvia:", + "LabelDidlMode": "DIDL tila:", + "LabelCustomDeviceDisplayNameHelp": "Tätä nimeä käytetään laitteen tunnistamiseen. Jos tyhjä, käytetään laitteen nimeä.", + "LabelCustomCssHelp": "Käytä omaa CSS: ää web-käyttöliittymään.", + "LabelCorruptedFrames": "Korruptoituneita kuvia:", + "LabelCriticRating": "Kriitikoiden luokitus:", + "LabelCommunityRating": "Yhteisön luokitus:", + "LabelCancelled": "Peruttu", + "LabelBlockContentWithTags": "Estä kohteet tunnisteilla:", + "LabelBitrate": "Bitrate:", + "LabelAudioSampleRate": "Audion näytteenottotaajuus:", + "LabelAudioChannels": "Audiokanavia:", + "LabelAudioBitrate": "Audion bitrate:", + "LabelAudioCodec": "Audio codec:", + "LabelTranscodingContainer": "Säiliö:", + "LabelTranscodingAudioCodec": "Audio codec:", + "LabelSubtitleDownloaders": "Tekstitysten lataajat:", + "LabelSpecialSeasonsDisplayName": "Erikoiskauden näyttönimi:", + "LabelSoundEffects": "Ääniefektit:", + "LabelSortTitle": "Lajitteluotsikko:", + "LabelSkipIfAudioTrackPresent": "Ohita, jos oletusääniraita vastaa latauskieltä", + "LabelSkipBackLength": "Taaksepäin hyppäämisen pituus:", + "LabelSkipForwardLength": "Eteenpäin hyppäämisen pituus:", + "LabelScheduledTaskLastRan": "Viimeisin ajo {0}, kesto {1}.", + "LabelRemoteClientBitrateLimitHelp": "Valinnainen bittinopeusraja yksittäisille suoratoistaville laitteille lähiverkon ulkopuolella. Tämä on hyödyllistä estääkseen laitteita pyytämästä suurempia bittinopeuksia kuin Internet-yhteys voi käsitellä. Tämä voi johtaa palvelimen prosessorin kuormituksen lisääntymiseen, jotta videot voidaan transkoodata pienempiin bittinopeuksiin.", + "LabelRemoteClientBitrateLimit": "Internetistä suoratoiston enimmäisnopeus (Mbps):", + "LabelRefreshMode": "Päivitystila:", + "LabelRecordingPathHelp": "Määritä oletussijainti tallennuksille. Jätä tyhjäksi käyttääksesi palvelimen datakansiota.", + "LabelRecordingPath": "Oletustallennuspolku:", + "LabelProtocolInfoHelp": "Arvo, jota käytetään vastattaessa laitteen GetProtocolInfo-pyyntöihin.", + "LabelProfileCodecsHelp": "Pilkulla erotettuna. Jätä tyhjäksi käyttääksesi kaikissa codeceissa.", + "LabelProfileContainersHelp": "Pilkulla erotettuna. Jätä tyhjäksi käyttääksesi kaikissa säiliöissä.", + "LabelProfileVideoCodecs": "Video codecs:", + "LabelProfileContainer": "Säiliö:", + "LabelProfileCodecs": "Codecs:", + "LabelProfileAudioCodecs": "Audio codecs:", + "LabelPostProcessor": "Jälkikäsittelysovellus:", + "LabelPostProcessorArguments": "Jälkikäsittelysovelluksen komentoriviargumentit:", + "LabelPleaseRestart": "Muutokset tulevat voimaan kun web-asiakasohjelma päivitetään manuaalisesti (reload).", + "LabelPlayDefaultAudioTrack": "Toista oletusraita kielestä riippumatta", + "LabelOverview": "Yleiskatsaus:", + "LabelPasswordResetProvider": "Salasanan nollauksen palveluntarjoaja:", + "LabelParentalRating": "Ikäraja:", + "LabelOptionalNetworkPathHelp": "Jos tämä kansio on jaettu verkossa, polun jakaminen voi mahdollistaa Jellyfin-sovellusten muilla laitteilla käyttää mediatiedostoja suoraan.", + "LabelMovieRecordingPath": "Elokuvien tallenteiden polku (valinnainen):", + "LabelMusicStreamingTranscodingBitrateHelp": "Määritä enimmäisnopeus musiikkia suoratoistettaessa.", + "LabelMusicStreamingTranscodingBitrate": "Musiikin transkoodauksen bitrate:", + "LabelMinBackdropDownloadWidth": "Taustan latauksen vähimmäisleveys:", + "LabelMetadataReadersHelp": "Järjestä ensisijaiset paikallisen metadatan lähteet prioriteettijärjestykseen. Ensimmäinen löydetty tiedosto luetaan.", + "LabelMetadataPathHelp": "Määrää mukautettu sijainti ladatulle kuvamateriaalille ja metadatalle.", + "LabelMetadataDownloadersHelp": "Ota käyttöön ja järjestä ensisijaiset metadatan lataajat prioriteettijärjestykseen. Alemman prioriteetin omaavia lataajia käytetään vain täyttämään puuttuvia tietoja.", + "LabelMaxResumePercentageHelp": "Kohteita pidetään toistettuina, jos toisto pysäytetään tämän ajan kuluttua.", + "LabelMaxResumePercentage": "Enimmäisaika jatkoa varten (%):", + "LabelMaxChromecastBitrate": "Chromecastin suoratoiston laatu:", + "LabelMaxBackdropsPerItem": "Taustojen maksimimäärä per kohde:", + "LabelLoginDisclaimer": "Sisäänkirjautumisen vastuuvapauslauseke:", + "LabelLibraryPageSize": "Kirjastosivun kohteiden määrä:", + "LabelLibraryPageSizeHelp": "Asettaa kirjastosivulla näytettävien kohteiden määrän. Arvo 0 poistaa sivutuksen käytöstä.", + "Unrated": "Luokittelematon", + "ExtractChapterImagesHelp": "Pikkukuvien luominen mahdollistaa sovellusten näyttää graafiikkaa valintavalikoissa. Prosessi voi olla hidas, prosessoria kuormittava ja saattaa vaatia useita gigatavuja tilaa. Se suoritetaan, kun videoita havaitaan, ja myös yöksi suunniteltuna tehtävänä. Aikataulu on konfiguroitavissa ajoitetuissa tehtävissä. Tätä tehtävää ei ole suositeltavaa suorittaa korkean kuormituksen aikana.", + "OnWakeFromSleep": "Lepotilasta poistuttaessa", + "WeeklyAt": "{0}sin klo {1}", + "Whitelist": "Sallitut", + "Watched": "Katsotut", + "ViewPlaybackInfo": "Näytä toistamisen tiedot", + "ValueVideoCodec": "Video Codec: {0}", + "ValueTimeLimitSingleHour": "Aikaraja: 1 tunti", + "ValueTimeLimitMultiHour": "Aikaraja: {0} tuntia", + "ValueContainer": "Säiliö: {0}", + "ValueConditions": "Ehdot: {0]", + "ValueCodec": "Codec: {0}", + "ValueAudioCodec": "Audio Codec: {0}", + "SeriesYearToPresent": "{0} - Nykyhetki", + "DeinterlaceMethodHelp": "Valitse lomituksen poistomenetelmä, jota käytetään lomitetun sisällön transkoodaukseen.", + "FormatValue": "Muoto: {0}", + "General": "Yleinen", + "FolderTypeUnset": "Sekalainen sisältö", + "FetchingData": "Noudetaan lisätietoja", + "Features": "Ominaisuudet", + "ErrorMessageStartHourGreaterThanEnd": "Loppumisajan on oltava myöhemmin kuin aloitusaika.", + "MediaInfoAnamorphic": "Anamorfinen" } diff --git a/src/strings/fr-ca.json b/src/strings/fr-ca.json index 131be8a57f..5880dbb770 100644 --- a/src/strings/fr-ca.json +++ b/src/strings/fr-ca.json @@ -134,12 +134,12 @@ "BirthLocation": "Lieu de naissance", "BirthPlaceValue": "Lieu de naissance : {0}", "Blacklist": "Liste noire", - "BookLibraryHelp": "Les livres audios et numériques sont supportés. Consultez le {0}Guide de nommage pour livre de Jellyfin{1}.", + "BookLibraryHelp": "Les livres audios et formats textes sont supportés. Consultez le {0}Guide de nommage de livres de Jellyfin{1}.", "Box": "Boîtier", "BoxRear": "Dos de boîtier", "Browse": "Parcourir", "BrowsePluginCatalogMessage": "Explorer notre catalogue des plugins pour voir les plugins disponibles.", - "AllowHWTranscodingHelp": "Si l'option est activée, permet au récepteur TV de transcoder les flux à la volée. Cela peut aider à réduire le transcodage requis par le serveur Jellyfin.", + "AllowHWTranscodingHelp": "Permet au récepteur TV de transcoder les flux à la volée. Cela peut aider à réduire le transcodage requis par le serveur Jellyfin.", "BurnSubtitlesHelp": "Détermine si le serveur doit graver les sous-titres lors de la conversion vidéo en fonction du format des sous-titres. Éviter la gravure des sous-titres améliorera les performances du serveur. Sélectionnez Auto pour graver les formats basés sur l'image (par exemple, VOBSUB, PGS, SUB/IDX etc) ainsi que certains sous-titres ASS/SSA", "ButtonAccept": "Accepter", "ButtonAdd": "Ajouter", @@ -185,5 +185,18 @@ "ButtonManualLogin": "Connexion manuelle", "ButtonMenu": "Menu", "ButtonMore": "Plus", - "ButtonNetwork": "Réseau" + "ButtonNetwork": "Réseau", + "AspectRatio": "Format de visionnement", + "AskAdminToCreateLibrary": "Demander à l'administrateur pour créer une bibliothèque de média.", + "Artist": "Artiste", + "AllowFfmpegThrottlingHelp": "Quand un transcodage ou rémux se déplace après la position de relecture, suspendre le processus pour consommer moins de ressources. Ceci est le plus utile pour chercher moins. Désactiver s'il y a des problèmes de relecture.", + "AllowFfmpegThrottling": "Restreindre la vitesse de transcodage", + "AlbumArtist": "Artiste de l'Album", + "Album": "Album", + "AuthProviderHelp": "Sélectionner un fournisseur d'authentification pour authentifier le mot de passe de cet utilisateur.", + "HeaderFavoriteSongs": "Chansons favorites", + "HeaderFavoriteShows": "Séries favorites", + "HeaderFavoriteEpisodes": "Épisodes favoris", + "HeaderFavoriteArtists": "Artistes favoris", + "HeaderFavoriteAlbums": "Albums favoris" } diff --git a/src/strings/fr.json b/src/strings/fr.json index 6db1113d49..81d6470ee9 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", @@ -23,15 +23,15 @@ "AllowMediaConversion": "Autoriser la conversion des médias", "AllowMediaConversionHelp": "Autoriser ou refuser l'accès à la fonctionnalité de conversion des médias.", "AllowOnTheFlySubtitleExtraction": "Autoriser l'extraction des sous-titres à la volée", - "AllowOnTheFlySubtitleExtractionHelp": "Les sous-titres intégrés peuvent être extraits des vidéos et distribués aux clients au format texte pour éviter le transcodage. Sur certains systèmes, cela peut prendre du temps et arrêter la lecture de la vidéo pendant le processus d'extraction. Désactivez cette option pour graver les sous-titres avec un transcodage quand l'appareil ne les prend pas en charge nativement.", + "AllowOnTheFlySubtitleExtractionHelp": "Les sous-titres intégrés peuvent être extraits des vidéos et envoyés vers les clients au format texte afin d'éviter le transcodage vidéo. Sur certains systèmes, cela peut prendre du temps et arrêter la lecture de la vidéo pendant le processus d'extraction. Désactivez cette option pour conserver les sous-titres pendant le transcodage si l'appareil client ne les prend pas en charge nativement.", "AllowRemoteAccess": "Autoriser les connexions distantes à ce serveur Jellyfin.", "AllowRemoteAccessHelp": "Si l'option est désactivée, toutes les connexions distantes seront bloquées.", "AllowedRemoteAddressesHelp": "Liste d'adresses IP ou d'IP/masque de sous-réseau séparées par des virgules qui seront autorisées à se connecter à distance. Si la liste est vide, toutes les adresses distantes seront autorisées.", - "AlwaysPlaySubtitles": "Toujours lancer les sous-titres", + "AlwaysPlaySubtitles": "Toujours afficher les sous-titres", "AlwaysPlaySubtitlesHelp": "Les sous-titres correspondant à la préférence linguistique seront chargés indépendamment de la langue de l'audio.", "AnyLanguage": "N'importe quel langage", "Anytime": "N'importe quand", - "AroundTime": "Aux environs de {0}", + "AroundTime": "Aux environs de", "Artists": "Artistes", "AsManyAsPossible": "Autant que possible", "Ascending": "Croissant", @@ -45,13 +45,13 @@ "BirthLocation": "Lieu de naissance", "BirthPlaceValue": "Lieu de naissance : {0}", "Blacklist": "Liste noire", - "BookLibraryHelp": "Les livres audios et numériques sont supportés. Consultez le {0}Guide de nommage pour livre{1}.", + "BookLibraryHelp": "Les livres audios et numériques sont supportés. Consultez le {0} guide de nommage pour livre {1}.", "Books": "Livres", "Box": "Boîtier", "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 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.", + "BurnSubtitlesHelp": "Détermine si le serveur doit incruster les sous-titres lors du transcodage de la vidéo. Les performances seront grandement améliorées sans incrustation. Sélectionnez Auto pour incruster par image les formats (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", @@ -100,7 +100,7 @@ "ButtonRemove": "Supprimer", "ButtonRename": "Renommer", "ButtonRepeat": "Répéter", - "ButtonResetEasyPassword": "Réinitialiser le code Easy PIN", + "ButtonResetEasyPassword": "Réinitialiser le code easy PIN", "ButtonResetPassword": "Réinitialiser le mot de passe", "ButtonRestart": "Redémarrer", "ButtonResume": "Reprendre", @@ -130,7 +130,7 @@ "CancelRecording": "Annuler l'enregistrement", "CancelSeries": "Annuler la série", "Categories": "Catégories", - "ChangingMetadataImageSettingsNewContent": "Les modifications des réglages de téléchargement des métadonnées et des visuels ne seront appliquées qu'au nouveau contenu ajouté à votre médiathèque. Pour appliquer ces changements aux titres existants, vous devrez actualiser leurs métadonnées manuellement.", + "ChangingMetadataImageSettingsNewContent": "Les modifications des paramètres de téléchargement des métadonnées et des illustrations ne seront appliquées qu'au contenu nouvellement ajouté à votre médiathèque. Pour appliquer ces changements aux titres pré-existants, vous devrez actualiser leurs métadonnées manuellement.", "ChannelAccessHelp": "Sélectionnez les chaînes à partager avec cet utilisateur. Les administrateurs pourront modifier toutes les chaînes en utilisant le gestionnaire de métadonnées.", "ChannelNameOnly": "Seulement la chaîne {0}", "ChannelNumber": "Numéro de chaîne", @@ -199,10 +199,10 @@ "EditMetadata": "Éditer les métadonnées", "EditSubtitles": "Modifier les sous-titres", "EnableBackdrops": "Images d'arrière-plans", - "EnableBackdropsHelp": "Afficher les images d'arrière-plans sur certaines pages pendant la navigation dans la médiathèque.", + "EnableBackdropsHelp": "Afficher des images d'arrière-plans sur certaines pages pendant la navigation dans la médiathèque.", "EnableCinemaMode": "Mode cinéma", "EnableColorCodedBackgrounds": "Fonds avec code couleur", - "EnableDisplayMirroring": "Partage d'écran", + "EnableDisplayMirroring": "Affichage en miroir", "EnableExternalVideoPlayers": "Lecteurs vidéo externes", "EnableExternalVideoPlayersHelp": "Une liste des lecteurs externes sera affichée au lancement de la lecture d'une vidéo.", "EnableHardwareEncoding": "Activer l'encodage matériel", @@ -231,7 +231,7 @@ "EveryNDays": "Tous les {0} jours", "ExitFullscreen": "Sortir du plein écran", "ExtraLarge": "Très Grand", - "ExtractChapterImagesHelp": "L'extraction d'images de chapitre permettra aux applications d'afficher des menus visuels pour la sélection des scènes. Le processus peut être long, consommateur de ressources et peut nécessiter de nombreux gigaoctets de stockage. Il s'exécute lorsque des vidéos sont découvertes et également comme tâche planifiée. La planification peut être modifiée dans les options du planificateur de tâches. Il n'est pas conseillé d'exécuter cette tâche pendant les heures d'usage intensif.", + "ExtractChapterImagesHelp": "L'extraction d'images de chapitre permettra aux applications d'afficher des menus visuels pour la sélection des scènes. Le processus peut être lent, gourmand en ressources et peut nécessiter plusieurs gigaoctets de stockage. Il s'exécute lorsque des vidéos sont découvertes et également comme tâche planifiée. L'horaire est configurable dans les options du planificateur de tâches. Il n'est pas recommandé d'exécuter cette tâche pendant les heures d'usage intensif.", "FFmpegSavePathNotFound": "Nous ne pouvons pas localiser FFmpeg en utilisant le chemin que vous avez saisi. FFprobe est également nécessaire et doit exister dans le même dossier. Ces composants sont généralement regroupés dans le même téléchargement. Veuillez vérifier le chemin et essayer à nouveau.", "FastForward": "Avance rapide", "Favorite": "Favori", @@ -274,7 +274,7 @@ "HeaderAddUser": "Ajouter un utilisateur", "HeaderAdditionalParts": "Parties additionelles", "HeaderAdmin": "Administrateur", - "HeaderAlbumArtists": "Artistes d'album", + "HeaderAlbumArtists": "Artistes", "HeaderAlert": "Alerte", "HeaderAllowMediaDeletionFrom": "Autoriser la suppression de médias à partir de", "HeaderApiKey": "Clé API", @@ -569,7 +569,7 @@ "LabelEmbedAlbumArtDidl": "Intégrer les images d'album dans le DIDL", "LabelEmbedAlbumArtDidlHelp": "Certains appareils préfèrent cette méthode pour obtenir les images d'album. D'autres peuvent échouer à lire avec cette option activée.", "LabelEnableAutomaticPortMap": "Autoriser le mapping automatique de port", - "LabelEnableAutomaticPortMapHelp": "Essayer de mapper automatiquement le port public au port local via UPnP. Cela peut ne pas fonctionner avec certains modèles de routeurs. La modification de ce paramètre ne sera effective qu’après redémarrage du serveur.", + "LabelEnableAutomaticPortMapHelp": "Mapper automatiquement les ports publics vers des ports locaux via UPnP. Cela peut ne pas fonctionner avec certains modèles de routeurs. La modification de ce paramètre ne prendra effet qu'après redémarrage du serveur.", "LabelEnableBlastAliveMessages": "Diffuser des message de présence", "LabelEnableBlastAliveMessagesHelp": "Activer cette option si le serveur n'est pas détecté de manière fiable par les autres appareils UPnP sur votre réseau.", "LabelEnableDlnaClientDiscoveryInterval": "Intervalle de découverte des clients (secondes)", @@ -624,7 +624,7 @@ "LabelKidsCategories": "Catégories jeunesse :", "LabelKodiMetadataDateFormat": "Format de la date de sortie :", "LabelKodiMetadataDateFormatHelp": "Toutes les dates des fichiers NFO seront anlysés en utilisant ce format.", - "LabelKodiMetadataEnableExtraThumbs": "Copier les extrafanart vers le champ extrathumbs", + "LabelKodiMetadataEnableExtraThumbs": "Copier les extrafanart dans les champs extrathumbs", "LabelKodiMetadataEnableExtraThumbsHelp": "Pendant le téléchargement, les images peuvent être enregistrées en tant qu'extrafanart et extrathumbs pour améliorer la compatibilité avec le skin Kodi.", "LabelKodiMetadataEnablePathSubstitution": "Activer la substitution des chemins", "LabelKodiMetadataEnablePathSubstitutionHelp": "Active la substitution du chemin des images en utilisant les paramètres de substitution des chemins du serveur.", @@ -658,7 +658,7 @@ "LabelMetadataDownloadLanguage": "Langue de téléchargement préférée :", "LabelMetadataDownloadersHelp": "Activez et classez vos sources de téléchargement de métadonnées préférées dans l'ordre de priorité. Les plus basses seront utilisées uniquement pour remplir les informations manquantes.", "LabelMetadataPath": "Chemin des métadonnées :", - "LabelMetadataPathHelp": "Veuillez spécifier un emplacement personnalisé pour les images et les métadonnées téléchargées.", + "LabelMetadataPathHelp": "Veuillez spécifier un emplacement personnalisé pour les illustrations et les métadonnées téléchargées.", "LabelMetadataReaders": "Lecteurs de métadonnées :", "LabelMetadataReadersHelp": "Classez vos sources locales de métadonnées préférées dans l'ordre de priorité. Le premier fichier trouvé sera lu.", "LabelMetadataSavers": "Enregistreurs de métadonnées :", @@ -692,7 +692,7 @@ "LabelNumberOfGuideDays": "Nombre de jours de données du guide à télécharger :", "LabelNumberOfGuideDaysHelp": "Télécharger plus de journées du guide permet de programmer des enregistrements plus longtemps à l'avance et de visualiser plus de contenus, mais prendra également plus de temps. Automatique permettra une sélection automatique basée sur le nombre de chaînes.", "LabelOptionalNetworkPath": "(Optionnel) Dossier réseau partagé :", - "LabelOptionalNetworkPathHelp": "Si le dossier est partagé sur votre réseau, donner accès au chemin du dossier réseau peut autoriser les applications Jellyfin sur d'autres appareils à avoir accès à ses fichiers directement.", + "LabelOptionalNetworkPathHelp": "Si le dossier est partagé sur votre réseau, donner le chemin d'accès au dossier réseau peut permettre aux applications Jellyfin sur d'autres appareils d'avoir accès à ses fichiers directement. Par exemple, {0} ou {1}.", "LabelOriginalAspectRatio": "Ratio d'aspect original :", "LabelOriginalTitle": "Titre original :", "LabelOverview": "Synopsis :", @@ -737,8 +737,8 @@ "LabelRemoteClientBitrateLimit": "Limite de débit de streaming Internet (Mbps) :", "LabelRemoteClientBitrateLimitHelp": "Une limite de débit optionnelle par streaming pour les connexions hors du réseau local. Utile pour éviter que les appareils ne demandent un débit supérieur à ce que votre connexion internet peu fournir. Cela peut augmenter la charge du processeur de votre serveur pour transcoder les vidéos à la volée à un débit plus faible.", "LabelRuntimeMinutes": "Durée (minutes) :", - "LabelSaveLocalMetadata": "Enregistrer les images dans les dossiers multimédia", - "LabelSaveLocalMetadataHelp": "L'enregistrement des images dans les dossiers multimédia les placera à un endroit où elles seront facilement modifiables.", + "LabelSaveLocalMetadata": "Enregistrer les illustrations dans les dossiers des médias", + "LabelSaveLocalMetadataHelp": "L'enregistrement des illustrations dans les dossiers des médias les placera à un endroit où elles seront facilement modifiables.", "LabelScheduledTaskLastRan": "Dernière exécution {0}, durée {1}.", "LabelScreensaver": "Économiseur d'écran :", "LabelSeasonNumber": "Numéro de saison :", @@ -1038,7 +1038,7 @@ "OptionMissingEpisode": "Épisodes manquantes", "OptionMonday": "Lundi", "OptionNameSort": "Nom", - "OptionNew": "Nouveau...", + "OptionNew": "Nouveau…", "OptionNone": "Aucun", "OptionOnAppStartup": "Au démarrage de l'application", "OptionOnInterval": "Par intervalle", @@ -1164,7 +1164,7 @@ "Screenshot": "Capture d'écran", "Screenshots": "Captures d'écran", "Search": "Recherche", - "SearchForCollectionInternetMetadata": "Rechercher sur Internet les images et les métadonnées", + "SearchForCollectionInternetMetadata": "Rechercher les illustrations et les métadonnées sur Internet", "SearchForMissingMetadata": "Rechercher les métadonnées manquantes", "SearchForSubtitles": "Rechercher des sous-titres", "SearchResults": "Résultats de la recherche", @@ -1399,7 +1399,7 @@ "AuthProviderHelp": "Sélectionner un fournisseur d'authentification pour authentifier le mot de passe de cet utilisateur.", "PasswordResetProviderHelp": "Choisissez un Fournisseur de réinitialisation de mot de passe à utiliser lorsqu'un utilisateur demande la réinitialisation de son mot de passe", "HeaderHome": "Accueil", - "LabelUserLoginAttemptsBeforeLockout": "Essais manqués restants avant verrouillage utilisateur :", + "LabelUserLoginAttemptsBeforeLockout": "Tentatives de connexion échouées avant que l'utilisateur ne soit verrouillé :", "DashboardOperatingSystem": "Système d'Exploitation: {0}", "DashboardArchitecture": "Architecture : {0}", "LaunchWebAppOnStartup": "Démarrer l'interface web dans mon navigateur quand le serveur est démarré", @@ -1409,7 +1409,7 @@ "MessageNoServersAvailable": "Aucun serveur n'a été trouvé en utilisant la recherche automatique de serveur.", "OptionBanner": "Bannière", "OptionList": "Liste", - "OptionLoginAttemptsBeforeLockout": "Détermine le nombre de tentative de connexion infructueuse avant le blocage d'accès.", + "OptionLoginAttemptsBeforeLockout": "Définis le nombre de tentatives de connexion échouées avant blocage du compte.", "OptionPoster": "Affiche", "OptionPosterCard": "Affichette", "OptionThumb": "Vignette", @@ -1437,10 +1437,10 @@ "LabelVideoBitrate": "Débit vidéo :", "LabelTranscodingProgress": "Progression du transcodage :", "LabelTranscodingFramerate": "Taux de rafraîchissement du transcodage :", - "LabelPleaseRestart": "Les changements prendront effet lors d'un rechargement manuel du client web.", + "LabelPleaseRestart": "Les changements prendront effet après un rechargement manuel du client web.", "LabelPlayMethod": "Méthode de lecture :", "LabelPlayer": "Lecteur :", - "LabelBaseUrl": "Lien d'origine :", + "LabelBaseUrl": "URL de base :", "LabelAudioSampleRate": "Taux d’échantillonnage audio :", "LabelAudioCodec": "Codec audio :", "LabelAudioChannels": "Canaux audio :", @@ -1448,7 +1448,7 @@ "FetchingData": "Récuperer des données suplémentaires", "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.", + "LabelBaseUrlHelp": "Ajoute un sous-répertoire personnalisé à l'adresse URL du serveur. Par exemple : http://example.com/< ;baseurl> ;", "HeaderFavoritePeople": "Personnes préférées", "OptionRandom": "Aléatoire", "ButtonSplit": "Séparer", @@ -1458,16 +1458,14 @@ "MessageConfirmAppExit": "Voulez-vous quitter ?", "LabelVideoResolution": "Résolution vidéo :", "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 :", - "CopyStreamURLError": "Il y a eu une erreur lors de la copie du URL.", + "CopyStreamURLError": "Une erreur est survenue lors de la copie de l'URL.", "AskAdminToCreateLibrary": "Demander à un administrateur de créer une médiathèque.", - "AllowFfmpegThrottlingHelp": "Quand le transcodage ou le remultiplexage est suffisamment 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.", - "AllowFfmpegThrottling": "Adapter la Vitesse du Transcodage", - "NoCreatedLibraries": "Il semblerait que vous n'ayez créé aucune librairie. {0}Voulez-vous en créer une maintenant ?{1}", + "AllowFfmpegThrottlingHelp": "Quand le transcodage ou le remultiplexage est suffisamment en avant de la position de lecture, le processus se mettra en pause afin d’économiser des ressources. Plus utile lors d’une lecture continue. À désactiver en cas de problèmes de lecture.", + "AllowFfmpegThrottling": "Adapter la vitesse du transcodage", + "NoCreatedLibraries": "Il semble que vous n'ayez pas encore créé de bibliothèques. {0}Voulez-vous en créer une maintenant ?{1}", "PlaybackErrorNoCompatibleStream": "Ce client n'est pas compatible avec le média et le serveur n'envoie pas de format 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.", @@ -1495,8 +1493,63 @@ "ListPaging": "{0}-{1} de {2}", "WriteAccessRequired": "Le serveur Jellyfin a besoin d'un accès en écriture à ce dossier. Merci de vérifier l’accès en écriture et réessayez.", "PathNotFound": "Le chemin d'accès n'a pas pu être trouvé. Merci de le vérifier et de réessayer.", - "YadifBob": "Yadif Bob", - "Yadif": "Yadif", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", "LabelDeinterlaceMethod": "Méthode de désentrelacement :", - "DeinterlaceMethodHelp": "Sélectionnes la méthode de désentrelacement à utiliser lors du transcodage de contenu entrelacé." + "DeinterlaceMethodHelp": "Sélectionnes la méthode de désentrelacement à utiliser lors du transcodage de contenu entrelacé.", + "LabelLibraryPageSize": "Taille de la page de la médiathèque :", + "LabelLibraryPageSizeHelp": "Définit la quantité d'éléments à afficher sur une page de médiathèque. Définir à 0 afin de désactiver la pagination.", + "UnsupportedPlayback": "Jellyfin ne peut pas décoder du contenu protégé par un système de gestion des droits numériques, mais une tentative de lecture sera effectuée sur tout le contenu, y compris les titres protégés. Certains fichiers peuvent apparaître complètement noir, du fait de protections ou de fonctionnalités non supportées, comme les titres interactifs.", + "MessageUnauthorizedUser": "Vous n'êtes pas autorisé à accéder au serveur pour le moment. Veuillez contacter l'administrateur de votre serveur pour plus d'informations.", + "ButtonTogglePlaylist": "Liste de lecture", + "ButtonToggleContextMenu": "Plus", + "Filter": "Filtre", + "New": "Nouveau", + "HeaderFavoritePlaylists": "Listes de lecture favorites", + "TabDVR": "DVR", + "LabelChromecastVersion": "Version de Chromecast", + "LabelEnableHttpsHelp": "Autorise le serveur à écouter les requêtes HTTPS sur le port configurée. Un certificat valide doit être configuré pour permettre ce mode de fonctionnement.", + "LabelEnableHttps": "Activer HTTPS", + "HeaderServerAddressSettings": "Paramètres adresses serveur", + "HeaderRemoteAccessSettings": "Paramètres d'accès distant", + "HeaderHttpsSettings": "Paramètres HTTPS", + "HeaderDVR": "Enregistreur vidéo numérique", + "ApiKeysCaption": "Liste actuelle des clés API actives", + "SaveChanges": "Enregistrer les modifications", + "LabelRequireHttpsHelp": "Si activé, le serveur va automatiquement rediriger toutes les requêtes en HTTP vers HTTPS. Cette option n'a aucun effet si le serveur n'écoute pas HTTPS.", + "LabelRequireHttps": "Nécessite HTTPS", + "LabelNightly": "De nuit", + "LabelStable": "Stable", + "EnableDetailsBanner": "Bannière des détails", + "EnableDetailsBannerHelp": "Affichez une image de bannière en haut de la page de détails de l'article.", + "HeaderSyncPlaySelectGroup": "Rejoindre un groupe", + "LabelSyncPlayAccessCreateAndJoinGroups": "Autoriser l'utilisateur à créer un ou rejoindre un groupe", + "LabelSyncPlayLeaveGroupDescription": "Désactiver SyncPlay", + "LabelSyncPlayLeaveGroup": "Quitter le groupe", + "LabelSyncPlayNewGroupDescription": "Créer un nouveau groupe", + "LabelSyncPlayNewGroup": "Nouveau groupe", + "LabelSyncPlaySyncMethod": "Méthode de synchronisation :", + "LabelSyncPlayPlaybackDiff": "Décalage de la lecture :", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Décalage de temps avec le serveur  :", + "HeaderSyncPlayEnabled": "SyncPlay activé", + "MessageSyncPlayLibraryAccessDenied": "L'accès à ce contenu est restreint.", + "MessageSyncPlayJoinGroupDenied": "Permission requise pour utiliser SyncPlay.", + "MessageSyncPlayCreateGroupDenied": "Permission requise pour créer un groupe.", + "MessageSyncPlayGroupDoesNotExist": "Impossible de rejoindre le groupe car il n'existe pas.", + "MessageSyncPlayPlaybackPermissionRequired": "Autorisation de lecture requise.", + "MessageSyncPlayNoGroupsAvailable": "Aucun groupe disponible. Commencez par lancer quelque chose.", + "MessageSyncPlayGroupWait": "{0} est en train de charger...", + "MessageSyncPlayUserLeft": "{0} a quitté le groupe.", + "MessageSyncPlayUserJoined": "{0} a rejoint le groupe.", + "MessageSyncPlayDisabled": "SyncPlay désactivé.", + "MessageSyncPlayEnabled": "SyncPlay activé.", + "LabelSyncPlayAccess": "Accès SyncPlay", + "LabelSyncPlayAccessNone": "Désactivé pour cet utilisateur", + "LabelSyncPlayAccessJoinGroups": "Autoriser l'utilisateur à rejoindre un groupe", + "SyncPlayAccessHelp": "Sélectionner le niveau d'accès de cet utilisateur pour la fonctionnalité SyncPlay. SyncPlay permet de synchroniser la lecture avec d'autres utilisateurs.", + "MessageSyncPlayErrorMedia": "Impossible d'activer SyncPlay ! Erreur média.", + "MessageSyncPlayErrorMissingSession": "Impossible d'activer SyncPlay ! Session manquante.", + "MessageSyncPlayErrorNoActivePlayer": "Aucun player actif trouvé. SyncPlay a été désactivé.", + "MessageSyncPlayErrorAccessingGroups": "Une erreur s'est produite pendant l'accès à la liste de groupes." } diff --git a/src/strings/gsw.json b/src/strings/gsw.json index a154e4fc8e..e396313856 100644 --- a/src/strings/gsw.json +++ b/src/strings/gsw.json @@ -7,7 +7,7 @@ "ButtonQuickStartGuide": "Schnellstart Instruktione", "ButtonResetPassword": "Passwort zrug setze", "ButtonSave": "Speichere", - "ButtonSignOut": "Sign out", + "ButtonSignOut": "Uslogge", "ButtonSort": "Sortiere", "ChannelAccessHelp": "Wähl en Kanal us, um de mit dem User z'teile. Administratore werded immer d'Möglichkeit ha alli Kanäl mitm Metadate Manager z'bearbeite.", "Continuing": "Fortlaufend", @@ -29,7 +29,7 @@ "HeaderLatestSongs": "Letschti Songs", "HeaderLatestTrailers": "Letschti Trailer", "HeaderManagement": "Verwaltig", - "HeaderNextUp": "Als nächts", + "HeaderNextUp": "Als Nächstes", "HeaderNowPlaying": "Jetz am spelle", "HeaderParentalRating": "Parental Rating", "HeaderPaths": "Pfad", @@ -131,28 +131,26 @@ "Wednesday": "Mittwoch", "WelcomeToProject": "Willkomme bi Jellyfin!", "WizardCompleted": "Das esch alles wo mer momentan müend wüsse. Jellyfin het i de zwüscheziit agfange informatione über diini medie-bibliothek z'sammle. Lueg der es paar vo eusne Apps a und denn klick uf Beende um zum Server Dashboard z'cho.", - "Albums": "Albom", - "Artists": "Könstler", - "Books": "Büecher", - "Channels": "Kanäu", - "Collections": "Sammlige", - "Favorites": "Favorite", + "Albums": "Alben", + "Artists": "Künstler", + "Books": "Bücher", + "Channels": "Kanäle", + "Collections": "Sammlungen", + "Favorites": "Favoriten", "Folders": "Ordner", "Genres": "Genres", - "HeaderAlbumArtists": "Albom-Könstler", - "HeaderContinueWatching": "Wiiterluege", - "HeaderFavoriteAlbums": "Lieblingsalbe", - "HeaderFavoriteArtists": "Lieblings-Interprete", - "HeaderFavoriteEpisodes": "Lieblingsepisode", - "HeaderFavoriteShows": "Lieblingsserie", + "HeaderAlbumArtists": "Album-Künstler", + "HeaderContinueWatching": "weiter schauen", + "HeaderFavoriteAlbums": "Lieblingsalben", + "HeaderFavoriteArtists": "Lieblings-Künstler", + "HeaderFavoriteEpisodes": "Lieblingsepisoden", + "HeaderFavoriteShows": "Lieblingsserien", "HeaderFavoriteSongs": "Lieblingslieder", - "HeaderLiveTV": "Live-Färnseh", + "HeaderLiveTV": "Live-Fernseh", "HeaderRecordingGroups": "Ufnahmegruppe", - "LabelIpAddressValue": "IP-Adrässe: {0}", "LabelRunningTimeValue": "Loufziit: {0}", "MessageApplicationUpdated": "Jellyfin Server esch aktualisiert worde", "MessageNamedServerConfigurationUpdatedWithValue": "De Serveriistöuigsberiich {0} esch aktualisiert worde", - "MessageServerConfigurationUpdated": "Serveriistöuige send aktualisiert worde", "Movies": "Film", "Photos": "Fotis", "Playlists": "Wedergabeliste", diff --git a/src/strings/he.json b/src/strings/he.json index 381b9bc181..a3e4bc9326 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -1,14 +1,14 @@ { "Actor": "שחקן", "Add": "הוסף", - "AddToCollection": "להוסיף לאוסף", - "AddToPlayQueue": "הוסף לתור הפעלה", + "AddToCollection": "הוסף לאוסף", + "AddToPlayQueue": "הוסף לרשימת ניגון", "AddToPlaylist": "הוסף לרשימת ניגון", - "AdditionalNotificationServices": "עיין בקטלוג התוספים להתקנת שרותי התראות נוספים", + "AdditionalNotificationServices": "עיין ברשימת התוספים להתקנת שירותי התראות נוספים.", "All": "הכל", "AllChannels": "כל הערוצים", "AllEpisodes": "כל הפרקים", - "AllLibraries": "כל הספרייה", + "AllLibraries": "כל הספריות", "Anytime": "בכל עת", "AroundTime": "בסביבות {0}", "AsManyAsPossible": "כמה שיותר", @@ -52,7 +52,7 @@ "ConfirmDeleteItems": "מחיקת פריטים אלה תמחק אותם הן ממערכת הקבצים והן מספריית המדיה שלך. האם אתה בטוח שברצונך להמשיך?", "ConfirmDeletion": "אשר מחיקה", "Continuing": "ממשיך", - "CustomDlnaProfilesHelp": "צור פרופיל מותאם אישית למכשיר חדש או לעקוף פרופיל מערכת", + "CustomDlnaProfilesHelp": "צור פרופיל מותאם אישית למכשיר חדש או לעקיפת פרופיל מערכת.", "DefaultErrorMessage": "אירעה שגיאה בעיבוד הבקשה. בבקשה נסה שוב מאוחר יותר.", "Delete": "מחק", "DeleteImage": "מחק תמונה", @@ -130,7 +130,7 @@ "HeaderSeries": "סדרה", "HeaderSeriesOptions": "אפשרויות סדרה", "HeaderServerSettings": "הגדרות שרת", - "HeaderSetupLibrary": "הגדר את ספריית המדיה שלך.", + "HeaderSetupLibrary": "הגדר את ספריית המדיה שלך", "HeaderSpecialEpisodeInfo": "פרטי אפיזודות מיוחדות", "HeaderSpecialFeatures": "מאפיינים מיוחדים", "HeaderStatus": "מצב", @@ -164,7 +164,7 @@ "LabelChannels": "ערוצים:", "LabelCollection": "אוספים:", "LabelCommunityRating": "דירוג הקהילה:", - "LabelContentType": "סוג התוכן", + "LabelContentType": "סוג התוכן:", "LabelCountry": "מדינה:", "LabelCriticRating": "דירוג ביקורת:", "LabelCurrentPassword": "סיסמא נוכחית:", @@ -221,7 +221,7 @@ "LabelNext": "הבא", "LabelNotificationEnabled": "אפשר התראה זו", "LabelNumber": "מספר:", - "LabelNumberOfGuideDays": "מספר ימי לוח שידורים להורדה", + "LabelNumberOfGuideDays": "מספר ימים להורדה מלוח השידורים:", "LabelNumberOfGuideDaysHelp": "הורדת יותר ימי לוח שידורים מאפשרת יכולת לתכנן ולראות יותר תוכניות קדימה, אבל גם זמן ההורדה יעלה. מצב אוטומטי ייקבע לפני מספר הערוצים.", "LabelOriginalAspectRatio": "יחס גובה-רוחב מקורי:", "LabelOriginalTitle": "כותרת מקורית:", @@ -234,14 +234,14 @@ "LabelPersonRoleHelp": "דוגמה: נהג משאית גלידה", "LabelPlaceOfBirth": "מקום לידה:", "LabelPlaylist": "רשימת ניגון:", - "LabelPreferredDisplayLanguage": "שפת ממשק מועדפת", - "LabelPreferredDisplayLanguageHelp": "תרגום הממשק של Jellyfin הוא תהליך ממושך", + "LabelPreferredDisplayLanguage": "שפת תצוגה מועדפת:", + "LabelPreferredDisplayLanguageHelp": "תרגום Jellyfin הוא תהליך מתמשך.", "LabelPrevious": "הקודם", - "LabelProfileAudioCodecs": "מקודדי צליל", - "LabelProfileCodecs": "מקודדים", + "LabelProfileAudioCodecs": "מקודדי שמע:", + "LabelProfileCodecs": "מקודדים:", "LabelProfileVideoCodecs": "‮מקודדי וידאו:", "LabelPublicHttpPort": "מספר פורט HTTP פומבי", - "LabelReadHowYouCanContribute": "למד איך תוכל לתרום", + "LabelReadHowYouCanContribute": "למד איך אתה יכול לתרום.", "LabelRecord": "הקלטה:", "LabelRefreshMode": "מצב רענון:", "LabelReleaseDate": "תאריך הוצאה:", @@ -254,17 +254,17 @@ "LabelSortTitle": "מיין כותרת:", "LabelStartWhenPossible": "התחל ברגע שניתן:", "LabelStatus": "סטטוס:", - "LabelStopWhenPossible": "הפסק ברגע שאפשר", + "LabelStopWhenPossible": "הפסק כשיתאפשר:", "LabelTagline": "שורת תיוג:", "LabelTime": "זמן:", - "LabelTimeLimitHours": "הגבלת זמן (בשעות)", + "LabelTimeLimitHours": "מגבלת זמן (שעות):", "LabelTitle": "כותרת:", "LabelTrackNumber": "קטע מספר:", "LabelTriggerType": "סוגר טריגר:", "LabelType": "סוג:", "LabelUseNotificationServices": "השתמש בשירותים הבאים:", "LabelUser": "משתמש:", - "LabelUserLibrary": "ספריית משתמש", + "LabelUserLibrary": "ספריית משתמש:", "LabelYear": "שנה:", "LabelYourFirstName": "שמך הפרטי:", "LabelYoureDone": "סיימת!", @@ -277,12 +277,12 @@ "MaxParentalRatingHelp": "תוכן עם דירוג גובה יותר יוסתר מהמשתמש.", "MessageAreYouSureDeleteSubtitles": "האם אתה בטוח שברצונך למחוק קובץ כתובית זה?", "MessageConfirmRecordingCancellation": "‏האם אתה בטוח שברצונך לבטל הקלטה זו?", - "MessageDownloadQueued": "הורד תור", + "MessageDownloadQueued": "הורדה נוספה לתור.", "MessageItemSaved": "הפריט נשמר.", "MessageItemsAdded": "פריטים נוספו.", - "MessageLeaveEmptyToInherit": "השאר ריק כדי לרשת את ההגדרות מפריט אב, או את ערך ברירת המחדל הגלובלי.", + "MessageLeaveEmptyToInherit": "השאר ריק כדי לרשת את ההגדרות מפריט אב או את ערך ברירת המחדל הגלובלי.", "MessageNothingHere": "אין כאן כלום.", - "MessagePleaseEnsureInternetMetadata": "בבקשה וודא כי הורדת מידע מהאינטרנט מאופשרת", + "MessagePleaseEnsureInternetMetadata": "אנא וודא כי הורדת מטא-דאטה מהאינטרנט מופעלת.", "MinutesAfter": "דקות אחרי", "MinutesBefore": "דקות לפני", "Monday": "שני", @@ -397,7 +397,7 @@ "RecentlyWatched": "נצפה לאחרונה", "Record": "הקלט", "RecordSeries": "הקלט סדרה", - "RecordingCancelled": "בטל הקלטה", + "RecordingCancelled": "הקלטה בוטלה.", "RecordingScheduled": "ההקלטה מתוזמנת.", "Refresh": "רענון", "RefreshDialogHelp": "המטא נתונים מתרעננים על סמך הגדרות ושירותי אינטרנט שמופעלים בלוח המחוונים של מרכז אמבי.", @@ -419,7 +419,7 @@ "SearchForMissingMetadata": "חפש מטא נתונים חסרים", "SearchForSubtitles": "חיפוש של כתוביות", "SearchResults": "תוצאות חיפוש", - "SeriesCancelled": "בטל סדרות", + "SeriesCancelled": "סדרה בוטלה.", "SeriesRecordingScheduled": "הקלטת סדרה מתוזמנת.", "SeriesSettings": "הגדרות סדרה", "SeriesYearToPresent": "{0} - היום", @@ -510,8 +510,8 @@ "Artists": "אומנים", "Books": "ספרים", "Absolute": "מוחלט", - "AccessRestrictedTryAgainLater": "הגישה כרגע מוגבלת. אנא נסה שוב מאוחר יותר.", - "AddedOnValue": "נוסף {0}", + "AccessRestrictedTryAgainLater": "הגישה מוגבלת כרגע, נסה שוב במועד מאוחר יותר.", + "AddedOnValue": "נוספו {0}", "Blacklist": "רשימה שחורה", "Banner": "באנר", "Auto": "אוטומטי", @@ -528,10 +528,10 @@ "Audio": "שמע", "AspectRatio": "יחס גובה-רוחב", "AlwaysPlaySubtitlesHelp": "כתוביות בשפה המועדפת ייטענו ללא קשר לשפת השמע.", - "AlwaysPlaySubtitles": "הפעל כתוביות תמיד", + "AlwaysPlaySubtitles": "הפעל תמיד", "AllowRemoteAccessHelp": "אם לא מסומן, כל החיבורים המרוחקים ייחסמו.", "AllowRemoteAccess": "אפשר חיבור מרוחק לשרת Jellyfin זה.", - "AllowMediaConversionHelp": "אפשר או חסום גישה להמרת מדיה.", + "AllowMediaConversionHelp": "הענק או דחה גישה להמרת מדיה.", "Aired": "שודר", "AirDate": "תאריך שידור", "Yesterday": "אתמול", @@ -548,8 +548,8 @@ "HeaderContinueWatching": "המשך לצפות", "AllowOnTheFlySubtitleExtraction": "אפשר חילוץ כתוביות בזמן אמת", "AllowHWTranscodingHelp": "אפשר למלקט לקודד הזרמות בזמן אמת. זה עשוי לעזור בהפחתת הקידוד שנעשה ע\"י השרת.", - "AllComplexFormats": "כל הפורמטים המורכבים (ASS, SSA, VOBSUB, PGS, SUB\\IDX וכדומה)", - "AddItemToCollectionHelp": "הוסף פריטים לאוספים ע\"י חיפושם ושימוש בלחצן ימני או הקשה על התפריט כדי להוסיף אותם לאוסף.", + "AllComplexFormats": "כל הפורמטים המורכבים (ASS, SSA, VOBSUB, PGS, SUB/IDX)", + "AddItemToCollectionHelp": "הוסף חפצים לאוסף על ידי חיפושם ושימוש בתפריט הלחצן הימני או לחצן התפריט כדי להוסיפם לאוסף.", "Songs": "שירים", "Shows": "סדרות", "DownloadsValue": "{0} הורדות", @@ -741,5 +741,36 @@ "Browse": "עיין", "BoxRear": "מארז (מאחור)", "BookLibraryHelp": "ניתן להוסיף ספרים מוקלטים וספרים כתובים. עיינו {0}במדריך מתן שמות לספרים{1}.", - "Desktop": "שולחן עבודה" + "Desktop": "שולחן עבודה", + "MessageUnauthorizedUser": "אין לך גישה לשרת ברגע זה. אנא צור קשר עם מנהל השרת למידע נוסף.", + "MessageDeleteTaskTrigger": "האם אתה בטוח שברצונך למחוק את מפעיל המשימה הזה?", + "LastSeen": "נראה לאחרונה ב-{0}", + "PersonRole": "כ-{0}", + "ListPaging": "{0}-{1} מתוך {2}", + "EveryXHours": "בכל {0} שעות", + "OnApplicationStartup": "באתחול היישום", + "DeleteUserConfirmation": "האם אתה בטוח שברצונך למחוק את המשתמש?", + "DeleteDeviceConfirmation": "האם אתה בטוח שברצונך למחוק את המכשיר? הוא יופיע שוב בפעם הבאה שמשתמש ייכנס באמצעותו.", + "ColorSpace": "מרחב צבע", + "CinemaModeConfigurationHelp": "מצב קולנוע מביא את חוויית הקולנוע היישר אל הסלון עם האפשרות להפעיל טריילרים וקדימונים מותאמים אישית לפני הסרט.", + "ChannelAccessHelp": "בחר את הערוצים לשיתוף עם משתמש זה. מנהלים יוכלו לערוך את כל הערוצים בעזרת \"מנהל המטא-דאטה\".", + "ButtonResetEasyPassword": "אתחל קוד פין פשוט", + "ButtonOff": "כיבוי", + "ButtonLibraryAccess": "הרשאות גישה לספרייה", + "BurnSubtitlesHelp": "מחליט אם על השרת לצרוב כתוביות בזמן קידוד וידאו. הימנעות מכך תשפר מאוד את הביצועים. בחר \"אוטומטי\" לצריבת כתוביות על בסיס פורמט תמונה (VOBSUB, PGS, SUB, IDX) וכתוביות ASS או SSA מסויימות.", + "Artist": "אמן", + "AllowedRemoteAddressesHelp": "רשימת IP \\ מיסוך רשת המופרדת בפסיקים עבור רשתות שיורשו להתחבר מרחוק. במידה ותישאר ריקה, כל הכתובות יורשו להתחבר.", + "Album": "אלבום", + "AlbumArtist": "אמן האלבום", + "LabelSortOrder": "מיין סדר:", + "ShowYear": "הצג שנה", + "ShowTitle": "הצג כותרת", + "DropShadow": "צייר צל", + "Playlists": "רשימות הפעלה", + "Raised": "מורם", + "LabelSpecialSeasonsDisplayName": "שם תצוגת \"עונה מיוחדת\":", + "LabelSource": "מקור:", + "LabelSoundEffects": "אפקטי סאונד:", + "ButtonTogglePlaylist": "רשימת ניגון", + "ButtonToggleContextMenu": "עוד" } diff --git a/src/strings/hi-in.json b/src/strings/hi-in.json index 122f4aa98b..7a51a4f695 100644 --- a/src/strings/hi-in.json +++ b/src/strings/hi-in.json @@ -21,11 +21,11 @@ "AddToCollection": "संग्रह में जोड़ें", "Add": "जोड़ें", "Actor": "अभिनेता", - "AccessRestrictedTryAgainLater": "अभी प्रवेश प्रतिबंधित है। थोड़ी देर बाद कोशिश करें।", + "AccessRestrictedTryAgainLater": "वर्तमान में पहुंच प्रतिबंधित है। कृपया बाद में पुनः प्रयास करें.", "AllowHWTranscodingHelp": "ट्यूनर को निरंतर रूप से धाराओं को ट्रांसकोड करने दें। यह सर्वर द्वारा ट्रांसकोडिंग को कम करने में मदद कर सकता है।", "AllLanguages": "सभी भाषाएं", "AllEpisodes": "सभी प्रकरण", - "AllComplexFormats": "सभी जटिल प्रारूप (ASS, SSA, VOBSUB, PGS, SUB / IDX, आदि)", + "AllComplexFormats": "सभी जटिल प्रारूप (ASS, SSA, VOBSUB, PGS, SUB, IDX,…)", "AllChannels": "सभी चैनल्स", "Alerts": "चेतावनियां", "Albums": "संग्रहिकाएँ", @@ -34,5 +34,83 @@ "AddedOnValue": "जोड़ दिया", "AddToPlaylist": "प्लेलिस्ट में जोड़ें", "AllowMediaConversionHelp": "मीडिया परिवर्तन के लिये अनुमति दें", - "AllowMediaConversion": "मीडिया रूपांतरण की अनुमति दें" + "AllowMediaConversion": "मीडिया रूपांतरण की अनुमति दें", + "ButtonOk": "ठीक", + "ButtonOff": "बंद", + "ButtonNextTrack": "आगे धावन पथ", + "ButtonNew": "नया", + "ButtonNetwork": "संजाल", + "ButtonMore": "अधिक", + "ButtonManualLogin": "मैनुअल लॉगिन", + "ButtonLibraryAccess": "पुस्तकालय का उपयोग", + "ButtonLearnMore": "और अधिक जानें", + "ButtonInfo": "जानकारी", + "ButtonHome": "घर", + "ButtonHelp": "मदद", + "ButtonGuide": "मार्गदर्शक", + "ButtonGotIt": "समझ गया", + "ButtonFullscreen": "पूर्ण स्क्रीन", + "ButtonForgotPassword": "पासवर्ड भूल गए", + "ButtonFilter": "निस्पंदन", + "ButtonEditOtherUserPreferences": "इस उपयोगकर्ता की प्रोफ़ाइल, छवि और व्यक्तिगत प्राथमिकताएँ संपादित करें।", + "ButtonEditImages": "छवियों को संपादित करें", + "ButtonEdit": "संपादित करें", + "ButtonDownload": "डाउनलोड", + "ButtonDown": "नीचे", + "ButtonDeleteImage": "छवि हटाएं", + "ButtonDelete": "हटाएं", + "ButtonConnect": "जुडिये", + "ButtonChangeServer": "सर्वर बदलें", + "ButtonCancel": "रद्द करना", + "ButtonBack": "वापस", + "ButtonAudioTracks": "ऑडियो ट्रैक्स", + "ButtonArrowUp": "ऊपर", + "ButtonArrowRight": "दाएँ", + "ButtonArrowLeft": "बाएं", + "ButtonArrowDown": "नीचे", + "ButtonAddUser": "उपयोगकर्ता जोड़ें", + "ButtonAddServer": "सर्वर जोड़े", + "ButtonAddScheduledTaskTrigger": "ट्रिगर जोड़ें", + "ButtonAddMediaLibrary": "मीडिया लाइब्रेरी जोड़ें", + "ButtonAddImage": "छवि जोड़ें", + "ButtonAdd": "जोड़ना", + "UnsupportedPlayback": "Jellyfin DRM द्वारा संरक्षित सामग्री को डिक्रिप्ट नहीं कर सकता है, लेकिन सभी सामग्री की परवाह किए बिना, संरक्षित शीर्षकों सहित प्रयास किया जाएगा। एन्क्रिप्शन या अन्य असमर्थित सुविधाओं जैसे इंटरेक्टिव शीर्षक के कारण कुछ फाइलें पूरी तरह से काली दिखाई दे सकती हैं।", + "BoxRear": "बॉक्स (पीछे)", + "Box": "डिब्बा", + "Books": "पुस्तकें", + "BookLibraryHelp": "ऑडियो और पाठ्य पुस्तकें समर्थित हैं। {0} पुस्तक नामकरण गाइड {1} की समीक्षा करें।", + "Blacklist": "काला सूची में डालना", + "BirthPlaceValue": "जन्म स्थान: {0}", + "BirthLocation": "जन्म स्थान", + "BirthDateValue": "जन्म: {0}", + "Banner": "झंडा", + "Backdrops": "पृष्ठभूमि", + "Backdrop": "पृष्ठभूमि", + "AutoBasedOnLanguageSetting": "ऑटो (भाषा सेटिंग के आधार पर)", + "Auto": "ऑटो", + "AuthProviderHelp": "इस उपयोगकर्ता के पासवर्ड को प्रमाणित करने के लिए एक प्रमाणीकरण प्रदाता का उपयोग करें।", + "Audio": "नया", + "AttributeNew": "नया", + "AspectRatio": "आस्पेक्ट अनुपात", + "AskAdminToCreateLibrary": "लाइब्रेरी बनाने के लिए किसी व्यवस्थापक से पूछें।", + "Ascending": "आरोही", + "AsManyAsPossible": "जितने अधिक संभव हों", + "Artists": "कलाकार की", + "Artist": "कलाकार", + "Art": "कला", + "AroundTime": "लगभग", + "Anytime": "किसी भी समय", + "AnyLanguage": "कोई भी भाषा", + "AlwaysPlaySubtitlesHelp": "भाषा की वरीयता से मेल खाने वाले उपशीर्षक ऑडियो भाषा की परवाह किए बिना लोड किए जाएंगे।", + "AlwaysPlaySubtitles": "हमेशा खेलो", + "AllowedRemoteAddressesHelp": "कोमा ने नेटवर्क के लिए आईपी पते या आईपी / नेटमास्क प्रविष्टियों की सूची को अलग कर दिया है जिन्हें दूरस्थ रूप से कनेक्ट करने की अनुमति दी जाएगी। यदि खाली छोड़ दिया जाता है, तो सभी दूरस्थ पते की अनुमति दी जाएगी।", + "AllowRemoteAccessHelp": "अनियंत्रित होने पर, सभी दूरस्थ कनेक्शन अवरुद्ध हो जाएंगे।", + "AllowRemoteAccess": "इस जेलिफ़िन सर्वर को दूरस्थ कनेक्शन की अनुमति दें।", + "AllowFfmpegThrottlingHelp": "जब एक ट्रांसकोड या रीमूक्स वर्तमान प्लेबैक स्थिति से काफी आगे हो जाता है, तो प्रक्रिया को रोकें ताकि यह कम संसाधनों का उपभोग करेगा। अक्सर मांग किए बिना देखने पर यह सबसे उपयोगी है। यदि आप प्लेबैक समस्याओं का अनुभव करते हैं तो इसे बंद कर दें।", + "AllowFfmpegThrottling": "थ्रोटल ट्रांसकोड", + "AllowOnTheFlySubtitleExtractionHelp": "वीडियो ट्रांसकोडिंग को रोकने में मदद करने के लिए एंबेडेड सबटाइटल वीडियो से निकाले जा सकते हैं और सादे पाठ में ग्राहकों तक पहुंचाए जाते हैं। कुछ प्रणालियों पर यह एक लंबा समय ले सकता है और निष्कर्षण प्रक्रिया के दौरान वीडियो प्लेबैक को स्टाल करने का कारण बन सकता है। जब वे क्लाइंट डिवाइस द्वारा मूल रूप से समर्थित नहीं होते हैं, तो वीडियो ट्रांसकोडिंग के साथ जले हुए एम्बेडेड उपशीर्षक को अक्षम करें।", + "AlbumArtist": "चित्राधार कलाकार", + "AllowOnTheFlySubtitleExtraction": "मक्खी पर उपशीर्षक निष्कर्षण की अनुमति दें", + "Album": "एल्बम", + "AddItemToCollectionHelp": "उनके लिए खोज करके संग्रह में आइटम जोड़ें और उन्हें संग्रह में जोड़ने के लिए उनके राइट-क्लिक या टैप मेनू का उपयोग करें।" } diff --git a/src/strings/hr.json b/src/strings/hr.json index 9710c13454..107014626e 100644 --- a/src/strings/hr.json +++ b/src/strings/hr.json @@ -1009,5 +1009,6 @@ "AllComplexFormats": "Svi kompleksni formati (ASS, SSA, VOBSUB, PGS, SUB/IDX, itd.)", "Books": "Knjige", "Channels": "Kanali", - "Collections": "Kolekcije" + "Collections": "Kolekcije", + "Artists": "Izvođači" } diff --git a/src/strings/hu.json b/src/strings/hu.json index b704e03848..9382927acd 100644 --- a/src/strings/hu.json +++ b/src/strings/hu.json @@ -101,8 +101,8 @@ "EnableBackdrops": "Háttérképek", "EnableBackdropsHelp": "A háttérképek a könyvtár böngészése közben néhány oldal hátterében jelennek meg.", "EnableHardwareEncoding": "Hardveres kódolás engedélyezése", - "EnableThemeSongs": "Főcím dalok", - "EnableThemeSongsHelp": "Főcím dalok lejátszása háttérben a könyvtár böngészése közben.", + "EnableThemeSongs": "Főcímdalok", + "EnableThemeSongsHelp": "Főcímdalok lejátszása háttérben a könyvtár böngészése közben.", "EndsAtValue": "Várható befejezés {0}", "Episodes": "Epizódok", "ExitFullscreen": "Kilépés a teljes képernyőből", @@ -167,7 +167,7 @@ "HeaderLibrarySettings": "Médiatár beállítások", "HeaderLiveTvTunerSetup": "Élő TV tuner beállítása", "HeaderMedia": "Média", - "HeaderMediaFolders": "Média Könyvtárak", + "HeaderMediaFolders": "Médiakönyvtárak", "HeaderMediaInfo": "Média Infó", "HeaderMetadataSettings": "Metaadat Beállítások", "HeaderMoreLikeThis": "Több ehhez hasonló", @@ -244,7 +244,7 @@ "LabelCustomDeviceDisplayName": "Megjelenítendő név:", "LabelCustomDeviceDisplayNameHelp": "Adj meg egy egyedi nevet, vagy hagyd üresen a készülék által elküldött név használatához.", "LabelCustomRating": "Egyéni értékelés:", - "LabelDashboardTheme": "Szerver vezérlőpult kinézet:", + "LabelDashboardTheme": "Szerver vezérlőpult kinézete:", "LabelDateAdded": "Hozzáadva:", "LabelDateTimeLocale": "Dátum és idő formátum:", "LabelDay": "Nap:", @@ -424,7 +424,7 @@ "OptionMissingEpisode": "Hiányzó Epizódok", "OptionMonday": "Hétfő", "OptionNameSort": "Név", - "OptionNew": "Új...", + "OptionNew": "Új…", "OptionParentalRating": "Korhatár besorolás", "OptionPlayCount": "Lejátszások száma", "OptionPlayed": "Megnézett", @@ -584,7 +584,7 @@ "Aired": "Adásban", "Albums": "Albumok", "AllChannels": "Minden csatorna", - "AllComplexFormats": "Minden összetett formátum (ASS, SSA, VOBSUB, PGS, SUB, IDX)", + "AllComplexFormats": "Minden összetett formátum (ASS, SSA, VOBSUB, PGS, SUB, IDX, ...)", "AllowMediaConversion": "Média konvertálás engedélyezése", "AllowMediaConversionHelp": "Add meg vagy tiltsd le a média konvertálás funkcióhoz való hozzáférést.", "AllowRemoteAccess": "Engedélyezze a távoli kapcsolatokat a Jellyfin szerverhez.", @@ -592,7 +592,7 @@ "AlwaysPlaySubtitles": "Mindig jelenjen meg", "AnyLanguage": "Bármelyik nyelv", "Anytime": "Bármikor", - "AroundTime": "{0} körül", + "AroundTime": "kb.", "AsManyAsPossible": "Amennyi lehetséges", "AspectRatio": "Képarány", "Auto": "Auto", @@ -604,7 +604,7 @@ "AlwaysPlaySubtitlesHelp": "A nyelvi beállításoknak megfelelő feliratok az audió nyelvétől függetlenül kerülnek betöltésre.", "Artists": "Előadók", "Blacklist": "Feketelista", - "BookLibraryHelp": "Az audió- és szövegkönyvek támogatottak. Nézd meg a {0} könyvelnevezési útmutatót {1}.", + "BookLibraryHelp": "Lehetőség van audió és hangoskönyvek visszajátszására. Nézd meg a {0} könyvelnevezési útmutatót {1}.", "BrowsePluginCatalogMessage": "Böngéssz a Bővítmény katalógusunkban a rendelkezésre álló bővítmények megtekintéséhez.", "AddItemToCollectionHelp": "Adj elemeket a gyűjteményekhez, ehhez keresed meg őket, majd kattints jobb egérgombbal, vagy kattints a menüre és add hozzá a gyűjteményhez.", "AllowedRemoteAddressesHelp": "Vesszővel válaszd el az IP-címek vagy IP / netmask címek listáját annak a hálózatnak amelyből távolról csatlakozhatnak. Ha üresen marad, az összes távoli cím megengedett.", @@ -630,7 +630,7 @@ "ChannelAccessHelp": "Válaszd ki a megosztani kívánt csatornákat a felhasználóval. A rendszergazdák a metaadatkezelő segítségével szerkeszthetik az összes csatornát.", "ChannelNameOnly": "Csak {0} csatorna", "ChannelNumber": "Csatorna száma", - "CinemaModeConfigurationHelp": "A Cinema Mode igazi mozi élményt nyújt előzetessel és egyedi intróval a film vetítése előtt.", + "CinemaModeConfigurationHelp": "A Mozi mód igazi mozi élményt nyújt előzetessel és egyedi intróval a film vetítése előtt.", "ColorSpace": "Színtér", "ColorTransfer": "Színátvitel", "Composer": "Zeneszerző", @@ -669,7 +669,7 @@ "DrmChannelsNotImported": "A csatornák DRM-el nem kerülnek importálásra.", "DropShadow": "Árnyék", "EasyPasswordHelp": "Az egyszerű PIN kódot az offline hozzáféréshez használják a támogatott kliens alkalmazásokban, valamint hálózaton belüli bejelentkezéshez is használható.", - "EnableCinemaMode": "Cinema Mode", + "EnableCinemaMode": "Mozi mód", "EnableColorCodedBackgrounds": "Színkódolt háttérképek", "EnableDisplayMirroring": "Kijelző tükrözés", "EnableExternalVideoPlayers": "Külső videolejátszók", @@ -758,7 +758,7 @@ "HeaderMusicQuality": "Zene minősége", "HeaderNewApiKey": "Új API kulcs", "HeaderNewDevices": "Új eszközök", - "HeaderNextEpisodePlayingInValue": "Következő epizód lejátszása {0} múlva", + "HeaderNextEpisodePlayingInValue": "A következő epizód {0} múlva automatikusan elindul", "HeaderNextVideoPlayingInValue": "Következő videó lejátszása {0}", "HeaderOtherItems": "Egyéb elemek", "HeaderPasswordReset": "Jelszó visszaállítása", @@ -786,10 +786,10 @@ "MediaInfoCodecTag": "Kódek címke", "Photos": "Fényképek", "Playlists": "Lejátszási listák", - "Shows": "Műsorok", + "Shows": "Sorozatok", "Songs": "Dalok", "ValueSpecialEpisodeName": "Special - {0}", - "EnableThemeVideos": "Videók témák", + "EnableThemeVideos": "Videótémák", "EnableThemeVideosHelp": "Videó témájának lejátszása háttérben a könyvtár böngészése közben.", "HeaderBlockItemsWithNoRating": "Blokkolja azokat az elemeket amelyek tiltott, vagy nem felismerhető minősítésűek:", "HeaderSchedule": "Ütemezés", @@ -853,7 +853,6 @@ "LabelCustomCertificatePath": "Egyéni SSL tanúsítvány elérési útvonala:", "LabelCustomCss": "Egyedi CSS:", "LabelCustomCssHelp": "Egyedi CSS stílusok alkalmazása a webes felülethez.", - "LabelCustomizeOptionsPerMediaType": "A média típusának testreszabása:", "LabelDeathDate": "Halálának dátuma:", "LabelDefaultScreen": "Alapértelmezett képernyő:", "LabelDefaultUser": "Alapértelmezett felhasználó:", @@ -869,7 +868,7 @@ "LabelArtistsHelp": "Ha több van használd a következő elválasztót ;", "LabelDisplayCollectionsView": "Jelenítse meg a Gyűjtemények menüpontot a filmgyűjtemények megjelenítéséhez", "LabelDisplayCollectionsViewHelp": "Ez külön menüpontot hoz létre a filmgyűjtemények megjelenítéséhez. Gyűjtemény létrehozásához kattints jobb gombbal vagy kattints a három pontra bármelyik filmen, és válaszd a 'Hozzáadás gyűjteményhez' lehetőséget. ", - "LabelEnableAutomaticPortMapHelp": "A szerver az UPnP segítségével a routeren megpróbálja automatikusan átirányítani a nyilvános portot a helyi portra. Előfordulhat, hogy egyes router modelleken ez nem működik. A módosítások újraindítás után lépnek életbe.", + "LabelEnableAutomaticPortMapHelp": "A szerver az UPnP segítségével a routeren megpróbálja automatikusan átirányítani a nyilvános portot a helyi portra. Előfordulhat, hogy egyes router modellek, vagy hálózati konfigurációk esetén ez nem működik. A módosítások újraindítás után lépnek életbe.", "LabelEnableBlastAliveMessagesHelp": "Engedélyezd ezt ha a szerver nem észleli megbízhatóan a hálózat más UPnP-eszközeit.", "LabelEnableDlnaClientDiscoveryInterval": "Kliens felderítési intervallum (másodperc)", "LabelEnableDlnaClientDiscoveryIntervalHelp": "A Jellyfin által végrehajtott SSDP keresések időtartamát határozza meg másodpercben.", @@ -934,7 +933,7 @@ "LabelNewName": "Új név:", "LabelNewsCategories": "Hírek kategóriái:", "LabelNumber": "Szám:", - "LabelOptionalNetworkPathHelp": "Ha ez a mappa meg van osztva a hálózaton, a hálózati megosztási útvonal megadása lehetővé teszi, hogy a Jellyfin alkalmazások más eszközökön közvetlenül hozzáférjenek a médiafájlokhoz.", + "LabelOptionalNetworkPathHelp": "Ha ez a mappa meg van osztva a hálózaton, a hálózati megosztási útvonal megadása lehetővé teszi, hogy a Jellyfin alkalmazások más eszközökön közvetlenül hozzáférjenek a médiafájlokhoz. Például: {0{ vagy {1}.", "LabelPasswordConfirm": "Jelszó (megerősítés):", "LabelPlaceOfBirth": "Születési hely:", "LabelPostProcessor": "A feldolgozás utáni alkalmazás:", @@ -1302,7 +1301,7 @@ "AllowOnTheFlySubtitleExtractionHelp": "A beágyazott feliratokat ki lehet nyerni a videókból és elküldeni az alkalmazásoknak sima szöveg formátumba, hogy ne legyen átkódolás. Néhány eszközön ez hosszú ideig is eltarthat, valamint a videó lejátszás megakadhat az eltávolítási folyamat futása közben. Ezt kikapcsolva a beágyazott feliratok videó átkódolással beégetésre kerülnek azon kliens eszközökre melyek nem támogatják a külső feliratokat.", "Art": "ClearArt", "AuthProviderHelp": "Válaszd ki az azonosítási szolgáltatást amely ezen felhasználó jelszavának ellenőrzését valósítja meg.", - "BurnSubtitlesHelp": "Meghatározza, hogy a szervernek be kell-e égetnie a feliratot videó átkódolás esetén a felirat típusának függvényében. Ennek elkerülésével a szerver teljesítménye javul. Válaszd az Auto lehetőséget a kép alapú feliratok (pl. VOBSUB, PGS, SUB/IDX, stb) és bizonyos ASS/SSA feliratok beégetéséhez.", + "BurnSubtitlesHelp": "Meghatározza, hogy a szervernek be kell-e égetnie a feliratot videó átkódolás esetén a felirat típusának függvényében. Ennek elkerülésével a szerver teljesítménye javul. Válaszd az Auto lehetőséget a kép alapú feliratok (pl. VOBSUB, PGS, SUB/IDX, stb.) és bizonyos ASS/SSA feliratok beégetéséhez.", "ButtonAddScheduledTaskTrigger": "Vezérlő Hozzáadása", "ButtonGuide": "Műsorújság", "ButtonRefreshGuideData": "Műsorújság Frissítése", @@ -1394,8 +1393,6 @@ "ButtonSplit": "Szétvág", "Absolute": "Abszolút", "LabelSkipIfAudioTrackPresentHelp": "Vedd ki a pipát, ha minden videóhoz szeretnél feliratot az audio nyelvétől függetlenül.", - "EnableFastImageFadeInHelp": "Gyorsabb előtűnés animáció a betöltött képekhez", - "EnableFastImageFadeIn": "Gyors kép-előtűnés", "SubtitleOffset": "Felirat eltolása", "SeriesDisplayOrderHelp": "Rakd sorba az epizódokat az adásba kerülésük dátuma, a DVD sorszám, vagy az abszolút számozás szerint.", "SelectAdminUsername": "Kérjük válassz felhasználónevet az adminisztrátor fiók számára.", @@ -1466,15 +1463,15 @@ "LabelParentNumber": "Szülő száma:", "LabelMetadataReadersHelp": "Rangsorold az előnyben részesített metaadat forrásokat. Az a forrás kerül sorsolásra, amelyben először találunk információt.", "LabelLineup": "Felhozatal:", - "LabelBaseUrlHelp": "Ide hozzáadhatsz egy egyéni alkönyvtárat, hogy a szerverhez egyedibb URL-címről férj hozzá.", + "LabelBaseUrlHelp": "Egyedi alkönyvtárat ad hozzá a szervered URL címéhez. Például: http://pelda.com/<alapurl>", "ErrorPleaseSelectLineup": "Kérjük, válassz ki egy felhozatalt, és próbáld újra. Ha nem állnak rendelkezésre felsorolások, akkor ellenőrizd, hogy helyes-e felhasználóneved, jelszavad és irányítószámod.", "ErrorAddingListingsToSchedulesDirect": "Hiba történt a felhozatal hozzáadása közben a Schedules Direct fiókhoz. A Schedules Direct csak korlátozott számú fiók hozzáadását támogatja. Lehetséges, hogy be kell jelentkezned a Schedules Direct weboldalán és eltávolítani néhány más listát a fiókodról mielőtt továbblépsz.", "DeviceAccessHelp": "Ez csak azokra az eszközökre alkalmazható, amelyek egyedileg vannak azonosítva és nem gátolják meg a böngészőből való elérést. A felhasználói eszközök kiszűrése meg fogja akadályozni az új eszközök használatát addig, amíg itt nem engedélyezed őket.", "PlaybackErrorNoCompatibleStream": "Ez a kliens nem kompatibilis ezzel a médiával és a szerver nem tud kompatibilis streamet küldeni az eszközre.", "AllowFfmpegThrottlingHelp": "Ha az átkódolás vagy remux eléggé előtöltődött a jelenlegi lejátszási pozícióhoz képest, ez megállítja a folyamatot, hogy kevesebb erőforrást vegyen igénybe. Ez akkor hasznos, ha ritkán ugrálsz előre a lejátszott videókban. Kapcsold ki, ha lejátszási problémákba ütközöl.", "AllowFfmpegThrottling": "Átkódolás visszafogása", - "PreferEmbeddedEpisodeInfosOverFileNames": "Inkább a beágyazott epizódokra vonatkozó információkat részesítse előnyben a fájlnevekkel szemben", - "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Ez a beágyazott metaadatok epizódinformációit használja, ha rendelkezésre állnak.", + "PreferEmbeddedEpisodeInfosOverFileNames": "Az epizódból elérhető beágyazott információkat használja inkább, fájlnevek helyett", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Ez az epizóddal kapcsolatos beágyazott metaadatokat használja, ha azok elérhetőek.", "OnApplicationStartup": "Az alkalmazás indításakor", "EveryXHours": "{0} óránként", "EveryHour": "Óránként", @@ -1484,7 +1481,7 @@ "DailyAt": "Naponta ekkor: {0}", "LastSeen": "Utoljára elérhető {0}", "PersonRole": "mint {0}", - "ListPaging": "{0}-{1} ennyiből: {2}", + "ListPaging": "{0}-{1} / {2}", "WriteAccessRequired": "A Jellyfin Szerver írási jogosultságot igényel ehhez a könyvtárhoz. Kérjük, ellenőrizd, hogy van-e jogod írni ide, majd próbáld újra.", "PathNotFound": "Az elérési út nem található. Kérjük, ellenőrizd, hogy az elérési út megfelelő-e, majd próbáld újra.", "Track": "Szám", @@ -1494,8 +1491,68 @@ "Movie": "Film", "Episode": "Epizód", "ClientSettings": "Kliens beállítások", - "BoxSet": "Dobozos készlet", + "BoxSet": "Dobozos kiadások", "Artist": "Előadó", "AlbumArtist": "Album előadó", - "Album": "Album" + "Album": "Album", + "LabelLibraryPageSizeHelp": "Az oldalnként megmutatott elemek száma. Nullára állítva a lapozási funkció ki lesz kapcsolva.", + "LabelLibraryPageSize": "Könyvtár oldalméret:", + "LabelDeinterlaceMethod": "Deinterlacing mód:", + "DeinterlaceMethodHelp": "Válassza ki a váltottsorosság megszűntetéséhez használandó módszert a váltottsoros tartalmak transzkódolásakor.", + "UnsupportedPlayback": "Jellyfin nem tud DRM-titkosított tartalmak dekriptálására, ettől függetlenül a lejátszással mindig megpróbálkozik. Néhány fájl emiatt teljesen fekete képernyőt ad, amely vagy a titkosítás miatt van, vagy nem olyan nem támogatott tartalmak miatt, mint az interaktív címek.", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "ReleaseGroup": "Kiadócsoport", + "MessageUnauthorizedUser": "Jelenleg nincs jogosultságod a szerverhez való hozzáféréshez. Kérjük, lépj kapcsolatba az adminisztrátorral további információkért!", + "ButtonTogglePlaylist": "Lejátszási listák", + "ButtonToggleContextMenu": "Továbbiak", + "Filter": "Szűrés", + "New": "Új", + "HeaderFavoritePlaylists": "Kedvenc lejátszási listák", + "ApiKeysCaption": "A jelenleg engedélyezett API kulcsok listája", + "LabelNightly": "Éjszakai", + "LabelStable": "Stabil", + "LabelChromecastVersion": "Chromecast verzió", + "LabelEnableHttpsHelp": "Engedélyezi a kiszolgálónak a kommunikációt HTTPS protokollon keresztül. Érvényes tanúsítványt is be kell állítani az érvénybe léptetéshez.", + "LabelRequireHttpsHelp": "Bekapcsolást követően minden egyes HTTP-kérést átirányít HTTPS protokollra. A már meglévő HTTPS kéréseket nem módosítja.", + "LabelRequireHttps": "HTTPS megkövetelése", + "LabelEnableHttps": "HTTPS engedélyezése", + "HeaderServerAddressSettings": "Kiszolgáló Címének Beállításai", + "HeaderRemoteAccessSettings": "Távoli Hozzáférés Beállításai", + "HeaderHttpsSettings": "HTTPS Beállítások", + "TabDVR": "DVR", + "HeaderDVR": "DVR", + "SaveChanges": "Változtatások mentése", + "MessageSyncPlayGroupWait": "{0} bufferel...", + "MessageSyncPlayUserLeft": "{0} elhagyta a csoportot.", + "MessageSyncPlayUserJoined": "{0} csatlakozott a csoporthoz.", + "MessageSyncPlayDisabled": "SyncPlay letiltva.", + "MessageSyncPlayEnabled": "SyncPlay engedélyezve.", + "LabelSyncPlayAccess": "SyncPlay hozzáférés", + "LabelSyncPlayAccessCreateAndJoinGroups": "A felhasználó létrehozhat csoportokat és csatlakozhat hozzájuk", + "LabelSyncPlayLeaveGroupDescription": "SyncPlay letiltása", + "LabelSyncPlayLeaveGroup": "Csoport elhagyása", + "LabelSyncPlayNewGroupDescription": "Új csoport létrehozása", + "LabelSyncPlayNewGroup": "Új csoport", + "LabelSyncPlaySyncMethod": "Szinkronizálási mód:", + "MillisecondsUnit": "ms", + "HeaderSyncPlayEnabled": "SyncPlay engedélyezve", + "HeaderSyncPlaySelectGroup": "Csatlakozás csoporthoz", + "SyncPlayAccessHelp": "Válaszd ki, hogy ez a felhasználó milyen szinten férhet hozzá a SyncPlay funkcióhoz. A SyncPlay lehetőséget biztosít a lejátszások közötti szinkronizációra.", + "MessageSyncPlayErrorMedia": "Nem sikerült a SyncPlay engedélyezése! Média hiba.", + "MessageSyncPlayErrorMissingSession": "A SyncPlay lejátszása sikertelen! Hiányzó munkamenet.", + "MessageSyncPlayErrorNoActivePlayer": "Nem található aktív lejátszó. A SyncPlay letiltásra került.", + "MessageSyncPlayErrorAccessingGroups": "Hiba történt a csoportok listájának betöltésekor.", + "MessageSyncPlayLibraryAccessDenied": "A tartalomhoz való hozzáférés korlátozva van.", + "MessageSyncPlayJoinGroupDenied": "A SyncPlay használatához jogosultság szükséges.", + "MessageSyncPlayCreateGroupDenied": "Jogosultság szükséges a csoportok létrehozásához.", + "MessageSyncPlayGroupDoesNotExist": "Nem sikerült csatlakozni a csoporthoz, mivel az nem létezik.", + "MessageSyncPlayPlaybackPermissionRequired": "Lejátszási jogosultság szükséges.", + "MessageSyncPlayNoGroupsAvailable": "Nincsenek elérhető csoportok. Először kezdj el lejátszani valamit.", + "LabelSyncPlayAccessNone": "Letiltva ennél a felhasználónál", + "LabelSyncPlayAccessJoinGroups": "A felhasználó csoportokhoz való csatlakozásának engedélyezése", + "LabelSyncPlayPlaybackDiff": "Lejátszási időkülönbség:", + "LabelSyncPlayTimeOffset": "Időeltolás a szerverhez képest:", + "EnableDetailsBannerHelp": "Megjelenít egy banner képet a részletes információoldal tetején.", + "EnableDetailsBanner": "Banner a részletes oldalon" } diff --git a/src/strings/is-is.json b/src/strings/is-is.json index 70fc891193..1042737ae2 100644 --- a/src/strings/is-is.json +++ b/src/strings/is-is.json @@ -46,7 +46,7 @@ "OptionContinuing": "Heldur áfram", "OptionBlockTvShows": "Sjónvarpsþættir", "OptionBlockMusic": "Tónlist", - "OptionBlockTrailers": "Stiklur", + "OptionBlockTrailers": "Sýnishorn", "AllowOnTheFlySubtitleExtractionHelp": "Hægt er að sækja texta sem eru innbyggðir í myndaskrá og senda þá beint til notanda á textaformi til þess að sleppa við að umbreyta (transcode) myndaskránni. Í sumum tölvum getur þetta tekið langan tíma og valdið hikstum á meðan verið er að sækja textan. Afvirkjaðu þetta til þess að láta alla texta vera brennda inn í myndaskránna ef tæki notenda styður ekki að spila skránna beint.", "AccessRestrictedTryAgainLater": "Aðgangur bannaður í augnablikinu. Vinsamlegast reynið síðar.", "Actor": "Leikari", @@ -93,7 +93,7 @@ "AllowOnTheFlySubtitleExtraction": "Leyfa að taka út texta á meðan það er í keyrslu", "AllowRemoteAccess": "Leyfa fjartengingar í þennan Jellyfin þjón.", "AllowRemoteAccessHelp": "Ef þetta er afhakað, allar fjartengingar, þ.e. í gegnum internetið, verða bannaðar.", - "AlwaysPlaySubtitles": "Alltaf spila texta", + "AlwaysPlaySubtitles": "Spila alltaf", "AnyLanguage": "Öll tungumál", "AroundTime": "Um {0}", "Art": "List", @@ -165,8 +165,8 @@ "HeaderProfile": "Prófíll", "HeaderPeople": "Fólk", "HeaderPassword": "Lykilorð", - "HeaderLatestMovies": "Nýjustu Kvikmyndir", - "HeaderLatestEpisodes": "Nýjustu Þættirnir", + "HeaderLatestMovies": "Kvikmyndir, nýlega bætt við", + "HeaderLatestEpisodes": "Þættir, nýlega bætt við", "HeaderHome": "Heim", "HeaderFavoriteVideos": "Uppáhalds Myndbönd", "HeaderFavoriteMovies": "Uppáhalds Kvikmyndir", @@ -217,7 +217,7 @@ "ButtonViewWebsite": "Skoða vefsíðu", "ButtonUp": "Upp", "ButtonUninstall": "Fjarlægja", - "ButtonTrailer": "Stikla", + "ButtonTrailer": "Sýnishorn", "ButtonSubtitles": "Texti", "ButtonSort": "Flokka", "ButtonSignIn": "Innskráning", @@ -241,17 +241,9 @@ "ConfirmDeleteImage": "Eyða mynd?", "ButtonRename": "Endurnefna", "Sync": "Samstilla", - "Never": "", - "News": "", "ButtonRevoke": "Afturkalla", "ButtonRepeat": "Endurtaka", - "MusicArtist": "", - "MusicAlbum": "", - "No": "", "Monday": "Mánudagur", - "Name": "", - "Mute": "", - "MusicVideo": "", "ButtonRefresh": "Endurhlaða", "ButtonParentalControl": "Foreldraeftirlit", "ButtonOff": "Af", @@ -290,8 +282,266 @@ "AllowedRemoteAddressesHelp": "Kommu aðskilinn listi yfir ip tölur eða ip-númeramát fyrir net sem mega fjartengjas. Ef þetta er autt eru allar fjartengingar leyfðar.", "AllowHWTranscodingHelp": "Leyfa viðtæki að umbreyta straumi í rauntíma.Þetta getur minnkað álag á þjón.", "ValueSpecialEpisodeName": "Sérstakt - {0}", - "Shows": "Þættir", + "Shows": "Sýningar", "Playlists": "Spilunarlisti", "ButtonScanAllLibraries": "Skanna Öll Gagnasöfn", - "AllLibraries": "Öll gagnasöfn" + "AllLibraries": "Öll gagnasöfn", + "RefreshMetadata": "Endurhlaða lýsigögn", + "Refresh": "Endurhlaða", + "ReleaseDate": "Útgáfudagur", + "RememberMe": "Muna eftir mér", + "RepeatAll": "Endurtaka allt", + "Repeat": "Endurtaka", + "RemoveFromPlaylist": "Fjarlægja úr spilunarlista", + "RemoveFromCollection": "Fjarlægja úr safni", + "HeaderHttpsSettings": "HTTPS Stillingar", + "HeaderFavoriteBooks": "Uppáhalds Bækur", + "HeaderEditImages": "Breyta ljósmyndum", + "HeaderContinueListening": "Halda áfram að hlusta", + "ChannelNumber": "Númer rásar", + "ChannelNameOnly": "Aðeins rás {0}", + "ButtonSubmit": "Senda", + "ButtonShutdown": "Slökkva á netþjón", + "EnableThemeVideosHelp": "Spila þema myndbönd í bakgrunni þegar gagnasafn er skoðað.", + "EnableThemeSongsHelp": "Spila þema lög í bakgrunni þegar gagnasafn er skoðað.", + "FormatValue": "Snið: {0}", + "Genre": "Tegund", + "HeaderActiveDevices": "Virk Tæki", + "HeaderAddToCollection": "Bæta í Safn", + "HeaderAddUpdateImage": "Bæta við/uppfæra ljósmynd", + "HeaderAddToPlaylist": "Bæta við á Spilunarlista", + "HeaderAlert": "Viðvörun", + "HeaderAppearsOn": "Birtist á", + "HeaderChannels": "Rásir", + "HeaderDetectMyDevices": "Finna tækin mín", + "HeaderFavoritePeople": "Uppáhalds Fólk", + "HeaderFavoritePlaylists": "Uppáhalds spilunarlistar", + "HeaderFilters": "Síur", + "HeaderForgotPassword": "Gleymt lykilorð", + "HeaderForKids": "Fyrir Krakka", + "HeaderFrequentlyPlayed": "Oft Spilað", + "HeaderGenres": "Flokkar", + "HeaderLatestMusic": "Tónlist, nýlega bætt við", + "HeaderMetadataSettings": "Stillingar lýsigagna", + "HeaderMedia": "Margmiðlunarsafn", + "HeaderLiveTv": "Sjónvarp í beinni útsendingu", + "HeaderLoginFailure": "Innskráning Mistókst", + "HeaderMyDevice": "Tækið mitt", + "HeaderMusicVideos": "Tónlistarmyndbönd", + "HeaderMusicQuality": "Tónlistargæði", + "HeaderMovies": "Kvikmyndir", + "HeaderNewDevices": "Ný tæki", + "HeaderPasswordReset": "Endurstilla Lykilorð", + "HeaderPhotoAlbums": "Myndaalbúm", + "OnApplicationStartup": "Við ræsingu forrits", + "EveryXHours": "Hverjum {0} klukkustundum", + "EveryHour": "Hverja klukkustund", + "Episode": "Þáttur", + "EnableThemeVideos": "Þema myndbönd", + "EnableThemeSongs": "Þema lög", + "EnablePhotos": "Birta myndir", + "EnableHardwareEncoding": "Virkja vélbúnaðarkóðun", + "LabelScreensaver": "Skjáhvíla:", + "LabelRequireHttps": "Krefjast HTTPS", + "LabelReleaseDate": "Útgáfudagur:", + "LabelReasonForTranscoding": "Ástæða fyrir umkóðun:", + "LabelPlayMethod": "Spilunaraðferð:", + "LabelPlaylist": "Spilunarlisti:", + "LabelPlayer": "Spilari:", + "LabelPlayDefaultAudioTrack": "Spila sjálfgefna hljóðrás óháð tungumáli", + "LabelPlaceOfBirth": "Fæðingarstaður:", + "LabelPersonRoleHelp": "Dæmi: Ísbílstjóri", + "LabelPersonRole": "Hlutverk:", + "LabelPath": "Slóð:", + "LabelPasswordRecoveryPinCode": "PIN númer:", + "LabelPasswordConfirm": "Lykilorð (staðfesta):", + "LabelPassword": "Lykilorð:", + "LabelOverview": "Yfirlit:", + "LabelOriginalTitle": "Upphaflegur Titill:", + "LabelOriginalAspectRatio": "Upprunalegt skjáhlutfall:", + "LabelNumber": "Númer:", + "LabelNotificationEnabled": "Virkja þessa tilkynningu", + "LabelNewsCategories": "Frétta flokkar:", + "LabelNewPasswordConfirm": "Staðfesta nýtt lykilorð:", + "LabelNewPassword": "Nýtt lykilorð:", + "LabelNewName": "Nýtt nafn:", + "LabelName": "Nafn:", + "LabelLanNetworks": "LAN net:", + "MessageImageFileTypeAllowed": "Aðeins JPEG og PNG skrár eru studdar.", + "DrmChannelsNotImported": "Rásir með DRM verða ekki fluttar inn.", + "DoNotRecord": "Ekki taka upp", + "DisplayModeHelp": "Veldu útlit fyrir viðmótið.", + "DisplayMissingEpisodesWithinSeasons": "Birta þætti sem vantar inn í þáttaraðir", + "DisplayInMyMedia": "Birta á heimaskjá", + "Display": "Birta", + "Dislike": "Mislíka", + "Disabled": "Óvirkt", + "Directors": "Leikstjórar", + "DirectStreamHelp2": "Beint streymi á skrá notar mjög litið vinnsluafl án þess að tapa myndgæðum.", + "Descending": "Niður", + "DeleteImageConfirmation": "Ertu viss um að þú viljir eyða þessari mynd?", + "DefaultErrorMessage": "Villa varð við vinnslu beiðninnar. Reyndu aftur síðar.", + "DeathDateValue": "Dó: {0}", + "DatePlayed": "Dagsetning spilað", + "DateAdded": "Dagsetning bætt við", + "CriticRating": "Einkunn gagnrýnanda", + "CopyStreamURLError": "Villa varð við afritun vefslóðar.", + "CopyStreamURLSuccess": "Afrit af vefslóð tókst.", + "CopyStreamURL": "Afrita vefslóð streymis", + "Continuing": "Áframhaldandi", + "ConfirmEndPlayerSession": "Langar þig að slökkva á Jellyfin á {0}?", + "ConfirmDeletion": "Staðfesta eyðingu", + "ConfirmDeleteItem": "Ef þessari skrá er eytt verður hún fjarlægð úr bæði skráarkerfinu og miðlasafninu. Ertu viss um að þú viljir halda áfram?", + "Composer": "Tónskáld", + "ClientSettings": "Stillingar biðlara", + "ButtonTogglePlaylist": "Spilunarlisti", + "ButtonToggleContextMenu": "Meira", + "ButtonSplit": "Skipta", + "ButtonStop": "Stöðva", + "ButtonResetEasyPassword": "Endurstilla Easy PIN númer", + "ButtonRefreshGuideData": "Uppfæra sjónvarpsþáttagögn", + "Artist": "Listamaður", + "AllowFfmpegThrottling": "Takmarka Umkóðun", + "Album": "Plata", + "SettingsSaved": "Stillingar vistaðar.", + "Settings": "Stillingar", + "Series": "Seríur", + "SendMessage": "Senda skilaboð", + "SelectAdminUsername": "Veldu notandanafn fyrir stjórnanda aðganginn þinn.", + "Season": "Sería", + "SearchResults": "Leitarniðurstöður", + "SearchForSubtitles": "Leita að skjátexta", + "Search": "Leita", + "Screenshots": "Skjámyndir", + "Screenshot": "Skjámynd", + "ScanLibrary": "Skanna gagnasafn", + "SaveSubtitlesIntoMediaFolders": "Vista skjátexta í miðlamöppur", + "SaveChanges": "Vista breytingar", + "Save": "Vista", + "Saturday": "Laugardagur", + "RunAtStartup": "Keyra við ræsingu", + "Rewind": "Spóla til baka", + "AlbumArtist": "Höfundur plötu", + "OptionHasTrailer": "Sýnishorn", + "ViewArtist": "Skoða listamann", + "ValueSongCount": "{0} lög", + "ValueSeriesCount": "{0} Þáttaraðir", + "ValueSeconds": "{0} sekúndur", + "ValueOneSong": "1 lag", + "ValueOneSeries": "1 Þáttaröð", + "ValueOneMusicVideo": "1 tónlistarmyndband", + "ValueOneMovie": "1 kvikmynd", + "ValueOneEpisode": "1 þáttur", + "Up": "Upp", + "Unplayed": "Óspilað", + "UninstallPluginHeader": "Fjarlægja Viðbót", + "Tuesday": "Þriðjudagur", + "Transcoding": "Umkóðun", + "Trailers": "Sýnishorn", + "TitlePlayback": "Spilun", + "Thursday": "Fimmtudagur", + "ThemeVideos": "Þemu myndbönd", + "ThemeSongs": "Þemu lög", + "TellUsAboutYourself": "Segðu okkur frá sjálfum þér", + "TabUsers": "Notendur", + "TabUpcoming": "Væntanlegt", + "TabTranscoding": "Umkóðun", + "TabTrailers": "Sýnishorn", + "TabSuggestions": "Tillögur", + "TabSongs": "Lög", + "TabResumeSettings": "Halda áfram", + "TabProfile": "Prófíll", + "TabPlugins": "Viðbætur", + "TabOther": "Annað", + "TabNetworks": "Netkerfi", + "TabMyPlugins": "Mínar viðbætur", + "TabMusicVideos": "Tónlistarmyndbönd", + "TabMusic": "Tónlist", + "TabMovies": "Kvikmyndir", + "PleaseRestartServerName": "Vinsamlegast endurræstu Jellyfin netþjóninn - {0}.", + "Previous": "Fyrri", + "Premiere": "Frumsýning", + "Producer": "Framleiðandi", + "Quality": "Gæði", + "RecentlyWatched": "Nýlega horft á", + "RecommendationBecauseYouLike": "Af því að þér líkar {0}", + "RecommendationBecauseYouWatched": "Af því að þú horfðir á {0}", + "RecommendationDirectedBy": "Leikstýrt af {0}", + "SortChannelsBy": "Raða rásum eftir:", + "SortByValue": "Raða eftir {0}", + "Sort": "Raða", + "Filter": "Sía", + "New": "Nýtt", + "Shuffle": "Stokka", + "ShowYear": "Sýna ár", + "ShowTitle": "Sýna titil", + "Share": "Deila", + "LabelDefaultUser": "Sjálfgefinn notandi:", + "LabelDefaultScreen": "Sjálfgefinn skjár:", + "LabelDeathDate": "Dánardagur:", + "LabelDay": "Dagur:", + "LabelCurrentPassword": "Núverandi lykilorð:", + "LabelCollection": "Safn:", + "LabelChannels": "Rásir:", + "LabelCachePath": "Slóð skyndiminnis:", + "LabelCache": "Skyndiminni:", + "LabelBurnSubtitles": "Brenna skjátexta:", + "LabelBitrate": "Bitahraði:", + "LabelBirthYear": "Fæðingarár:", + "LabelBirthDate": "Fæðingardagur:", + "LabelAudio": "Hljóð", + "LabelArtists": "Listamenn:", + "LabelAppNameExample": "Dæmi: Sickbeard, Sonarr", + "LabelAll": "Allt", + "LabelAccessDay": "Vikudagur:", + "Kids": "Krakkar", + "Hide": "Fela", + "Help": "Hjálp", + "Home": "Heim", + "HeaderYears": "Ár", + "HeaderVideos": "Myndbönd", + "HeaderVideoQuality": "Myndgæði", + "HeaderUsers": "Notendur", + "HeaderUser": "Notandi", + "TabMetadata": "Lýsigögn", + "TabGenres": "Flokkar", + "TabFavorites": "Eftirlæti", + "TabEpisodes": "Þættir", + "TabDirectPlay": "Bein Spilun", + "TabAdvanced": "Ítarlegt", + "Sunday": "Sunnudagur", + "Suggestions": "Tillögur", + "Subtitles": "Skjátexti", + "LabelMetadataPath": "Slóð lýsigagna:", + "LabelMetadata": "Lýsigögn:", + "LabelMessageTitle": "Titill skilaboðs:", + "LabelMessageText": "Texti skilaboðs:", + "LabelMaxStreamingBitrate": "Hámarks gæði streymis:", + "LabelLineup": "Uppröðun:", + "LabelKodiMetadataDateFormat": "Snið útgáfudags:", + "LabelInternetQuality": "Gæði Internets:", + "LabelIconMaxWidth": "Hámarksbreidd tákns:", + "LabelHomeNetworkQuality": "Gæði heimanets:", + "LabelHardwareAccelerationType": "Hröðun vélbúnaðar:", + "LabelFriendlyName": "Vinalegt nafn:", + "LabelFormat": "Snið:", + "LabelForgotPasswordUsernameHelp": "Sláðu inn notandanafnið þitt, ef þú manst eftir því.", + "LabelFont": "Leturgerð:", + "LabelFolder": "Mappa:", + "LabelEpisodeNumber": "Þáttur númer:", + "LabelEnableRealtimeMonitor": "Virkja vöktun í rauntíma", + "LabelEnableHardwareDecodingFor": "Gera vélbúnaðarafkóðun virka fyrir:", + "LabelDroppedFrames": "Felldir rammar:", + "LabelDiscNumber": "Númer disks:", + "LabelDeviceDescription": "Lýsing tækis", + "LabelDashboardTheme": "Þema mælaborðs:", + "LabelCustomCss": "Sérsniðin CSS:", + "LabelCriticRating": "Einkunn gagnrýnanda:", + "LabelCorruptedFrames": "Skemmdir rammar:", + "LabelCancelled": "Hætt við", + "LabelAppName": "Heiti forrits", + "LabelAllowServerAutoRestart": "Leyfa netþjóni að endurræsa sig sjálfkrafa til þess að uppfæra sig", + "LabelAllowHWTranscoding": "Leyfa vélbúnaðarumkóðun", + "Label3DFormat": "3D snið:", + "HeaderIdentification": "Auðkenning" } diff --git a/src/strings/it.json b/src/strings/it.json index 2a69625b3d..474ff5480e 100644 --- a/src/strings/it.json +++ b/src/strings/it.json @@ -14,13 +14,13 @@ "Albums": "Album", "All": "Tutto", "AllChannels": "Tutti i canali", - "AllComplexFormats": "Tutti i formati complessi (ASS, SSA, VOBSUB, PGS, SUB, IDX)", + "AllComplexFormats": "Tutti i formati complessi (ASS, SSA, VOBSUB, PGS, SUB, IDX, ...)", "AllEpisodes": "Tutti gli episodi", "AllLanguages": "Tutte le lingue", "AllLibraries": "Tutte le librerie", "AllowHWTranscodingHelp": "Abilita il sintonizzatore per codificare i flussi al volo. Ciò potrebbe contribuire a ridurre la transcodifica richiesta dal server.", "AllowOnTheFlySubtitleExtraction": "Consenti l'estrazione sottotitoli al volo", - "AllowOnTheFlySubtitleExtractionHelp": "I sottotitoli incorporati possono essere estratti dai video e consegnati ad applicazioni in testo semplice per evitare la transcodifica dei video. In alcuni sistemi questo può richiedere molto tempo e causare un rallentamento della riproduzione video durante il processo di estrazione. Disattivare questa opzione per avere i sottotitoli incorporati con la transcodifica video quando non sono supportati nativamente dal dispositivo client.", + "AllowOnTheFlySubtitleExtractionHelp": "I sottotitoli incorporati possono essere estratti dai video e consegnati ai client in testo semplice per evitare la transcodifica dei video. In alcuni sistemi questo può richiedere molto tempo e causare un rallentamento della riproduzione video durante il processo di estrazione. Disattivare questa opzione per avere i sottotitoli incorporati con la transcodifica video quando non sono supportati nativamente dal dispositivo client.", "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.", @@ -28,7 +28,7 @@ "AlwaysPlaySubtitlesHelp": "I sottotitoli corrispondenti alla lingua preferita saranno caricati a prescindere dalla lingua dell'audio.", "AnyLanguage": "Qualsiasi lingua", "Anytime": "In qualsiasi momento", - "AroundTime": "Circa {0}", + "AroundTime": "Circa", "Artists": "Artisti", "AsManyAsPossible": "Tutto il possibile", "Ascending": "Crescente", @@ -40,12 +40,12 @@ "BirthDateValue": "Nato il: {0}", "BirthLocation": "Luogo di nascita", "BirthPlaceValue": "nato a: {0}", - "BookLibraryHelp": "Libri e audiolibri sono supportati. Rivedere {0}la guida ai nomi dei libri di Jellyfin{1}.", + "BookLibraryHelp": "Libri e audiolibri sono supportati. Rivedi la guida {0} ai nomi dei libri {1} di Jellyfin.", "Books": "Libri", "BoxRear": "Box (retro)", "Browse": "Esplora", "BrowsePluginCatalogMessage": "Sfoglia il catalogo dei Plugins.", - "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.", + "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", @@ -93,7 +93,7 @@ "ButtonRename": "Rinomina", "ButtonRepeat": "Ripeti", "ButtonResetEasyPassword": "Resetta codice PIN", - "ButtonResetPassword": "Ripristina Password", + "ButtonResetPassword": "Reset Password", "ButtonRestart": "Riavvia", "ButtonResume": "Riprendi", "ButtonRevoke": "Revocare", @@ -202,7 +202,7 @@ "EnableStreamLoopingHelp": "Abilita questo se gli streaming in diretta contengono solo pochi secondi di dati e devono essere costantemente richiesti. L'abilitazione di questa funzione quando non è servita può causare problemi.", "EnableThemeSongs": "Canzoni a tema", "EnableThemeSongsHelp": "Le canzoni a tema saranno riprodotte mentre visualizzi la tua libreria.", - "EnableThemeVideos": "VIdeo a tema", + "EnableThemeVideos": "Video a tema", "EnableThemeVideosHelp": "Riproduzione dei video a tema sullo sfondo mentre visualizzi la tua libreria.", "Ended": "Finito", "EndsAtValue": "Finirà alle {0}", @@ -461,7 +461,7 @@ "Images": "Immagini", "ImportFavoriteChannelsHelp": "Se abilitata, solo i canali che sono contrassegnati come preferiti sul dispositivo di sintonizzazione verranno importati.", "ImportMissingEpisodesHelp": "Se abilitato, le informazioni relative agli episodi mancanti saranno importate nel database di Jellyfin e mostrate all'interno di Serie e Stagioni. Questo può causare scansioni della libreria più lente.", - "InstallingPackage": "Installazione di {0}", + "InstallingPackage": "Installazione di {0} (versione {1})", "InstantMix": "Mix istantaneo", "ItemCount": "{0} elementi", "Items": "Elementi", @@ -551,7 +551,7 @@ "LabelEmbedAlbumArtDidl": "Inserisci le copertine degli Album in Didl", "LabelEmbedAlbumArtDidlHelp": "Alcuni dispositivi preferiscono questo metodo per ottenere le copertine degli album. Altri possono non riuscire a riprodurli con questa opzione abilitata.", "LabelEnableAutomaticPortMap": "Abilita mappatura automatica delle porte", - "LabelEnableAutomaticPortMapHelp": "Tenta di mappare automaticamente la porta pubblica sulla porta locale tramite UPnP. Questo potrebbe non funzionare con alcuni modelli di router. I cambiamenti non saranno applicati fino ad un riavvio del server.", + "LabelEnableAutomaticPortMapHelp": "Automaticamente inoltra le porte pubbliche del router sul quelle locali del server tramite UPnP. Potrebbe non funzionare con alcuni modelli di router. I cambiamenti non saranno applicati fino ad il riavvio del server.", "LabelEnableBlastAliveMessages": "Invia segnale di presenza", "LabelEnableBlastAliveMessagesHelp": "Attivare questa opzione se il server non viene rilevato in modo affidabile da altri dispositivi UPnP in rete.", "LabelEnableDlnaClientDiscoveryInterval": "Intervallo di ricerca dispositivi (secondi)", @@ -671,7 +671,7 @@ "LabelNumberOfGuideDays": "Numero di giorni per i quali scaricare i dati della guida:", "LabelNumberOfGuideDaysHelp": "Scaricando più giorni si avrà la possibilità di pianificare in anticipo più programmi e vedere più liste, ma il tempo di download si allungherà. 'Auto': MB sceglierà automaticamente in base al numero di canali.", "LabelOptionalNetworkPath": "Cartella condivisa (Opzionale):", - "LabelOptionalNetworkPathHelp": "Se questa cartella è condivisa sulla rete, fornendo il percorso di condivisione di rete si può consentire alle applicazioni Jellyfin su altri dispositivi di accedere direttamente ai file multimediali.", + "LabelOptionalNetworkPathHelp": "Se questa cartella è condivisa sulla rete, fornendo il percorso di condivisione di rete si può consentire alle applicazioni Jellyfin su altri dispositivi di accedere direttamente ai file multimediali. Ad esempio {0} oppure {1}.", "LabelOriginalAspectRatio": "Aspetto originale:", "LabelOriginalTitle": "Titolo originale:", "LabelOverview": "Trama:", @@ -995,7 +995,7 @@ "OptionMissingEpisode": "Episodi mancanti", "OptionMonday": "Lunedì", "OptionNameSort": "Nome", - "OptionNew": "Nuovo...", + "OptionNew": "Nuovo…", "OptionNone": "Nessuno", "OptionOnAppStartup": "All'avvio", "OptionOnInterval": "Su intervallo", @@ -1035,14 +1035,14 @@ "OptionWeekly": "Settimanale", "OriginalAirDateValue": "Prima messa in onda (originale): {0}", "Overview": "Trama", - "PackageInstallCancelled": "Installazione di {0} annullata.", - "PackageInstallCompleted": "Installazione di {0} completa.", - "PackageInstallFailed": "Installazione di {0} fallita.", + "PackageInstallCancelled": "Installazione di {0} (versione {1}) annullata.", + "PackageInstallCompleted": "Installazione di {0} (versione {1}) completata.", + "PackageInstallFailed": "Installazione di {0} (versione {1}) fallita.", "ParentalRating": "Classificazione per genitori", "PasswordMatchError": "Le password non coincidono.", - "PasswordResetComplete": "la password è stata ripristinata.", - "PasswordResetConfirmation": "Sei sicuro di voler ripristinare la password?", - "PasswordResetHeader": "Ripristina Password", + "PasswordResetComplete": "Reset della password eseguito.", + "PasswordResetConfirmation": "Sicuro di voler eseguire il reset della password?", + "PasswordResetHeader": "Reset Password", "PasswordSaved": "Password salvata.", "People": "Attori", "PerfectMatch": "Corrispondenza perfetta", @@ -1202,7 +1202,7 @@ "TabProfiles": "Profili", "TabRecordings": "Registrazioni", "TabResponses": "Risposte", - "TabResumeSettings": "Ripristina", + "TabResumeSettings": "Riprendi", "TabScheduledTasks": "Operazioni Pianificate", "TabSeries": "Serie TV", "TabSettings": "Impostazioni", @@ -1342,7 +1342,7 @@ "MediaInfoStreamTypeVideo": "Video", "MessageNoCollectionsAvailable": "Le collezioni ti consentono di fruire di raggruppamenti personalizzati di Film, Serie e Album. Clicca il tasto + per iniziare a creare collezioni.", "MessageNoServersAvailable": "Nessun server è stato trovato usando la ricerca automatica di server.", - "LabelBaseUrlHelp": "Puoi aggiungere una sottodirectory personalizzata qui per accedere al server da un URL più originale.", + "LabelBaseUrlHelp": "Aggiunge una cartella personalizzata all'URL del server, ad esempio http://example.com/<baseurl>", "OptionAlbum": "Album", "LabelPasswordResetProvider": "Provider per il Reset della Password:", "LabelServerName": "Nome del Server:", @@ -1355,7 +1355,7 @@ "OptionProtocolHls": "Streaming in Diretta HTTP", "OptionDownloadArtImage": "Art", "OptionMax": "Massimo", - "PasswordResetProviderHelp": "Scegli un Provider Reset Password da utilizzare quando questo utente richiede un reset della password", + "PasswordResetProviderHelp": "Scegli un Provider Reset Password da utilizzare quando questo utente ne richiede il reset", "PlaybackData": "Dati di Riproduzione", "TagsValue": "Tag: {0}", "Whitelist": "Lista bianca", @@ -1455,9 +1455,7 @@ "MessageConfirmAppExit": "Vuoi uscire?", "HeaderNavigation": "Navigazione", "CopyStreamURLError": "Si è verificato un errore nel copiare l'indirizzo.", - "EnableFastImageFadeInHelp": "Abilita la dissolvenza veloce per le immagini caricate", - "EnableFastImageFadeIn": "Dissolvenza immagine veloce", - "PlaybackErrorNoCompatibleStream": "C'era un problema con il profiling del client e il server non sta inviando un formato compatibile.", + "PlaybackErrorNoCompatibleStream": "Il client è incompatibile con il media e il server non sta inviando un formato compatibile.", "OptionForceRemoteSourceTranscoding": "Forza la transcodifica da fonti di media remoti (come LiveTV)", "NoCreatedLibraries": "Sembra che tu non abbia ancora creato delle librerie. {0}Vuoi crearne una adesso?{1}", "LabelVideoResolution": "Risoluzione video:", @@ -1471,5 +1469,86 @@ "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", - "Album": "Album" + "Album": "Album", + "OnWakeFromSleep": "Al risveglio", + "Person": "Persona", + "LabelDeinterlaceMethod": "Metodo di deinterlacciamento:", + "DeinterlaceMethodHelp": "Metodo di deinterlacciamento da usare durante la transcodifica.", + "Artist": "Artista", + "OnApplicationStartup": "All'avvio", + "EveryXHours": "Ogni {0} ore", + "EveryHour": "Ogni ora", + "EveryXMinutes": "Ogni {0} minuti", + "WeeklyAt": "Tutti i {0} alle {1}", + "DailyAt": "Tutti i giorni alle {0}", + "LastSeen": "Visto l'ultima volta {0}", + "PersonRole": "nel ruolo di {0}", + "ListPaging": "{0}-{1} di {2}", + "WriteAccessRequired": "Jellyfin Server richiede il permesso di scrittura su questa cartella. Verificare l'autorizzazione e riprovare.", + "PathNotFound": "Percorso non trovato. Assicurarsi che sia valido e riprovare.", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "Track": "Traccia", + "Season": "Stagione", + "OtherArtist": "Altri Artisti", + "Movie": "Film", + "LabelLibraryPageSizeHelp": "Numero di elementi presenti nella paginazione della libreria. Il valore 0 disabilita la paginazione.", + "LabelLibraryPageSize": "Elementi nella paginazione della libreria:", + "Episode": "Episodio", + "BoxSet": "Cofanetto", + "AlbumArtist": "Artisti dell'Album", + "ReleaseGroup": "Release Group", + "UnsupportedPlayback": "Jellyfin non è in grado di decriptare i contenuti protetti da DRM ma tutti i contenuti verranno tentati a prescindere, compresi quelli protetti. Alcuni file potrebbero apparire completamente neri a causa della crittografia o di altre funzionalità non supportate, come i titoli interattivi.", + "MessageUnauthorizedUser": "Non sei autorizzato ad accedere in questo momento al server. Contatta l'amministratore per ulteriori dettagli.", + "ButtonTogglePlaylist": "Playlist", + "ButtonToggleContextMenu": "Altro", + "HeaderFavoritePlaylists": "Playlist Favorite", + "Filter": "Filtro", + "New": "Nuovo", + "ApiKeysCaption": "Elenco chiavi API abilitate", + "LabelStable": "Stabile", + "LabelChromecastVersion": "Versione Chromecast", + "LabelRequireHttpsHelp": "Se selezionata, il server reindirizzerà tutte le richieste HTTP a HTTPS. Vale solo se il sever è configurato per l'ascolto in HTTPS.", + "LabelRequireHttps": "Richiede HTTPS", + "LabelEnableHttpsHelp": "Abilita il server all'ascolto sulla porta HTTPS. Il certificato deve essere configurato e valido per l'abilitazione.", + "LabelEnableHttps": "Abilita HTTPS", + "HeaderServerAddressSettings": "Configurazione Indirizzo Server", + "HeaderRemoteAccessSettings": "Configurazione Access Remoto", + "HeaderHttpsSettings": "Configurazione HTTPS", + "TabDVR": "DVR", + "SaveChanges": "Salva modifiche", + "HeaderDVR": "DVR", + "LabelNightly": "Nightly", + "SyncPlayAccessHelp": "Scegli il livello d'accesso di questo utente a SyncPlay. SyncPlay ti permette di riprodurre contemporaneamente su diversi dispositivi.", + "MessageSyncPlayErrorMedia": "Impossibile abilitare SyncPlay! Errore media.", + "MessageSyncPlayErrorMissingSession": "Impossibile abilitare SyncPlay! Sessione mancante.", + "MessageSyncPlayErrorNoActivePlayer": "Nessun player attivo. SyncPlay è stato disabilitato.", + "MessageSyncPlayErrorAccessingGroups": "Errore durante l'accesso alla lista dei gruppi.", + "MessageSyncPlayLibraryAccessDenied": "L'accesso a questo contenuto è negato.", + "MessageSyncPlayJoinGroupDenied": "E' Necessario il permesso per l'utilizzo di SyncPlay.", + "MessageSyncPlayCreateGroupDenied": "E' necessario il permesso di creazione di un gruppo.", + "MessageSyncPlayGroupDoesNotExist": "Impossibile unirsi al gruppo perchè non esiste.", + "MessageSyncPlayPlaybackPermissionRequired": "Permesso di riproduzione necessario.", + "MessageSyncPlayNoGroupsAvailable": "Nessun gruppo disponibile. Inizia a riprodurre qualcosa.", + "MessageSyncPlayGroupWait": "{0} sta bufferizzando...", + "MessageSyncPlayUserLeft": "{0} ha lasciato il gruppo.", + "MessageSyncPlayUserJoined": "{0} si è unito al gruppo.", + "MessageSyncPlayDisabled": "SyncPlay disabilitato.", + "MessageSyncPlayEnabled": "SyncPlay abilitato.", + "LabelSyncPlayAccess": "Accesso SyncPlay", + "LabelSyncPlayAccessNone": "Disabilitato per questo utente", + "LabelSyncPlayAccessJoinGroups": "Permetti all'utente di unirsi ai gruppi", + "LabelSyncPlayAccessCreateAndJoinGroups": "Permetti all'utente di creare e unirsi ai gruppi", + "LabelSyncPlayLeaveGroupDescription": "Disabilita SyncPlay", + "LabelSyncPlayLeaveGroup": "Lascia il gruppo", + "LabelSyncPlayNewGroupDescription": "Crea un nuovo gruppo", + "LabelSyncPlayNewGroup": "Nuovo gruppo", + "LabelSyncPlaySyncMethod": "Metodo Sync:", + "LabelSyncPlayPlaybackDiff": "Differenza oraria nella riproduzione:", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Differenza temporale con il server:", + "HeaderSyncPlayEnabled": "SyncPlay abilitato", + "HeaderSyncPlaySelectGroup": "Unisciti a un gruppo", + "EnableDetailsBannerHelp": "Mostra il banner nella parte superiore della pagina di dettaglio dell'elemento.", + "EnableDetailsBanner": "Banner Dettagli" } diff --git a/src/strings/ja.json b/src/strings/ja.json index 4a15d798c3..0f90a05c40 100644 --- a/src/strings/ja.json +++ b/src/strings/ja.json @@ -1139,5 +1139,10 @@ "LabelEnableBlastAliveMessages": "アライブメッセージを配信する", "LabelDateAddedBehaviorHelp": "メタデータある場合、これらのオプションの前にメタデータ使います。", "AskAdminToCreateLibrary": "管理者にライブラリを作成する依頼をしてください。", - "AllowFfmpegThrottling": "トランスコードをスロットルする" + "AllowFfmpegThrottling": "トランスコードをスロットルする", + "Episode": "エピソード", + "ClientSettings": "クライアント設定", + "Artist": "アーティスト", + "AlbumArtist": "アルバム アーティスト", + "Album": "アルバム" } diff --git a/src/strings/kk.json b/src/strings/kk.json index 852e408a3b..2c2c7c3782 100644 --- a/src/strings/kk.json +++ b/src/strings/kk.json @@ -15,7 +15,7 @@ "Alerts": "Eskertýler", "All": "Bári", "AllChannels": "Barlyq arnalar", - "AllComplexFormats": "Barlyq kúrdeli pishimderi (ASS, SSA, VOBSUB, PGS, SUB/IDX jáne t.b.)", + "AllComplexFormats": "Barlyq kúrdeli pishimder (ASS, SSA, VOBSUB, PGS, SUB jáne IDX)", "AllEpisodes": "Barlyq bólimder", "AllLanguages": "Barlyq tilder", "AllLibraries": "Barlyq tasyǵyshhanalar", @@ -27,10 +27,9 @@ "AllowRemoteAccess": "Osy Jellyfin Serverine syrtqy qosylymdar úshin ruqsat etý.", "AllowRemoteAccessHelp": "Eger jalaýsha alastalǵan bolsa, baryq syrtqy baılanystar qursaýlanady.", "AllowedRemoteAddressesHelp": "Qashyqtan qosylýǵa ruqsat etiletin jeliler úshin útirlermen bólingen IP-mekenjaılarynyń tizbesi nemese IP/netmask jazbalar Eger bos qaldyrylsa, barlyq qashyqtaǵy mekenjaılarǵa ruqsat etiledi.", - "AlwaysPlaySubtitles": "Sýbtıtrlerdi árqashan oınatý", + "AlwaysPlaySubtitles": "Árqashan oınatý", "AlwaysPlaySubtitlesHelp": "Til teńshelimine sáıkes kelgen sýbtıtrler dybys tiline qatyssyz júkteledi.", "AnamorphicVideoNotSupported": "Anamorftyq beıne úshin qoldaý kórsetilmeıdi", - "AndroidUnlockRestoreHelp": "Aldyńǵy satyp alýdy qalpyna keltirý úshin, bastapqyda satyp alý jasalǵan naq sol Google (nemese Amazon) tirkelgisimen qurylǵyǵa kirińiz. Qoldanba dúkeni qosylǵan jáne kezkelgen ata-ana shekteýsiz, jáne belsendi ınternet baılanysy bar ekenine kóz jetkizińiz. Aldyńǵy satyp alý qalpyna keltirý úshin muny tek qana bir ret isteý kerek.", "AnyLanguage": "Qaı-qaısy til", "Anytime": "Árkezde", "AroundTime": "{0} aınalasynda", @@ -56,7 +55,7 @@ "BoxRear": "Qorap arty", "Browse": "Sharlaý", "BrowsePluginCatalogMessage": "Qoljetimdi plagındermen tanysý úshin plagın tizimdemesin sholyńyz.", - "BurnSubtitlesHelp": "Sýbtıtrler pishimine baılanysty beıneni túrlendirgen kezde server sýbtıtrlerdi jazyýyn anyqtaıdy. Sýbtıtrler jazýdy qashqaqtaý serverdiń ónimdiligin jaqsartady. Sýretke negizdelgen pishimderdi (VOBSUB, PGS, SUB/IDX j.t.b.) jáne keıbir ASS/SSA sýbtıtrlerin jazý úshin Avtomattyny tańdańyz.", + "BurnSubtitlesHelp": "Beıneni qaıta kodtaǵan kezde server sýbtıtrlerdi jazyýyn anyqtaıdy. Onan qashqaqtaý serverdiń ónimdiligin biraz jaqsartady. Sýretke negizdelgen pishimderdi (VOBSUB, PGS, SUB jáne IDX) jáne keıbir ASS nemese SSA sýbtıtrlerin jazý úshin Avtomattyny tańdańyz.", "ButtonAdd": "Ústeý", "ButtonAddMediaLibrary": "Tasyǵyshhana ústeý", "ButtonAddScheduledTaskTrigger": "Trıger ústeý", @@ -198,7 +197,7 @@ "DisplayInOtherHomeScreenSections": "Basqy ekran bólimderinde beıneleý (mys. Eń sońǵy tasyǵyshderekter jáne Kórýdi jalǵastyrý)", "DisplayMissingEpisodesWithinSeasons": "Joq bólimderdi maýsym ishinde beıneleý", "DisplayMissingEpisodesWithinSeasonsHelp": "Bul sondaı-aq server konfıgýrasýasyndaǵy TD tasyǵyshhanalary úshin qosýlýy qajet.", - "DisplayModeHelp": "Jellyfin iske qosylǵanda ekran túrin tańdańyz.", + "DisplayModeHelp": "Interfeıs úshin laıyqty ornalasý mánerin tańdańyz.", "DoNotRecord": "Jazýǵa bolmaıdy", "Down": "Tómenge", "Download": "Júktep alý", @@ -503,7 +502,7 @@ "Images": "Sýretter", "ImportFavoriteChannelsHelp": "Qosylǵanda, túner qurylǵysyndaǵy tańdaýly retinde belgilengen ǵana arnalar shetten ákelinetin bolady.", "ImportMissingEpisodesHelp": "Qosylǵanda, joq epızodtar týraly aqparat sizdiń Jellyfin derekqorǵa ákelinedi jáne maýsymdar men telehıkaıalar aıasynda paıda bolady. Tasyǵyshhana skanerleýde bul aıtarlyqtaı uzaq ýaqyt alýy múmkin.", - "InstallingPackage": "{0} ornatylýda", + "InstallingPackage": "{0} ({1} nusqasy) ornatylýda", "InstantMix": "Lezdik qospalaý", "ItemCount": "{0} tarmaq", "Items": "Tarmaqtar", @@ -834,7 +833,6 @@ "LabelTypeText": "Mátin", "LabelUnairedMissingEpisodesWithinSeasons": "Kórsetilmegen bólimderdi maýsym ishinde beıneleý", "LabelUnknownLanguage": "Belgisiz til", - "LabelUploadSpeedLimit": "Júktep salý qarqynynyń shegi, Mbıt/s:", "LabelUrl": "URL:", "LabelUseNotificationServices": "Kelesi qyzmetterdi paıdalaný:", "LabelUser": "Paıdalanýshy:", @@ -872,7 +870,6 @@ "LiveBroadcasts": "Tikeleı taratymdar", "LiveTV": "Efır", "LiveTvFeatureDescription": "Jellyfin Server ornatylǵan úılesimdi TD-túner qurylǵysy arqyly kezkelgen Jellyfin-qoldanbaǵa TD-efırdi tikeleı jiberý.", - "LiveTvRequiresUnlock": "", "LiveTvUpdateAvailable": "(Jańartý qoljetimdi)", "LoginDisclaimer": "Jellyfin jeke tasyǵyshhanańyzdy (mysaly, úılik beıneler men fotosýretterdi) basqarýǵa kómektesý úshin arnalǵan. Bizdiń paıdalaný sharttaryn qarańyz. Kezkelgen Jellyfin baǵdarlamalyq jasaqtamasyn paıdalnǵanda osy sharttardyń qabyldaýyn bildiredi.", "Logo": "Logotıp", @@ -950,7 +947,6 @@ "MessagePluginConfigurationRequiresLocalAccess": "Osy plagındi teńsheý úshin jergilikti serverińizge tikeleı kirińiz.", "MessagePluginInstallDisclaimer": "Jellyfin qaýymdastyǵy múshelerimen qurylǵan plagınder Jellyfin tájirıbeńizdi qosymsha múmkindiktermen jáne jeńildiktermen jaqsartý úshin jaqsy tásili bolyp tabylady. Ornatpas buryn, olar Jellyfin serverińizge tasyǵyshhanany uzaq skanerleý, qosymsha óńdik óńdetý jáne júıeniń turaqtylyǵyn tómendetý sıaqty áserler etýge múmkin bolýyna habardar bolyńyz.", "MessageReenableUser": "Qaıta qosý úshin tómende qarańyz", - "MessageServerConfigurationUpdated": "Server konfıgýrasıasy jańartyldy", "MessageSettingsSaved": "Parametrler saqtaldy.", "MessageTheFollowingLocationWillBeRemovedFromLibrary": "Tasyǵyshhanańyzdan kelesi tasyǵysh ornalasýlary alastalady:", "MessageUnableToConnectToServer": "Tańdalǵan serverge qosylýymyz dál qazir múmkin emes. Bul iske qosylǵanyna kóz jetkizińiz jáne áreketti keıin qaıtalańyz.", @@ -986,16 +982,16 @@ "NoNextUpItemsMessage": "Eshteńe tabylmady. Kórsetimderińizdi qaraı bastańyz!", "NoPluginConfigurationMessage": "Osy plagınde teńsheletin parametrler joq.", "NoSubtitleSearchResultsFound": "Eshqandaı nátıjeler tabylmady.", - "NoSubtitles": "Sýbtıtrlersiz", + "NoSubtitles": "Eshqandaı", "NoSubtitlesHelp": "Ádepkide sýbtıtrler júktelmeıdi. Olardy oınatý kezinde áli de qolmen qosýǵa bolady.", "None": "Eshqandaı", "Normal": "Kádimgi", "NumLocationsValue": "{0} qalta", "Off": "Óshir", "OneChannel": "Bir arnadan", - "OnlyForcedSubtitles": "Tek qana májbúrli sýbtıtrler", + "OnlyForcedSubtitles": "Tek qana májbúrli", "OnlyForcedSubtitlesHelp": "Tek qana májbúrli dep belgilengen sýbtıtrler júkteledi.", - "OnlyImageFormats": "Tek keskin pishimder (VOBSUB, PGS, SUB j.t.b.)", + "OnlyImageFormats": "Tek keskin pishimder (VOBSUB, PGS jáne SUB)", "Open": "Ashý", "OptionActor": "Aktór", "OptionActors": "Aktórler", @@ -1138,9 +1134,9 @@ "OptionWeekly": "Apta saıyn", "OriginalAirDateValue": "Bastapqy efır: {0}", "Overview": "Jalpy sholý", - "PackageInstallCancelled": "{0} ornatylýy boldyrylmady.", - "PackageInstallCompleted": "{0} ornatylýy aıaqtaldy.", - "PackageInstallFailed": "{0} ornatylýy sátsiz.", + "PackageInstallCancelled": "{0} ({1} nusqasy) ornatylýy boldyrylmady.", + "PackageInstallCompleted": "{0} ({1} nusqasy) ornatylýy aıaqtaldy.", + "PackageInstallFailed": "{0} ({1} nusqasy) ornatylýy sátsiz.", "ParentalRating": "Jastas sanaty", "PasswordMatchError": "Paróli men Paróldi rastaý óristeri sáıkes bolý kerek.", "PasswordResetComplete": "Paról ysyryldy.", @@ -1229,7 +1225,6 @@ "SearchForSubtitles": "Sýbtıtrlerdi izdeý", "SearchResults": "Izdeý nátıjeleri", "SecondaryAudioNotSupported": "Dybys jolshyǵyn aýystyrý úshin qoldaý kórsetilmeıdi", - "SelectCameraUploadServers": "Kameradan fotosýretterdi kelesi serverlerge júktep salý:", "SendMessage": "Habar jiberý", "Series": "Telehıkaıa", "SeriesCancelled": "Telehıkaıa boldyrylmady.", @@ -1346,8 +1341,6 @@ "TrackCount": "{0} jolshyq", "Trailers": "Treılerler", "Transcoding": "Qaıta kodtaýda", - "TryMultiSelect": "Úndesken bólekteýdi synap kórý", - "TryMultiSelectMessage": "Birneshe tasyǵysh derekter elementterin óńdeý úshin, kezkelgen posterdi jaı ǵana tintýir batyrmaǵa basyp turyp nuqyńyz jáne basqarýyn qalaǵan elementterdi bólekteńiz. Synap kórińiz!", "Tuesday": "seısenbi", "TvLibraryHelp": "{0}TD-kórsetimdi ataý nusqaýlyǵyn{1} qarap shyǵý.", "Uniform": "Biryńǵaı", @@ -1491,8 +1484,6 @@ "MessageConfirmAppExit": "Shyǵýdy qalaısyz ba?", "LabelVideoResolution": "Beıne ajyratymdylyǵy:", "LabelStreamType": "Aǵyn túri:", - "EnableFastImageFadeInHelp": "Júktelgen sýretter úshin shapshan kórsetilýin qosý", - "EnableFastImageFadeIn": "Sýrettiń shapshan kórsetilýi", "LabelPlayerDimensions": "Oınatqysh ólshemderi:", "LabelDroppedFrames": "Ótkizilgen kadrlar:", "LabelCorruptedFrames": "Búlingen kadrlar:", @@ -1501,6 +1492,35 @@ "ButtonSplit": "Bólý", "AskAdminToCreateLibrary": "Tasýǵyshanany jasaý úshin ákimshiden suraý.", "AllowFfmpegThrottling": "Qaıta kodtaýdy retteý", - "PlaybackErrorNoCompatibleStream": "Klıent profaılyn jasaýda másele oryn aldy jáne server úılesimdi pishiminde tasyǵysh derekterin jibermedi.", - "AllowFfmpegThrottlingHelp": "Qaıta kodtaý nemese qaıta býmalaý aǵymdyq oınatý jaıǵasymynan edáýir alǵa ozǵanda, qor kózderin azdaý tutynatyndaı etip údiristi kidirtedi. Bul jıi izdemeı qaraý kezinde paıdaly. Eger oınatý máseleleri bolsa, ony óshirińiz." + "PlaybackErrorNoCompatibleStream": "Bul klıent tasyǵysh derektermen úılesimsiz jáne server úılesimdi pishiminde tasyǵysh derekterin jibermedi.", + "AllowFfmpegThrottlingHelp": "Qaıta kodtaý nemese qaıta býmalaý aǵymdyq oınatý jaıǵasymynan edáýir alǵa ozǵanda, qor kózderin azdaý tutynatyndaı etip údiristi kidirtedi. Bul jıi izdemeı qaraý kezinde paıdaly. Eger oınatý máseleleri bolsa, ony óshirińiz.", + "Album": "Álbom", + "DeinterlaceMethodHelp": "Jol aralyq jaımaly mazmundy qaıta kodtaý kezinde paıdalaný úshin jol aralyq jaımany joıý ádisin tańdańyz.", + "LabelDeinterlaceMethod": "Jol aralyq jaımany joıý ádisi:", + "YadifBob": "YADIF eki eseleýimen", + "OnApplicationStartup": "Qoldanba iske qosylǵanda", + "EveryXHours": "Ár {0} saǵ", + "EveryHour": "Ár saǵat", + "EveryXMinutes": "Ár {0} mın", + "OnWakeFromSleep": "Uıqylyqtan oıanǵanda", + "WeeklyAt": "{0} {1} kezinde", + "DailyAt": "Kúnde {0} kezinde", + "LastSeen": "Sońǵy kóringeni {0}", + "PersonRole": "- {0}", + "ListPaging": "{0}-{1} {2} ishinen", + "Yadif": "YADIF", + "Track": "Jolshyq", + "Season": "Maýsym", + "ReleaseGroup": "Shyǵarýshy top", + "Person": "Tulǵa", + "OtherArtist": "Basqa oryndaýshy", + "Movie": "Fılm", + "LabelLibraryPageSize": "Tasyǵyshhana betiniń ólshemi:", + "Episode": "Bólim", + "ClientSettings": "Týtynýshy parametrleri", + "ButtonTogglePlaylist": "Oýnatý tizimi", + "ButtonToggleContextMenu": "Kúbirek", + "BoxSet": "Jıyntyq", + "Artist": "Ornatýshy", + "AlbumArtist": "Álbom ornatýshysy" } diff --git a/src/strings/ko.json b/src/strings/ko.json index acd07a336b..4c3cd60da1 100644 --- a/src/strings/ko.json +++ b/src/strings/ko.json @@ -976,7 +976,7 @@ "OptionEnableForAllTuners": "모든 튜너 장치 활성화", "OptionBanner": "배너", "Option3D": "3D", - "OnlyImageFormats": "이미지 포맷만 (VOBSUB, PGS, SUB 등)", + "OnlyImageFormats": "이미지 포맷만 (VOBSUB, PGS, SUB)", "Off": "끄기", "NumLocationsValue": "{0} 폴더", "Normal": "보통", @@ -1265,7 +1265,7 @@ "MediaInfoBitDepth": "비트뎁스", "LabelPostProcessor": "후처리 애플리케이션:", "RefreshQueued": "새로 고침 대기 중", - "NoPluginConfigurationMessage": "", + "NoPluginConfigurationMessage": "이 플러그인은 설정할 것이 없습니다.", "OptionExtractChapterImage": "챕터 이미지 추출 활성화", "RestartPleaseWaitMessage": "Jellyfin 서버가 종료되었다가 다시 시작될 때까지 기다리십시오. 1-2분 정도 걸릴 수 있습니다.", "Up": "위", @@ -1325,7 +1325,7 @@ "MediaInfoPixelFormat": "픽셀 형식", "MapChannels": "채널 매핑", "LaunchWebAppOnStartupHelp": "서버가 처음 시작되면 웹 브라우저에서 웹 클라이언트를 실행하십시오. 서버 재시작의 경우에는 적용되지 않습니다.", - "Large": "큰", + "Large": "크게", "LanNetworksHelp": "대역폭을 강제로 제한할 때 로컬 네트워크로 간주되는 쉼표로 구분된 IP 주소 및 IP/서브넷 마스크 목록입니다. 지정될 경우 모든 다른 IP 주소는 외부 네트워크로 간주되며 외부 대역폭 제한이 적용됩니다. 공백일 경우 서버의 서브넷만이 로컬 네트워크로 간주됩니다.", "LabelffmpegPathHelp": "ffmpeg 실행 파일 혹은 ffmpeg를 포함하는 폴더 경로입니다.", "LabelXDlnaDocHelp": "urn:schemas-dlna-org:device-1-0 네임스페이스에 포함된 X_DLNADOC 요소의 내용을 결정합니다.", @@ -1343,8 +1343,6 @@ "LabelSkipIfAudioTrackPresentHelp": "오디오 언어와 상관없이 모든 비디오가 자막을 갖추도록 하려면 이 항목을 체크하지 마십시오.", "LabelSelectFolderGroupsHelp": "체크되지 않은 폴더는 폴더 고유의 보기 방식으로 표시됩니다.", "LabelSelectFolderGroups": "자동으로 이하의 폴더의 항목을 영화, 음악, TV 와 같은 보기 방식으로 그룹화:", - "EnableFastImageFadeInHelp": "로드된 이미지에 더 빠른 페이드 인 효과를 적용", - "EnableFastImageFadeIn": "빠른 이미지 페이드 인 효과", "LabelScheduledTaskLastRan": "최근 실행: {0}, 소모시간: {1}.", "LabelRemoteClientBitrateLimitHelp": "(선택) 모든 외부 네트워크로 접속된 장치들에 적용되는 각 스트리밍별 비트레이트 제한입니다. 이는 서버의 인터넷이 처리할 수 있는 한계보다 더 높은 비트레이트를 요청하는 것을 방지할 수 있습니다. 비디오를 더 낮은 비트레이트로 트랜스코딩하기 위해 서버에 높은 CPU 부하를 줄 수 있습니다.", "LabelReasonForTranscoding": "트랜스코딩 원인:", @@ -1378,24 +1376,60 @@ "LabelDefaultScreen": "기본 화면:", "LabelDateTimeLocale": "날짜/시간 로케일:", "XmlTvPathHelp": "XMLTV 파일을 저장할 경로를 설정합니다. Jellyfin은 이 파일을 읽어 주기적으로 변경 사항을 확인합니다. 파일 생성 및 파일 업데이트는 사용자가 수동으로 해야 합니다.", - "MessageTheFollowingLocationWillBeRemovedFromLibrary": "다음 미디어 위치가 라이브러리에서 제거:", + "MessageTheFollowingLocationWillBeRemovedFromLibrary": "다음과 같은 미디어 저장소들을 라이브러리에서 제거합니다:", "MessageReenableUser": "재활성화는 아래를 참조하십시오", "MessagePluginConfigurationRequiresLocalAccess": "이 플러그인을 구성하려면 로컬 서버에 직접 로그인하십시오.", "MessageNoCollectionsAvailable": "컬렉션을 사용하면 영화, 시리즈 및 앨범의 개인화 된 그룹을 즐길 수 있습니다. + 버튼을 클릭하여 컬렉션 만들기를 시작합니다.", - "LabelPlayerDimensions": "플레이어 넓이:", + "LabelPlayerDimensions": "플레이어 화면 크기:", "LabelParentNumber": "부모 번호:", "LabelLineup": "라인업:", - "LabelDroppedFrames": "떨어진 프레임:", + "LabelDroppedFrames": "드롭 프레임:", "LabelDeinterlaceMethod": "디인터레이싱 방법:", - "LabelCustomDeviceDisplayNameHelp": "디바이스에서 보고 한 이름을 사용하려면 사용자 정의 표시 이름을 제공하거나 비워 두십시오.", + "LabelCustomDeviceDisplayNameHelp": "기기에 표시할 이름을 지정하거나 공란으로 두어 등록된 기기 명을 사용합니다.", "Episode": "에피소드", - "EnableColorCodedBackgrounds": "색이 입혀진 배경", + "EnableColorCodedBackgrounds": "컬러코드가 삽입된 배경", "DropShadow": "하단 그림자", - "Depressed": "압축", - "DeinterlaceMethodHelp": "인터레이스 컨텐츠를 트랜스코딩 할 때 사용할 디인터레이싱 방법을 선택하십시오.", + "Depressed": "압축된", + "DeinterlaceMethodHelp": "비월주사식 콘텐츠를 순차주사로 변환할시 사용할 비월제거 방법을 선택하십시오.", "ClientSettings": "클라이언트 설정", "BoxSet": "박스 세트", "Artist": "아티스트", "AlbumArtist": "앨범 아티스트", - "Album": "앨범" + "Album": "앨범", + "NoCreatedLibraries": "라이브러리가 없습니다. {0}지금 생성하겠습니까?{1}", + "NewCollectionHelp": "영화 및 다른 라이브러리 콘텐츠들을 묶어 개인화된 컬렉션을 구성할 수 있습니다.", + "Never": "항상 안 함", + "Movie": "영화", + "MoveRight": "오른쪽으로 이동", + "MoveLeft": "왼쪽으로 이동", + "MoreFromValue": "{0} 에서 더 자세히", + "MetadataSettingChangeHelp": "변경된 메타데이터 설정은 새 콘텐츠에 적용됩니다. 기존의 콘텐츠에 적용하려면 상세 화면에서 새로 고침 버튼을 누르거나 메타데이터 매니저를 통해 일괄적으로 새로 고침을 수행하십시오.", + "MessagePluginInstallDisclaimer": "Jellyfin 커뮤니티에서 만들어진 플러그인은 Jellyfin의 기능과 편의성을 향상시킬 수 있습니다. 다만 이러한 플러그인은 라이브러리 스캔 속도 저하, 추가 백그라운드 프로세싱, 시스템 불안정과 같은 문제를 야기할 수 있다는 것을 유념하시기 바랍니다.", + "LabelLibraryPageSizeHelp": "라이브러리 페이지에 표시될 항목 수를 조절합니다. 0으로 지정 시 페이징을 비활성화합니다.", + "LabelLibraryPageSize": "라이브러리 페이지 크기:", + "LabelEnableBlastAliveMessages": "서버 활성화 메세지", + "OptionEnableExternalContentInSuggestionsHelp": "제안 항목에 인터넷 예고편과 라이브 TV 프로그램이 포함되도록 허용합니다.", + "OptionEnableExternalContentInSuggestions": "제안 항목에 외부 콘텐츠 허용", + "OptionDownloadImagesInAdvanceHelp": "기본적으로 대부분의 이미지는 Jellyfin 앱에서 요청할 때에만 다운로드됩니다. 새 미디어를 추가할 때 모든 이미지를 미리 다운로드하려면 이 옵션을 활성화하십시오. 라이브러리 스캔이 심각하게 지연될 수도 있습니다.", + "OptionDownloadImagesInAdvance": "미리 이미지 다운로드", + "OptionDisplayFolderView": "일반적인 미디어 폴더를 볼 수 있는 폴더 보기를 표시합니다", + "OptionAutomaticallyGroupSeriesHelp": "활성화하면 라이브러리 내의 여러 폴더에 분산된 시리즈를 하나의 시리즈로 병합합니다.", + "OptionAutomaticallyGroupSeries": "여러 폴더에 분산된 시리즈를 자동으로 병합합니다", + "OptionAllowVideoPlaybackRemuxing": "변환이 필요한 비디오를 재인코딩하지 않고 재생하는 것을 허용", + "OptionAllowSyncTranscoding": "트랜스코딩이 필요한 미디어의 다운로드 및 싱크 허용", + "OptionAllowMediaPlaybackTranscodingHelp": "트랜스코딩 접근을 제한하면 Jellyfin 앱에서 지원되지 않는 미디어 형식을 재생할 때 문제가 발생할 수 있습니다.", + "OptionForceRemoteSourceTranscoding": "원격 미디어 소스를 강제 트랜스코딩 (라이브 TV 등)", + "OnlyForcedSubtitlesHelp": "'강제'로 표시된 자막만 불러옵니다.", + "OnlyForcedSubtitles": "강제로 설정한 자막만", + "OneChannel": "단채널", + "NoSubtitlesHelp": "자막을 자동으로 불러오지 않습니다. 재생 중에 수동으로 켤 수 있습니다.", + "MusicLibraryHelp": "{0}음악 이름 지정 규칙{1}을 확인하십시오.", + "MovieLibraryHelp": "{0}영화 이름 지정 규칙{1}을 확인하십시오.", + "MessageUnauthorizedUser": "현재 서버에 접속할 권한이 없습니다. 자세한 정보는 서버 관리자에게 문의하십시오.", + "HeaderFavoritePlaylists": "즐겨찾는 플레이리스트", + "ButtonTogglePlaylist": "플레이리스트", + "ButtonToggleContextMenu": "더보기", + "Rate": "평", + "PerfectMatch": "정확히 일치", + "OtherArtist": "다른 아티스트" } diff --git a/src/strings/lt-lt.json b/src/strings/lt-lt.json index c87628316c..942861f469 100644 --- a/src/strings/lt-lt.json +++ b/src/strings/lt-lt.json @@ -149,7 +149,7 @@ "Help": "Padėti", "Identify": "Identifikuoti", "Images": "Atvaizdai", - "InstallingPackage": "Diegiama {0}", + "InstallingPackage": "Diegiama {0} (versija {1})", "InstantMix": "Leisti miksą", "ItemCount": "{0} elementų", "Kids": "Vaikams", @@ -538,7 +538,7 @@ "AllLanguages": "Visos kalbos", "AllowMediaConversion": "Leisti medijos konvertavimą", "AllowRemoteAccess": "Leisti nuotolinius prisijungimus prie šio Jellyfin serverio.", - "AnyLanguage": "Bet kokia kalba", + "AnyLanguage": "Bet Kokia Kalba", "Artists": "Atlikėjai", "Audio": "Garsas", "Auto": "Auto", @@ -616,7 +616,7 @@ "AllowMediaConversionHelp": "Leisti arba uždrausti medijos konvertavimą.", "AlwaysPlaySubtitles": "Visada rodyti subtitrus", "AutoBasedOnLanguageSetting": "Auto (pagal kalbos parinktį)", - "BookLibraryHelp": "Garso ir tekstinės knygos yra palaikomos. Peržiūrėkite {0}knygų vardinimo gidą{1}.", + "BookLibraryHelp": "Garso ir tekstinės knygos yra palaikomos. Peržiūrėkite {0} knygų vardinimo gidą {1}.", "ButtonEditOtherUserPreferences": "Keisti šio vartotojo profilį, paveikslą ir asmeninius nustatymus.", "ButtonResetEasyPassword": "Atstatyti pin kodą", "ButtonShuffle": "Sumaišyti", @@ -630,7 +630,7 @@ "Disconnect": "Atsijungti", "DisplayInMyMedia": "Rodyti pradiniame ekrane", "DisplayMissingEpisodesWithinSeasons": "Rodyti sezonuose trūkstamas serijas", - "DisplayModeHelp": "Pasirinkite ekrano tipą, kuriame veikia Jellyfin.", + "DisplayModeHelp": "Pasirinkite sąsajos išdėstymo stilių.", "Down": "Žemyn", "DownloadsValue": "{0} atsisiuntimų", "DrmChannelsNotImported": "Kanalai su DRM nebus įkeliami.", @@ -641,9 +641,9 @@ "EnablePhotos": "Rodyti nuotraukas", "EnablePhotosHelp": "Nuotraukos bus rodomos šalia kitų medijos failų.", "EnableThemeSongs": "Teminės dainos", - "AspectRatio": "Vaizdo santykis", + "AspectRatio": "Vaizdo Santykis", "Ascending": "Didėjančia tvarka", - "AllComplexFormats": "Visi sudėtingi formatai (ASS, SSA, VOBSUB, PGS, SUB/IDX, etc.)", + "AllComplexFormats": "Visi Sudėtingi Formatai (ASS, SSA, VOBSUB, PGS, SUB/IDX, t.t.)", "AllowHWTranscodingHelp": "Leisti imtuvui perkoduoti srautus grojant. Tai gali sumažinti perkodavimus reikalingus serveriui.", "AuthProviderHelp": "Pasirinkite autentifikavimo paslaugos teikėją šio vartotojo slaptažodžio autentifikavimui.", "AddItemToCollectionHelp": "Pridėkite įrašus į kolekciją. Suraskite įrašą, bei naudokite jo meniu, kad pridėti į kolekciją.", @@ -688,8 +688,8 @@ "HeaderDirectPlayProfileHelp": "Pridėti tiesioginio leidimo profilius, kad nurodyti kokius formatus įrenginys palaiko be perkodavimo.", "CopyStreamURLSuccess": "Srauto nuoroda nukopijuota.", "DefaultMetadataLangaugeDescription": "Tai yra numatytieji nustatymai. Jie gali būti keičiami kiekvienai bibliotekai atskirai.", - "AllowOnTheFlySubtitleExtractionHelp": "Įterptus subtitrus iš vaizdo įrašo galima išgauti ir klientams pateikti paprastu tekstu, kad būtų išvengta vaizdo įrašų perkodavimo. Kai kuriose sistemose tai gali užtrukti ilgą laiką ir gali sustabdyti vaizdo atkūrimą subtitrų išgavimo metu. Išjunkite tai, kad subtitrus būtu įrašomi į vaizdo įrašą naudojant perkodavimą, jei jie yra nepalaikomi kliento įrenginio.", - "BurnSubtitlesHelp": "Nustato, ar konvertuojant vaizdo įrašą serveris turėtų įrašyti subtitrus, atsižvelgiant į subtitrų formatą. Subtitrų įrašymo išvengimas pagerina serverio našumą. Pasirinkite „Auto“, jei norite įrašyti atvaizdais paremtus formatus (VOBSUB, PGS, SUB / IDX ir kt.) Ir tam tikrus ASS / SSA subtitrus.", + "AllowOnTheFlySubtitleExtractionHelp": "Įterptus subtitrus iš vaizdo įrašo galima išgauti ir klientams pateikti paprastu tekstu, kad būtų išvengta vaizdo įrašų perkodavimo. Kai kuriose sistemose tai gali užtrukti ilgą laiką ir gali sustabdyti vaizdo atkūrimą subtitrų išgavimo metu. Išjunkite tai, kad subtitrus būtu įrašomi į vaizdo įrašą naudojant perkodavimą, jei jie yra nepalaikomi kliento įrenginio.", + "BurnSubtitlesHelp": "Nustato, ar perkoduojant vaizdo įrašą serveris turėtų įrašyti subtitrus, atsižvelgiant į subtitrų formatą. Išvengiant subtitrų įrašymo gali pagerinti serverio našumą. Pasirinkite „Auto“, jei norite įrašyti atvaizdais paremtus formatus (VOBSUB, PGS, SUB, IDX, ...) Ir tam tikrus ASS arba SSA subtitrus.", "DefaultSubtitlesHelp": "Subtitrai įkeliami atsižvelgiant į numatytuosius ir priverstinius žymenis įterptuose metaduomenyse. Kalbos nustatymai įvertinami, kai yra keletas variantų.", "HeaderDeleteProvider": "Ištrinti paslaugos teikėją", "HeaderDeleteTaskTrigger": "Ištrinti užduoties trigerį", @@ -812,7 +812,7 @@ "HeaderTranscodingProfileHelp": "Pridėti perkodavimo profilius, kad nurodyti, kokius formatus reikia naudoti, kai reikia perkoduoti.", "HeaderTunerDevices": "Tiunerio prietaisai", "HeaderTuners": "Tiuneris", - "HeaderTypeImageFetchers": "{0} atvaizdų parsiuntėjai", + "HeaderTypeImageFetchers": "{0} atvaizdų persiuntėjai", "HeaderTypeText": "Įvesti tekstą", "HeaderUpcomingOnTV": "Laukiama per TV", "HeaderUploadImage": "Įkelti atvaizdą", @@ -927,7 +927,7 @@ "Guide": "Gidas", "GuideProviderLogin": "Prisijungti", "HandledByProxy": "Valdomas atvirkštiniu \"proxy\" serveriu", - "HardwareAccelerationWarning": "Įjungus aparatinės įrangos spartinimą, kai kuriose diegimo aplinkose gali atsirasti nestabilumas. Įsitikinkite, kad jūsų operacinė sistema ir vaizdo tvarkyklės yra visiškai atnaujintos. Jei įjungus šį vaizdo įrašą kyla problemų, turite pakeisti nustatymą į Automatinis.", + "HardwareAccelerationWarning": "Įjungus aparatinės įrangos spartinimą, kai kuriose diegimo aplinkose gali atsirasti nestabilumas. Įsitikinkite, kad jūsų operacinė sistema ir vaizdo tvarkyklės yra visiškai atnaujintos. Jei įjungus šį vaizdo įrašą kyla problemų, turite pakeisti nustatymą į \"Joks\".", "HeaderAdmin": "Administratorius", "HeaderAlbums": "Albumai", "HeaderAlert": "Perspėjimas", @@ -1003,5 +1003,17 @@ "HeaderSelectMetadataPath": "Metaduomenų kelio išrinkimas", "HeaderSelectMetadataPathHelp": "Suraskite arba įrašykite kelią metaduomenų saugojimui. Aplankalas turi būti su rašymo teise.", "HeaderSelectPath": "Išrinkti kelią", - "HeaderSelectServer": "Išrinkti serverį" + "HeaderSelectServer": "Išrinkti serverį", + "LabelCorruptedFrames": "Sugadinti kadrai:", + "HeaderNavigation": "Navigacija", + "HeaderFavoritePlaylists": "Mėgstami Grojaraščiai", + "ApiKeysCaption": "Įjungtų API raktų sąrašas", + "Episode": "Episodas", + "CopyStreamURLError": "Klaida kopijuojant URL.", + "ClientSettings": "Kliento Nustatymai", + "ButtonTogglePlaylist": "Grojaraštis", + "ButtonToggleContextMenu": "Daugiau", + "ButtonSplit": "Skirstyti", + "AskAdminToCreateLibrary": "Prašyti administratoriaus, kad sukurtų mediateka.", + "Album": "Albumas" } diff --git a/src/strings/lv.json b/src/strings/lv.json index 8c19b23ad7..cfdb0c0c06 100644 --- a/src/strings/lv.json +++ b/src/strings/lv.json @@ -16,7 +16,7 @@ "MinutesAfter": "minūtes pēc", "MetadataManager": "Metadatu Pārvaldnieks", "Metadata": "Metadati", - "MessageYouHaveVersionInstalled": "", + "MessageYouHaveVersionInstalled": "Jums pašlaik ir uzstādīta versija {0}.", "MessageUnableToConnectToServer": "Mēs pašlaik nevaram sazināties ar izvēlēto serveri. Pārliecinies ka tas strādā, un mēģini vēlreiz.", "MessageTheFollowingLocationWillBeRemovedFromLibrary": "Sekojošie multvides ceļi tiks noņemti no tavas bibliotēkas:", "MessageSettingsSaved": "Iestatījumi saglabāti.", @@ -239,7 +239,7 @@ "Kids": "Bērni", "Items": "Vienumi", "ItemCount": "{0} vienumi", - "InstallingPackage": "Instalē {0}", + "InstallingPackage": "Instalē {0} (versija {1})", "Images": "Attēli", "Identify": "Identificēt", "Horizontal": "Horizontāls", @@ -455,7 +455,7 @@ "DrmChannelsNotImported": "Kanāli ar DRM netiks importēti.", "DownloadsValue": "{0} lejupielādes", "Download": "Lejupielādēt", - "DisplayModeHelp": "Izvēlies ekrāna veidu, uz kura tu izmanto Jellyfin.", + "DisplayModeHelp": "Izvēlies izkārtojuma veidu, kuru tu gribi priekš šī interfeisa.", "DoNotRecord": "Neierakstīt", "DisplayInOtherHomeScreenSections": "Rādīt mājas ekrāna sadaļās kā jaunākā multvide un turpini skatīties", "DisplayInMyMedia": "Rādīt mājas ekrānā", @@ -605,8 +605,8 @@ "Art": "Māksla", "AroundTime": "Ap {0}", "Anytime": "Jebkad", - "AnyLanguage": "Jebkura valoda", - "AlwaysPlaySubtitles": "Vienmēr rādīt subtitrus", + "AnyLanguage": "Jebkura Valoda", + "AlwaysPlaySubtitles": "Vienmēr Rādīt", "AllowedRemoteAddressesHelp": "Ar komatiem atdalīts IP adrešu vai IP/tīkla masku saraksts, kas norāda uz tīkliem, kas var pieslēgties attālināti. Ja atstāts tukšs, visas attālinātās adreses tiks atļautas.", "AllowRemoteAccessHelp": "Ja atķeksēts, visi attālinātie savienojumi tiks bloķēti.", "AllowRemoteAccess": "Atļaut attālinātus savienojumus šim Jellyfin Serverim.", @@ -615,7 +615,7 @@ "AllLibraries": "Visas bibliotēkas", "AllLanguages": "Visas valodas", "AllEpisodes": "Visas epizodes", - "AllComplexFormats": "Visi sarezģītie formāti (ASS, SSA, VOBSUB, PGS, SUB/IDX, utt.)", + "AllComplexFormats": "Visi Sarežģītie formāti (ASS, SSA, VOBSUB, PGS, SUB, IDX, …)", "AllChannels": "Visi kanāli", "All": "Viss", "Alerts": "Paziņojumi", @@ -951,8 +951,8 @@ "HeaderCustomDlnaProfiles": "Pielāgoti Profili", "HeaderConfirmProfileDeletion": "Apstiprināt Profila Dzēšanu", "HeaderChapterImages": "Nodaļu Attēli", - "HeaderCastCrew": "Lomas/Apkalpe", - "HeaderCastAndCrew": "Lomas/Apkalpe", + "HeaderCastCrew": "Lomas & Apkalpe", + "HeaderCastAndCrew": "Lomas un Apkalpe", "HeaderAppearsOn": "Redzams", "FFmpegSavePathNotFound": "Mēs nespējām atrast FFmpeg norādītajā ceļā. FFprobe arī ir vajadzīgs, un tam ir jāatrodas tajā pašā mapē. Šīs komponentes parasti tiek apvienotas vienā un tajā pašā lejupielādē. Lūdzu pārbaudiet ceļu un mēģiniet vēlreiz.", "HeaderAdditionalParts": "Papildus Ceļi", @@ -1001,7 +1001,7 @@ "ButtonArrowLeft": "Kreisi", "ButtonArrowDown": "Lejup", "ButtonAddScheduledTaskTrigger": "Pievienot Trigeru", - "BookLibraryHelp": "Audio un teksta grāmatas tiek atbalstītas. Pārskati {0}grāmatu nosaukumu instrukciju{1}.", + "BookLibraryHelp": "Audio un teksta grāmatas tiek atbalstītas. Pārskati {0} grāmatu nosaukumu instrukciju {1}.", "Blacklist": "Melnais saraksts", "AuthProviderHelp": "Izvēlies Autentifikācijas Nodrošinājumu, kas tiks izmantots lai autentificētu šī lietotāja paroli.", "AspectRatio": "Attēla Proporcijas", @@ -1081,5 +1081,156 @@ "OptionAllowBrowsingLiveTv": "Atļaut Tiešraides TV piekļuvi", "MediaInfoForced": "Piespiests", "LabelPublicHttpPortHelp": "Publiskai porta numurs, kas tiks kartēts uz vietējo HTTP portu.", - "LabelOptionalNetworkPath": "(Neobligāts) Koplietota tīkla mape:" + "LabelOptionalNetworkPath": "(Neobligāts) Koplietota tīkla mape:", + "LabelLibraryPageSizeHelp": "Uzstāda vienumu daudzumu, ko rādīt bibliotēkas lapā. Uzstādi uz 0 lai atspējotu lapošanu.", + "LabelLibraryPageSize": "Bibliotēkas lapu lielums:", + "LabelKodiMetadataUserHelp": "Saglabāt skatīšanās datus uz NFO datnēm, lai tos varētu izmantot citas lietotnes.", + "LabelKodiMetadataDateFormatHelp": "Visi datumi iekš NFO datnēm tiks parsēti ar šo formātu.", + "LabelInNetworkSignInWithEasyPasswordHelp": "Izmanto vieglo pin kodu lai piekļūtu klientiem savā vietējā tīklā. Tava parastā parole būs vajadzīga ārpus mājām. Ja pin kods ir atstāts tukšs, tev nevajadzēs paroli savā vietējā mājas tīklā.", + "LabelInNetworkSignInWithEasyPassword": "Iespējot iekštīkla piekļuvi ar manu vieglo pin kodu", + "LabelImportOnlyFavoriteChannels": "Ierobežot uz kanāliem, kas atzīmēti kā favorīti", + "LabelGroupMoviesIntoCollectionsHelp": "Kad tiks rādīti filmu saraksti, filmas kas pieder pie kolekcijas tiks attēlotas kā viens grupēts vienums.", + "LabelServerNameHelp": "Šis vārds tiks izmantots lai identificētu tavu serveri un noklusējumā ir servera datora vārds.", + "LabelExtractChaptersDuringLibraryScan": "Izvilkt sadaļu attēlus bibliotēkas skenēšanas laikā", + "LabelBaseUrlHelp": "Šeit tu vari pievienot pielāgotas apakš-mapes lai piekļūtu serverim no unikālāka URL.", + "LabelBaseUrl": "Pamata URL:", + "LabelEnableSingleImageInDidlLimitHelp": "Dažas ierīces pareizi neatskaņos ja vairāki attēli ir iegulti iekš Didl.", + "LabelEnableSingleImageInDidlLimit": "Ierobežot uz vienu iegulto attēlu", + "LabelEnableDlnaClientDiscoveryIntervalHelp": "Nosaka laiku sekundēs starp Jellyfin veiktajiem SSDP meklējumiem.", + "LabelEmbedAlbumArtDidlHelp": "Dažas ierīces labprātāk izmanto šo metodi lai saņemtu albumu vākus. Citas var neatskaņot ar šo opciju ieslēgtu.", + "LabelDroppedFrames": "Nomestie kadri:", + "LabelDownMixAudioScaleHelp": "Pastiprināt audio lejupmiksēšanas laikā. Vērtība viens paturēs oriģinālo skaļumu.", + "LabelDownMixAudioScale": "Audio pastiprinājums lejupmiksējot:", + "LabelDisplayMissingEpisodesWithinSeasons": "Rādīt trūkstošās epizodes sezonās", + "LabelDateAddedBehaviorHelp": "Ja atrodas metadatu vērtība, tā vienmēr tiks izmantota pirms jebkuras no šīm opcijām.", + "LabelDashboardTheme": "Servera vadības paneļa tēma:", + "LabelCustomDeviceDisplayNameHelp": "Ievadi pielāgotu displeja vārdu vai atstāj tukšu lai izmantotu ierīces noteikto.", + "LabelCachePathHelp": "Nosaki pielāgotu atrašanās vietu priekš keša datnēm kā attēliem. Atstāj tukšu lai izmantotu servera noklusējumu.", + "LabelAllowedRemoteAddressesMode": "Attālās IP adreses filtra režīms:", + "LabelAllowedRemoteAddresses": "Attālās IP adreses filtrs:", + "LabelAlbumArtPN": "Albumu vāku PN:", + "LabelAirsBeforeSeason": "Tiešraidē pirms sezonas:", + "LabelAirsBeforeEpisode": "Tiešraidē pirms epizodes:", + "LabelAirsAfterSeason": "Tiešraidē pēc sezonas:", + "HeaderSubtitleProfilesHelp": "Subtitru profili apraksta ierīces atbalstītos subtitru formātus.", + "HeaderPendingInvitations": "Gaidošie Uzaicinājumi", + "HeaderKeepSeries": "Paturēt Sēriju", + "HeaderFavoritePlaylists": "Atskaņošanas Sarakstu Favorīti", + "ApiKeysCaption": "Saraksts ar pašlaik iespējotajām API atslēgām", + "EncoderPresetHelp": "Izvēlies ātrāku vērtību lai uzlabotu veiktspēju, vai lēnāku vērtību lai uzlabotu kvalitāti.", + "FetchingData": "Iegūst papildu datus", + "ErrorDeletingItem": "Notika kļūda dzēšot vienumu no Jellyfin Servera. Lūdzu pārliecinies vai Jellyfin Server ir rakstoša piekļuve pie satura mapes un mēģini vēlreiz.", + "ErrorAddingTunerDevice": "Kļūda pievienojot tūnera ierīci. Lūdzu pārliecinies ka tā ir pieejama un mēģini vēlreiz.", + "ErrorAddingMediaPathToVirtualFolder": "Notika kļūda pievienojot satura ceļu. Lūdzu pārliecinies ka ceļš ir derīgs un ka Jellyfin Servera procesam ir piekļuve tai vietai.", + "Episode": "Epizode", + "DeviceAccessHelp": "Tas attiecas tikai uz ierīcēm, kas var tikt unikāli identificētas un neaizliegs piekļuvi no pārlūka. Filtrējot lietotāju ierīču piekļuvi neatļaus tiem izmantot jaunas ierīces, līdz tās nav tikušas šeit atļautas.", + "DeinterlaceMethodHelp": "Izvēlies rindpārlēces sakļaušanas (deinterlacing) metodi kad tiek trans-kodēts rindpārlēces izvērsts (interlaced) saturs.", + "CustomDlnaProfilesHelp": "Izveido pielāgotu profilu priekš jaunas ierīces, vai pārraksti sistēmas profilu.", + "ColorTransfer": "Krāsu pārsūtīšana", + "ClientSettings": "Klientu Iestatījumi", + "ButtonTogglePlaylist": "Atskaņošanas Saraksts", + "ButtonToggleContextMenu": "Vairāk", + "BurnSubtitlesHelp": "Nosaka, vai serverim ir jāiededzina subtitri video trans-kodēšanas laikā. To nedarot tiks stipri palielināta veiktspēja. Izvēlies Auto lai iededzinātu uz attēliem bāzētus formātus (VOBSUB, PGS, SUB, IDX, …) un noteiktus ASS vai SSA subtitrus.", + "Artist": "Izpildītājs", + "AllowOnTheFlySubtitleExtractionHelp": "Iegultie subtitri var tikt izvilkto no video un nogādāti klientiem kā parasts teksts, lai nevajadzētu veikt lieku video trans kodēšanu. Uz dažām sistēmām tas var aizņemt ilgu laiku un likt video atskaņošanai uzkārties izvilkšanas procesa laikā. Atspējo šo lai iegultos subtitrus iededzinātu video trans kodēšanas veidā, kad tos noklusēti neatbalsta klienta ierīce.", + "AlbumArtist": "Albuma Izpildītājs", + "Album": "Albums", + "PleaseRestartServerName": "Lūdzu atsāknē savu Jellyfin Serveri - {0}.", + "PlayNextEpisodeAutomatically": "Atskaņot nākamo epizodi automātiski", + "PlayFromBeginning": "Atskaņot no sākuma", + "PlayCount": "Atskaņošanas reizes", + "PlaybackData": "Atskaņošanas Dati", + "Person": "Persona", + "PerfectMatch": "Ideāla saderība", + "PasswordResetHeader": "Atiestatīt Paroli", + "PasswordResetConfirmation": "Vai tu tiešām gribi atiestatīt paroli?", + "PasswordResetComplete": "Parole ir atiestatīta.", + "PasswordMatchError": "Parolei un paroles pārbaudei ir jāsakrīt.", + "ParentalRating": "Vecāku vērtējums", + "PackageInstallFailed": "{0} (versija {1}) instalācija neizdevās.", + "PackageInstallCompleted": "{0} (versija {1}) instalācija pabeigta.", + "PackageInstallCancelled": "{0} (versija {1}) instalācija atcelta.", + "Overview": "Pārskats", + "OtherArtist": "Cits izpildītājs", + "OriginalAirDateValue": "Oriģinālais tiešraides datums: {0}", + "OptionWeekly": "Iknedēļu", + "OptionWeekends": "Nedēļas nogalēs", + "OptionWeekdays": "Darba dienās", + "OptionWednesday": "Trešdiena", + "OptionWakeFromSleep": "Modināt no miega", + "OptionUnplayed": "Neatskaņots", + "OptionTuesday": "Otrdiena", + "OptionTrackName": "Celiņa Nosaukums", + "OptionThumb": "Sīktēls", + "OptionThursday": "Ceturtdiena", + "OptionSunday": "Svētdiena", + "OptionSaveMetadataAsHidden": "Saglabāt metadatus un attēlus kā paslēptas datnes", + "OptionSaturday": "Sestdiena", + "OptionRequirePerfectSubtitleMatch": "Lejupielādēt tikai subtitrus, kas perfekti sakrīt ar manām video datnēm", + "OptionRegex": "Regex", + "OptionProtocolHls": "HTTP Tiešraides Straumes", + "OptionProfilePhoto": "Attēls", + "OptionPremiereDate": "Pirmizrādes Datums", + "OptionPoster": "Plakāts", + "OptionPlayCount": "Atskaņošanas Skaits", + "OptionOnAppStartup": "Lietotnes sāknēšanā", + "OptionNameSort": "Nosaukums", + "OptionMissingEpisode": "Trūkstošās Epizodes", + "OptionMax": "Maksimums", + "OptionLoginAttemptsBeforeLockoutHelp": "Vērtība nulle nozīmē noklusējuma trīs mēģinājumu priekš lietotājiem un piecu priekš administratoriem izmantošanu. Uzstādot uz -1 atspējos funkciju.", + "OptionLoginAttemptsBeforeLockout": "Nosaka, cik daudz nepareizi piekļuves mēģinājumi var notikt pirms notiek bloķēšana.", + "OptionImdbRating": "IMDb Vērtējums", + "OptionIgnoreTranscodeByteRangeRequestsHelp": "Ja iespējots, šie pieprasījumi tiks veikti bet ignorēs baitu diapazona hederu.", + "OptionIgnoreTranscodeByteRangeRequests": "Ignorēt trans-kodēšanas baitu diapazona pieprasījumus", + "OptionHomeVideos": "Attēli", + "OptionHlsSegmentedSubtitles": "HLS segmentēti subtitri", + "OptionHideUserFromLoginHelp": "Noderīgs lai paslēptu privātus vai paslēptus administratora kontus. Šim lietotājam būs jāieiet manuāli, ievadot savu lietotājvārdu un paroli.", + "OptionHideUser": "Paslēpt šo lietotāju no piekļuves ekrāniem", + "OptionHasThemeVideo": "Tēmu Video", + "OptionExternallyDownloaded": "Ārējā lejupielāde", + "OptionEstimateContentLength": "Novērtēt satura ilgumu trans-kodēšanas laikā", + "OptionEquals": "Vienāds ar", + "OptionEnableM2tsModeHelp": "Iespējo m2ts režīmu iekodējot uz mpegts.", + "OptionEnableM2tsMode": "Iespējot M2ts režīmu", + "OptionEnableForAllTuners": "Iespējot visām tūnera ierīcēm", + "OptionEnableExternalContentInSuggestionsHelp": "Atļaut interneta treilerus un tiešraides TV programmām tikt iekļautām ieteiktajā saturā.", + "OptionEnableExternalContentInSuggestions": "Iespējot ārējo saturu iekš ieteikumiem", + "OptionEnableAccessToAllChannels": "Iespējot piekļuvi visiem kanāliem", + "OptionEnableAccessFromAllDevices": "Iespējot piekļuvi no visām ierīcēm", + "OptionEmbedSubtitles": "Ievietot iekš konteinera", + "OptionDownloadThumbImage": "Sīktēls", + "OptionDownloadPrimaryImage": "Galvenais", + "OptionDownloadImagesInAdvanceHelp": "Noklusējumā, lielākā daļa attēlu tiek lejupielādēti kad tie ir pieprasīti Jellyfin lietotnē. Iespējo šo opciju lai iepriekšēji lejupielādētu visus attēlus, kad jauns saturs tiek importēts. Tas var radīt ievērojami ilgākus bibliotēkas skenējumus.", + "OptionDownloadImagesInAdvance": "Lejupielādēt attēlus iepriekš", + "OptionDownloadBackImage": "Aizmugure", + "OptionDownloadArtImage": "Māksla", + "OptionDisableUserHelp": "Ja atspējots, serveris neatļaus nekādus savienojumus no šī lietotāja. Esošie savienojumi tiks pēkšņi apturēti.", + "OptionDisableUser": "Atspējot šo lietotāju", + "OptionDatePlayed": "Atskaņošanas Datums", + "OptionDateAddedImportTime": "Izmantot datumu, kad tika ieskanēts bibliotēkā", + "OptionDateAddedFileTime": "Izmantot datņu izveides datumu", + "OptionDateAdded": "Pievienošanas Datums", + "OptionCriticRating": "Kritiķu Vēŗtējums", + "OptionCommunityRating": "Kopienas Vērtējums", + "OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)", + "OptionBlockLiveTvChannels": "Tiešraides TV Kanāli", + "OptionAllowVideoPlaybackTranscoding": "Atļaut video atskaņošanu, kas prasa trans-kodēšanu", + "OptionAllowVideoPlaybackRemuxing": "Atļaut video atskaņošanu, kas prasa pārveidošanu bez pārkodēšanas", + "OptionAllowUserToManageServer": "Atļaut šim lietotājam pārvaldīt serveri", + "OptionAllowSyncTranscoding": "Atļaut satura lejupielādi un sinhronizēšanu, kas prasa trans-kodēšanu", + "OptionAllowRemoteSharedDevicesHelp": "DLNA ierīces tiek uzskatītas kā koplietotas līdz lietotājs sāk tās vadīt.", + "OptionAllowRemoteSharedDevices": "Atļaut koplietotu ierīču attālinātu vadību", + "OptionAllowMediaPlayback": "Atļaut satura atskaņošanu", + "OptionAllowContentDownloading": "Atļaut satura lejupielādi un sinhronizāciju", + "OptionForceRemoteSourceTranscoding": "Piespiest trans-kodēšanu no attāliem satura avotiem (kā Tiešraides TV)", + "OptionAllowAudioPlaybackTranscoding": "Atļaut audio atskaņošanu, kas prasa trans-kodēšanu", + "OnlyForcedSubtitlesHelp": "Tikai subtitri, kas atzīmēti kā piespiedu tiks ielādēti.", + "OnlyForcedSubtitles": "Tikai Piespiedu", + "MoreUsersCanBeAddedLater": "Papildus lietotāji var tikt pievienoti vēlāk no vadības paneļa.", + "MessagePluginConfigurationRequiresLocalAccess": "Lai konfigurētu šo paplašinājumu lūdzu tieši ieej savā lokālajā serverī.", + "MessagePleaseEnsureInternetMetadata": "Lūdzu pārliecinies vai metadatu lejupielāde no interneta ir iespējota.", + "MessageUnauthorizedUser": "Jūs neesat autorizēti lai piekļūtu serverim šajā brīdī. Lūdzu sazinieties ar savu servera administratoru priekš papildus informācijas.", + "MessageInstallPluginFromApp": "Šis paplašinājums ir jāuzstāda no lietotnes, kurā jūs to vēlaties izmantot.", + "LabelEmbedAlbumArtDidl": "Ievietot albumu vākus iekš Didl", + "LabelSelectFolderGroups": "Automātiski grupēt saturu no sekojošām datnēm skatos kā Filmas, Mūzika un TV:" } diff --git a/src/strings/mr.json b/src/strings/mr.json new file mode 100644 index 0000000000..381c609a9e --- /dev/null +++ b/src/strings/mr.json @@ -0,0 +1,101 @@ +{ + "ButtonOpen": "उघडा", + "ButtonOk": "ऑन", + "ButtonOff": "ऑफ", + "ButtonNextTrack": "पुढचा ट्रॅक", + "ButtonNew": "नवीन", + "ButtonNetwork": "नेटवर्क", + "ButtonMore": "अजून", + "ButtonLearnMore": "अधिक माहिती", + "ButtonInfo": "माहिती", + "ButtonHelp": "मदत", + "ButtonGuide": "गाईड", + "ButtonGotIt": "समजले", + "ButtonForgotPassword": "पासवर्ड विसरलो", + "ButtonEditImages": "चित्र संपादित करा", + "ButtonEdit": "संपादित करा", + "ButtonDownload": "डाउनलोड करा", + "ButtonDown": "खाली", + "ButtonDeleteImage": "चित्र काढून टाका", + "ButtonDelete": "काढून टाका", + "ButtonChangeServer": "सर्व्हर बदला", + "ButtonCancel": "रद्द करा", + "ButtonBack": "मागे", + "ButtonAudioTracks": "ऑडिओ ट्रॅक", + "ButtonArrowUp": "वर", + "ButtonArrowRight": "उजवीकडे", + "ButtonArrowLeft": "डावीकडे", + "ButtonArrowDown": "खाली", + "ButtonAddUser": "प्रयोक्ता जोडा", + "ButtonAddServer": "सर्व्हर जोडा", + "ButtonAdd": "जोडा", + "Books": "पुस्तकं", + "Blacklist": "ब्लॅकलिस्ट", + "BirthPlaceValue": "जन्म ठिकाण: {0}", + "BirthLocation": "जन्मस्थान", + "BirthDateValue": "जन्म: {0}", + "Backdrops": "पार्श्वभूमी", + "Backdrop": "पार्श्वभूमी", + "Auto": "आपोआप", + "Audio": "ऑडिओ", + "AttributeNew": "नवीन", + "AspectRatio": "अ‍ॅस्पेक्ट रेशो", + "AsManyAsPossible": "जमतील तितके", + "Artists": "संगीतकार", + "Artist": "संगीतकार", + "Anytime": "कधीही", + "AnyLanguage": "कोणतीही भाषा", + "AlwaysPlaySubtitles": "नेहमीच प्ले करा", + "AllLibraries": "सर्व संग्रहालय", + "AllLanguages": "सर्व भाषा", + "AllEpisodes": "सर्व भाग", + "AllChannels": "सर्व वाहिन्या", + "All": "सर्व", + "Albums": "अल्बम", + "AlbumArtist": "अल्बम संगीतकार", + "Album": "अल्बम", + "AddedOnValue": "{0} जोडले", + "Add": "जोडा", + "Actor": "अभिनेता", + "EnableBackdrops": "पार्श्वभूमी", + "EditSubtitles": "सबटायटल संपादित करा", + "EditMetadata": "मेटाडेटा संपादित करा", + "EditImages": "चित्र संपादित करा", + "Edit": "संपादित करा", + "DrmChannelsNotImported": "डी.आर.एम. असलेल्या वाहिन्या आयात केल्या जाणार नाहीत.", + "DownloadsValue": "{0} डाउनलोड", + "Download": "डाउनलोड", + "Down": "खाली", + "DoNotRecord": "रेकॉर्ड करू नका", + "Directors": "दिग्दर्शक", + "Director": "दिग्दर्शक", + "Desktop": "डेस्कटॉप", + "DeleteImageConfirmation": "तुम्हाला नक्की हे चित्र काढून टाकायचे आहे का?", + "Delete": "काढून टाका", + "DeleteImage": "चित्र काढून टाका", + "ConfirmEndPlayerSession": "{0} येथील जेलिफिन बंद करावे का?", + "Composer": "संगीत दिग्दर्शक", + "Channels": "वाहिन्या", + "ChannelNumber": "वाहिनी क्रमांक", + "Categories": "वर्ग", + "CancelRecording": "रेकॉर्डिंग रद्द करा", + "ButtonWebsite": "संकेतस्थळ", + "ButtonViewWebsite": "संकेतस्थळ पाहा", + "ButtonUp": "वर", + "ButtonTrailer": "ट्रेलर", + "ButtonSubtitles": "सबटायटल", + "ButtonStop": "थांबा", + "ButtonStart": "सुरू करा", + "ButtonSettings": "सेटिंग्झ", + "ButtonSend": "पाठवा", + "ButtonSelectView": "दृष्य निवडा", + "ButtonSelectServer": "सर्व्हर निवडा", + "ButtonSelectDirectory": "डिरेक्टरी निवडा", + "ButtonSearch": "शोधा", + "ButtonScanAllLibraries": "सर्व संग्रहालय स्कॅन करा", + "ButtonRename": "नाव बदला", + "ButtonRemove": "काढून टाका", + "ButtonPreviousTrack": "मागचा ट्रॅक", + "ButtonPlay": "प्ले", + "ButtonPause": "पॉझ" +} diff --git a/src/strings/nb.json b/src/strings/nb.json index 3d863d17e0..3821c5544d 100644 --- a/src/strings/nb.json +++ b/src/strings/nb.json @@ -382,7 +382,7 @@ "Images": "Bilder", "ImportFavoriteChannelsHelp": "Hvis aktivert, vil kun kanaler som er markert som favoritt på tuneren bli importert.", "ImportMissingEpisodesHelp": "Hvis aktivert, vil informasjon om manglende episoder importeres til Jellyfin-databasen og de vil vises under sesonger og serier. Dette kan føre til at skanning av biblioteket tar betydelig lengre tid.", - "InstallingPackage": "Installerer {0}", + "InstallingPackage": "Installerer {0} (versjon {1})", "InstantMix": "Direktemiks", "ItemCount": "{0} elementer", "Items": "Elementer", @@ -1129,9 +1129,9 @@ "AllowOnTheFlySubtitleExtractionHelp": "Integrerte undertekster kan hentes ut fra videoer og bli levert til klienter i klartekst for å unngå omkoding av video. På noen systemer kan dette ta lang tid og føre til opphold i avspillingen samtidig som prosessen pågår. Deaktiver innstillingen for å brenne inn underteksten i videoen ved hjelp av omkoding når undertekstformatet ikke er støttet av klienten.", "AllowOnTheFlySubtitleExtraction": "Tillat at undertekster hentes ut fortløpende", "AllLanguages": "Alle språk", - "AllComplexFormats": "Alle avanserte formater (ASS, SSA, VOBSUB, PGS, SUB/IDX, etc.)", + "AllComplexFormats": "Alle avanserte formater (ASS, SSA, VOBSUB, PGS, SUB, IDX)", "AccessRestrictedTryAgainLater": "Tilgang er for øyeblikket begrenset. Vennligst prøv igjen senere.", - "BurnSubtitlesHelp": "Angir om serveren skal brenne inn teksting når videoer konverteres, basert på tekstformatet. Ytelsen på serveren vil forbedres dersom tekstingen ikke brennes inn. Velg Automatisk for å brenne inn bildebaserte formater (VOBSUB, PGS, SUB/IDX, osv.) og enkelte ASS/SSA-undertekster.", + "BurnSubtitlesHelp": "Angir om serveren skal brenne inn teksting når videoer konverteres, basert på tekstformatet. Ytelsen på serveren vil forbedres dersom tekstingen ikke brennes inn. Velg Automatisk for å brenne inn bildebaserte formater (VOBSUB, PGS, SUB, IDX) og enkelte ASS eller SSA-undertekster.", "General": "Generelt", "ChangingMetadataImageSettingsNewContent": "Endringer gjort i innstillinger for metadata eller omslagsbilder vil kun gjelde nytt innhold i biblioteket ditt. For å endre eksisterende innhold, må du oppdatere dets metadata manuelt.", "DefaultSubtitlesHelp": "Undertekster lastes inn basert på flaggene \"standard\" og \"tvungen\" i videoens integrerte metadata. Språkpreferanser tas høyde for dersom flere valg er tilgjengelig.", @@ -1218,7 +1218,7 @@ "Unmute": "Skru på lyd", "OptionIsHD": "HD", "ButtonAddImage": "Legg til bilde", - "DisplayModeHelp": "Velg hvilken slags skjerm du bruker Jellyfin på.", + "DisplayModeHelp": "Velg hvilket brukergrensesnitt oppsett du vil ha.", "DownloadsValue": "{0} nedlastninger", "EnableNextVideoInfoOverlayHelp": "Vis informasjon om den neste videoen i spillelisten ved slutten av en video.", "ExtractChapterImagesHelp": "Uthenting av kapittelbilder vil gjøre det mulig for klienter å vise bilder i menyer for å velge kapitel. Denne prosessen kan være treg, ressurskrevende, og kan kreve flere gigabyte med lagringsplass. Prosessen kjører når videoer oppdages, samt som en daglig planlagt hendelse. Tidsplanen kan endres i innstillinger for planlagte hendelser. Det anbefales ikke at denne prosessen kjøres når det er mange aktive brukere innlogget.", @@ -1454,8 +1454,6 @@ "SelectAdminUsername": "Vennligst velg et brukernavn for administrator kontoen. ", "HeaderNavigation": "Navigering", "MessageConfirmAppExit": "Vil du avslutte?", - "EnableFastImageFadeInHelp": "Bruk rask inntoning av animasjon for lastede bilder", - "EnableFastImageFadeIn": "Rask bilde inntoning", "CopyStreamURLError": "Det var en feil under kopiering av URL'en.", "LabelVideoResolution": "Oppløsning på video:", "LabelPlayerDimensions": "Dimensjoner på avspiller:", @@ -1467,5 +1465,18 @@ "AskAdminToCreateLibrary": "Spør en administrator om å lage et bibliotek.", "PlaybackErrorNoCompatibleStream": "Det oppstod et problem med klientprofilering, og serveren sender ikke et kompatibelt medieformat.", "AllowFfmpegThrottlingHelp": "Når en omkoding eller ompakking kommer langt nok foran den nåværende avspillingsposisjonen stoppes prosessen slik at den bruker mindre ressurser. Dette er mest nyttig når du sjeldent bytter posisjon i videoen. Slå av dette hvis du opplever problemer med avspilling.", - "AllowFfmpegThrottling": "Begrens hastighet på omkoding" + "AllowFfmpegThrottling": "Begrens hastighet på omkoding", + "Episode": "Episode", + "ClientSettings": "Klientinstillinger", + "BoxSet": "Samleboks", + "Artist": "Artist", + "AlbumArtist": "Albumartist", + "Album": "Album", + "LabelLibraryPageSizeHelp": "Velger hvor mange elementer som skal bli vist på en bibliotek side. Velg 0 for å deaktivere.", + "LabelLibraryPageSize": "Biblioteks side størrelse:", + "LabelDeinterlaceMethod": "Deinterlacing metode:", + "HeaderFavoritePlaylists": "Favorittspillelister", + "DeinterlaceMethodHelp": "Velg deinterlacing metoden som skal bli brukt når man transkoder interlaced innhold.", + "ButtonTogglePlaylist": "Spilleliste", + "ButtonToggleContextMenu": "Mer" } diff --git a/src/strings/nl.json b/src/strings/nl.json index 2acfe83414..132fd77803 100644 --- a/src/strings/nl.json +++ b/src/strings/nl.json @@ -14,7 +14,7 @@ "Alerts": "Meldingen", "All": "Alle", "AllChannels": "Alle kanalen", - "AllComplexFormats": "Alle complexe formaten (ASS, SSA, VOBSUB, PGS, SUB/IDX, etc.)", + "AllComplexFormats": "Alle Complexe Formaten (ASS, SSA, VOBSUB, PGS, SUB, IDX, etc.)", "AllEpisodes": "Alle afleveringen", "AllLanguages": "Alle talen", "AllLibraries": "Alle bibliotheken", @@ -26,7 +26,7 @@ "AllowRemoteAccess": "Externe verbindingen met deze Jellyfin Server toestaan.", "AllowRemoteAccessHelp": "Indien niet aangevinkt worden alle externe verbindingen geblokkeerd.", "AllowedRemoteAddressesHelp": "Komma-gescheiden lijst van IP-adressen of IP/netmask adressen voor netwerken die op afstand verbinding mogen maken. Indien blanco, worden alle externe adressen toegestaan.", - "AlwaysPlaySubtitles": "Altijd ondertitels weergeven", + "AlwaysPlaySubtitles": "Altijd afspelen", "AlwaysPlaySubtitlesHelp": "Ondertitels die met de taalvoorkeur overeenkomen worden weergegeven, ongeacht de audiotaal.", "AnyLanguage": "Elke taal", "Anytime": "Op elk moment", @@ -49,7 +49,7 @@ "BoxRear": "Hoes (achterkant)", "Browse": "Bladeren", "BrowsePluginCatalogMessage": "Bekijk de Plugin catalogus voor beschikbare Plug-ins.", - "BurnSubtitlesHelp": "Bepaalt of de server ondertitels moet inbranden wanneer video's op basis van het soort ondertitels geconverteerd moeten worden. Het inbranden van ondertitels heeft een negatief effect op de server performance. Selecteer Automatisch om op afbeelding gebaseerde formaten (VOBSUB, PGS, SUB/IDX etc.) en bepaalde ASS/SSA ondertitels in te branden.", + "BurnSubtitlesHelp": "Bepaalt of de server ondertitels moet branden bij het transcoderen van video's. Als u dit vermijd, worden de prestaties aanzienlijk verbeterd. Selecteer Auto om op afbeeldingen gebaseerde formaten (VOBSUB, PGS, SUB/IDX etc.) en bepaalde ASS/SSA ondertitels te branden.", "ButtonAdd": "Toevoegen", "ButtonAddMediaLibrary": "Voeg Media Bibliotheek toe", "ButtonAddScheduledTaskTrigger": "Trigger Toevoegen", @@ -178,7 +178,7 @@ "DisplayInOtherHomeScreenSections": "In secties van het startscherm weergeven, zoals \"Recente Media\" en \"Verder Kijken\"", "DisplayMissingEpisodesWithinSeasons": "Toon ontbrekende afleveringen binnen een seizoen", "DisplayMissingEpisodesWithinSeasonsHelp": "Dit moet ook worden ingeschakeld voor TV bibliotheken in de server configuratie.", - "DisplayModeHelp": "Selecteer het scherm type waar u Jellyfin op draait", + "DisplayModeHelp": "Selecteer het schermtype waar Jellyfin op draait.", "DoNotRecord": "Niet opnemen", "Down": "Omlaag", "Download": "Downloaden", @@ -195,10 +195,10 @@ "EnableColorCodedBackgrounds": "Kleurgecodeerde achtergronden", "EnableDisplayMirroring": "Beeld spiegelen", "EnableExternalVideoPlayers": "Externe video spelers", - "EnableExternalVideoPlayersHelp": "Een menu voor externe spelers zal worden getoond bij het afspelen van video's", + "EnableExternalVideoPlayersHelp": "Een menu voor externe spelers wordt getoond bij het afspelen van video's.", "EnableHardwareEncoding": "Activeer hardwaredecodering", "EnableNextVideoInfoOverlay": "Toon informatie over de volgende video tijdens het afspelen", - "EnableNextVideoInfoOverlayHelp": "Toon informatie over de volgende video in de afspeellijst aan het einde van de video", + "EnableNextVideoInfoOverlayHelp": "Toon informatie over de volgende video in de afspeellijst aan het einde van de video.", "EnablePhotos": "Foto's weergeven", "EnablePhotosHelp": "Afbeeldingen worden herkend en weergegeven naast andere mediabestanden.", "EnableStreamLooping": "Livestreams automatisch herhalen", @@ -254,7 +254,7 @@ "EncoderPresetHelp": "Kies een hogere waarde om de prestaties, of een tragere waarde om de kwaliteit te verbeteren.", "HDPrograms": "HD Programma's", "HandledByProxy": "Behandeld door reverse proxy", - "HardwareAccelerationWarning": "Hardwareversnelling inschakelen kan instabiliteit veroorzaken in sommige omgevingen. Zorg ervoor dat uw besturingssysteem en videostuurprogramma's volledig up-to-date zijn. Als u problemen ondervindt bij het afspelen van video, nadat u dit hebt ingeschakeld, moet u de instelling terugzetten naar Auto.", + "HardwareAccelerationWarning": "Hardwareversnelling inschakelen kan instabiliteit veroorzaken in sommige omgevingen. Zorg ervoor dat uw besturingssysteem en videostuurprogramma's volledig up-to-date zijn. Als u problemen ondervindt bij het afspelen van video, nadat u dit hebt ingeschakeld, moet u de instelling terugzetten naar geen.", "HeaderAccessSchedule": "Schema Toegang", "HeaderAccessScheduleHelp": "Maak een toegangsschema om de toegang tot bepaalde tijden te beperken.", "HeaderActiveDevices": "Actieve apparaten", @@ -278,7 +278,7 @@ "HeaderAudioBooks": "Luisterboeken", "HeaderAudioSettings": "Audio Instellingen", "HeaderAutomaticUpdates": "Automatische updates", - "HeaderBlockItemsWithNoRating": "Blokkeer items met geen of niet herkende keurinformatie.", + "HeaderBlockItemsWithNoRating": "Blokkeer items met geen of niet herkende beoordelingsinformatie:", "HeaderBooks": "Boeken", "HeaderBranding": "Huisstijl", "HeaderCancelRecording": "Opname Annuleren", @@ -411,7 +411,7 @@ "HeaderSelectTranscodingPath": "Selecteer Tijdelijke Transcodeer Pad", "HeaderSelectTranscodingPathHelp": "Bladeren of voer het pad in om te gebruiken voor het transcoderen van tijdelijke bestanden. De map moet beschrijfbaar zijn.", "HeaderSendMessage": "Stuur bericht", - "HeaderSeries": "Series:", + "HeaderSeries": "Series", "HeaderSeriesOptions": "Series Opties", "HeaderSeriesStatus": "Seriestatus", "HeaderServerSettings": "Server Instellingen", @@ -461,9 +461,9 @@ "Images": "Afbeeldingen", "ImportFavoriteChannelsHelp": "Bij inschakelen zullen alleen kanalen geïmporteerd worden die op de tuner als favoriet aangemerkt zijn.", "ImportMissingEpisodesHelp": "Indien ingeschakeld, wordt informatie over ontbrekende afleveringen in uw Jellyfin de database geïmporteerd en weergegeven in de seizoenen en series. Dit kan aanzienlijk langere bibliotheekscans veroorzaken.", - "InstallingPackage": "Installeren van {0}", + "InstallingPackage": "Installeren van {0} (versie {1})", "Kids": "Kinderen", - "Label3DFormat": "3D formaat", + "Label3DFormat": "3D formaat:", "LabelAbortedByServerShutdown": "(Afgebroken door afsluiten van de server)", "LabelAccessDay": "Dag van de week:", "LabelAccessEnd": "Eind tijd:", @@ -491,7 +491,7 @@ "LabelArtists": "Artiest:", "LabelArtistsHelp": "Scheidt meerdere met een ;", "LabelAudioLanguagePreference": "Voorkeurs audiotaal:", - "LabelAutomaticallyRefreshInternetMetadataEvery": "Vernieuw metagegevens automatisch van het internet", + "LabelAutomaticallyRefreshInternetMetadataEvery": "Vernieuw metagegevens automatisch van het internet:", "LabelBindToLocalNetworkAddress": "Binden aan het lokale netwerk adres:", "LabelBindToLocalNetworkAddressHelp": "Optioneel. Overrule het lokale IP-adres om aan de http-server te binden. Indien leeg gelaten, zal de server binden aan alle beschikbare adressen. Het veranderen van deze waarde vereist herstarten van Jellyfin Server.", "LabelBirthDate": "Geboortedatum:", @@ -506,7 +506,7 @@ "LabelCertificatePassword": "Certificaat paswoord:", "LabelCertificatePasswordHelp": "Als je certificaat een paswoord vereist, vul het dan hier in alstublieft.", "LabelChannels": "Kanalen:", - "LabelCollection": "Collectie", + "LabelCollection": "Collectie:", "LabelCommunityRating": "Beoordeling gemeenschap:", "LabelContentType": "Inhoud type:", "LabelCountry": "Land:", @@ -547,7 +547,7 @@ "LabelEmbedAlbumArtDidl": "Insluiten van albumhoezen in Didl", "LabelEmbedAlbumArtDidlHelp": "Sommige apparaten prefereren deze methode voor het verkrijgen van albumhoezen. Anderen kunnen falen om af te spelen met deze optie ingeschakeld.", "LabelEnableAutomaticPortMap": "Schakel automatisch poort vertalen in", - "LabelEnableAutomaticPortMapHelp": "Probeer om de publieke poort automatisch te vertalen naar de lokale poort via UPnP. Dit werkt niet op alle routers.", + "LabelEnableAutomaticPortMapHelp": "Publieke poort automatisch doorsturen naar een lokale poort via UPnP. Dit werkt niet op alle routers en netwerk configuraties. De wijzigingen worden pas actief na een herstart van de server.", "LabelEnableBlastAliveMessages": "Alive berichten zenden", "LabelEnableBlastAliveMessagesHelp": "Zet dit aan als de server niet betrouwbaar door andere UPnP-apparaten op uw netwerk wordt gedetecteerd.", "LabelEnableDlnaClientDiscoveryInterval": "Interval voor het zoeken naar clients (seconden)", @@ -563,7 +563,7 @@ "LabelEnableRealtimeMonitorHelp": "Wijzigingen aan bestanden worden op ondersteunde bestandssystemen direct verwerkt.", "LabelEnableSingleImageInDidlLimit": "Beperk tot één enkele ingesloten afbeelding", "LabelEnableSingleImageInDidlLimitHelp": "Sommige apparaten zullen niet goed weergeven als er meerdere afbeeldingen ingesloten zijn in Didl.", - "LabelEndDate": "Eind datum|", + "LabelEndDate": "Eind datum:", "LabelEpisodeNumber": "Afleveringsnummer:", "LabelEvent": "Gebeurtenis:", "LabelEveryXMinutes": "Iedere:", @@ -581,7 +581,7 @@ "LabelGroupMoviesIntoCollectionsHelp": "Bij de weergave van film lijsten, zullen films die tot een collectie behoren worden weergegeven als een gegroepeerd object.", "LabelEncoderPreset": "H264 codering preset:", "LabelHardwareAccelerationType": "Hardware acceleratie:", - "LabelHardwareAccelerationTypeHelp": "Dit is een experimentele functie die alleen beschikbaar is op ondersteunde systemen.", + "LabelHardwareAccelerationTypeHelp": "Hardwarematige versnelling vereist extra configuratie.", "LabelHomeNetworkQuality": "Thuisnetwerk kwaliteit:", "LabelHomeScreenSectionValue": "Beginscherm sectie {0}:", "LabelHttpsPort": "Lokale HTTPS poort nummer:", @@ -666,14 +666,14 @@ "LabelNumberOfGuideDays": "Aantal dagen van de gids om te downloaden:", "LabelNumberOfGuideDaysHelp": "Het downloaden van meer dagen van de gids gegevens biedt de mogelijkheid verder vooruit te plannen en een beter overzicht geven, maar het zal ook langer duren om te downloaden. Auto kiest op basis van het aantal kanalen.", "LabelOptionalNetworkPath": "(Optioneel) Gedeelde netwerkmap:", - "LabelOptionalNetworkPathHelp": "Als deze map wordt gedeeld op uw netwerk, kunnen middels het netwerkpad Jellyfin apps op andere apparaten rechtstreeks toegang tot mediabestanden krijgen.", + "LabelOptionalNetworkPathHelp": "Als deze map wordt gedeeld op uw netwerk, kunnen middels het netwerkpad Jellyfin apps op andere apparaten rechtstreeks toegang tot mediabestanden krijgen. Bijvoorbeeld {0} or {1}.", "LabelOriginalAspectRatio": "Originele aspect ratio:", "LabelOriginalTitle": "Orginele titel:", "LabelOverview": "Overzicht:", "LabelParentNumber": "Bovenliggend nummer:", "LabelParentalRating": "Kijkwijzer classificatie:", "LabelPassword": "Wachtwoord:", - "LabelPasswordConfirm": "Wachtworod (Bevestig)", + "LabelPasswordConfirm": "Wachtwoord (Bevestig):", "LabelPasswordRecoveryPinCode": "Pincode:", "LabelPath": "Pad:", "LabelPersonRole": "Rol:", @@ -681,17 +681,17 @@ "LabelPlaceOfBirth": "Geboorteplaats:", "LabelPlayDefaultAudioTrack": "Standaard audio spoor afspelen ongeacht de taal", "LabelPlaylist": "Afspeellijst:", - "LabelPostProcessor": "Nabewerkings toepassing:", - "LabelPostProcessorArguments": "Nabewerkings command line argumenten:", + "LabelPostProcessor": "Nabewerkings- toepassing:", + "LabelPostProcessorArguments": "Nabewerkings command lijn argumenten:", "LabelPostProcessorArgumentsHelp": "Gebruik {path} als het pad naar het opnamebestand.", - "LabelPreferredDisplayLanguage": "Voorkeurs weergavetaal:", + "LabelPreferredDisplayLanguage": "Voorkeur weergavetaal:", "LabelPreferredDisplayLanguageHelp": "Vertaling van Jellyfin is een voortdurend project.", - "LabelPreferredSubtitleLanguage": "Voorkeurstaal ondertitels:", + "LabelPreferredSubtitleLanguage": "Voorkeurstaal ondertiteling:", "LabelPrevious": "Vorige", "LabelProfileAudioCodecs": "Geluidscodecs:", - "LabelProfileCodecsHelp": "Gescheiden door een komma. Deze kan leeg gelaten worden om te laten gelden voor alle codecs.", - "LabelProfileContainersHelp": "Gescheiden door een komma. Deze kan leeg gelaten worden om te laten gelden voor alle containers.", - "LabelProtocol": "Protokol:", + "LabelProfileCodecsHelp": "Gescheiden door een komma. Dit kan leeg worden gelaten om te laten gelden voor alle codecs.", + "LabelProfileContainersHelp": "Gescheiden door een komma. Dit kan leeg worden gelaten om te laten gelden voor alle containers.", + "LabelProtocol": "Protocol:", "LabelProtocolInfoHelp": "De waarde die wordt gebruikt bij het reageren op GetProtocolInfo verzoeken van het apparaat.", "LabelPublicHttpPort": "Publieke HTTP poort nummer:", "LabelPublicHttpPortHelp": "Het publieke poortnummer dat moet worden toegewezen aan de lokale HTTP poort.", @@ -702,12 +702,12 @@ "LabelRecord": "Opnemen:", "LabelRecordingPath": "Standaard opname pad:", "LabelRecordingPathHelp": "Geef de standaard locatie op om opnamen op te slaan. Indien leeg gelaten, zal de map van de server-programma gegevens worden gebruikt.", - "LabelRefreshMode": "Vernieuw-modus", + "LabelRefreshMode": "Ververs-modus:", "LabelReleaseDate": "Uitgave datum:", "LabelRemoteClientBitrateLimit": "Internet streaming bitrate limiet (Mbps):", - "LabelRemoteClientBitrateLimitHelp": "Een optionele bitrate limiet per stream voor alle apparaten buiten het netwerk. Dit is handig om te voorkomen dat apparaten een hogere bitrate vragen dan je internetverbinding aan kan. Dit kan een verhoogde belasting van de CPU in je server veroorzaken om om videos direct te transcoderen naar een lagere bitrate.", + "LabelRemoteClientBitrateLimitHelp": "Een optionele bitrate per stream limiet voor alle apparaten buiten het netwerk. Dit is handig om te voorkomen dat apparaten een hogere bitrate vragen dan je internetverbinding aan kan. Dit kan een verhoogde belasting van de CPU in je server veroorzaken om videos direct te transcoderen naar een lagere bitrate.", "LabelRuntimeMinutes": "Speelduur (minuten):", - "LabelSaveLocalMetadata": "Afbeeldingen opslaan in de mediamappen", + "LabelSaveLocalMetadata": "Afbeeldingen opslaan in mediamappen", "LabelSaveLocalMetadataHelp": "Door afbeeldingen op te slaan in de mediamappen kunnen ze makkelijker worden aangepast.", "LabelScheduledTaskLastRan": "Laatste keer {0}, duur {1}.", "LabelScreensaver": "Schermbeveiliging:", @@ -722,31 +722,31 @@ "LabelSeriesRecordingPath": "Serieopname pad (optioneel):", "LabelServerHost": "Server:", "LabelServerHostHelp": "192.168.1.100:8096 of https://mijnserver.nl", - "LabelSimultaneousConnectionLimit": "Gelijktijdige streams limiet:", - "LabelSkipBackLength": "Terugspoellengte", - "LabelSkipForwardLength": "Vooruitspoellengte", + "LabelSimultaneousConnectionLimit": "Gelijktijdige stream limiet:", + "LabelSkipBackLength": "Terugspoellengte:", + "LabelSkipForwardLength": "Vooruitspoellengte:", "LabelSkipIfAudioTrackPresent": "Overslaan als het standaard geluidsspoor overeenkomt met de taal van de download", - "LabelSkipIfAudioTrackPresentHelp": "Vink dit uit om ervoor te zorgen dat alle video's ondertiteings krijgen, ongeacht de geluidstaal.", + "LabelSkipIfAudioTrackPresentHelp": "Vink dit uit om ervoor te zorgen dat alle video's ondertitelingen krijgen, ongeacht de geluidstaal.", "LabelSkipIfGraphicalSubsPresent": "Overslaan als de video al ingesloten ondertiteling heeft", "LabelSkipIfGraphicalSubsPresentHelp": "Tekstversies van ondertiteling opslaan zal video's efficiënter overbrengen en de kans op transcodering van video's verkleinen.", "LabelSonyAggregationFlags": "Sony aggregatie vlaggen:", "LabelSonyAggregationFlagsHelp": "Bepaalt de inhoud van het aggregationFlags element in de urn: schemas-sonycom av namespace.", "LabelSortBy": "Sorteren op:", - "LabelSortOrder": "Sorteervolgorde:", + "LabelSortOrder": "Sorteer volgorde:", "LabelSortTitle": "Sorteer titel:", "LabelSoundEffects": "Geluidseffecten:", "LabelSource": "Bron:", - "LabelSpecialSeasonsDisplayName": "De weergavenaam van de speciale seizoen:", + "LabelSpecialSeasonsDisplayName": "De weergavenaam van een speciaal seizoen:", "LabelSportsCategories": "Sport categorieën:", "LabelStartWhenPossible": "Start indien mogelijk:", "LabelStopWhenPossible": "Stop indien mogelijk:", "LabelStopping": "Stoppen", - "LabelSubtitleDownloaders": "Ondertiteldownloaders:", + "LabelSubtitleDownloaders": "Ondertiteling downloaders:", "LabelSubtitleFormatHelp": "Voorbeeld: srt", - "LabelSubtitlePlaybackMode": "Ondertitel mode:", - "LabelSubtitles": "Ondertitels", + "LabelSubtitlePlaybackMode": "Ondertitel modus:", + "LabelSubtitles": "Ondertiteling", "LabelSupportedMediaTypes": "Ondersteunde Media Types:", - "LabelTVHomeScreen": "TV mode begin scherm", + "LabelTVHomeScreen": "TV mode begin scherm:", "LabelTextBackgroundColor": "Tekst achtergrond kleur:", "LabelTextColor": "Tekst kleur:", "LabelTextSize": "Tekst grootte:", @@ -756,9 +756,9 @@ "LabelTitle": "Titel:", "LabelTrackNumber": "Tracknummer:", "LabelTranscodingAudioCodec": "Geluidscodec:", - "LabelTranscodingTempPathHelp": "Specificeer een eigen pad voor de transcode bestanden die geleverd worden aan cliënten. Laat leeg om het server standaard te gebruiken.", - "LabelTranscodingThreadCount": "Aantal transcodeer threads:", - "LabelTranscodingThreadCountHelp": "Selecteer het maximale aantal threads die gebruikt mogen worden om te transcoderen. Bij een lager aantal zal het CPU gebruik lager zijn, maar kan de afspeelkwaliteit minder zijn.", + "LabelTranscodingTempPathHelp": "Specificeer een eigen pad voor de transcode bestanden die geleverd worden aan gebruikers. Laat leeg om de server standaard te gebruiken.", + "LabelTranscodingThreadCount": "Aantal transcodeer draden:", + "LabelTranscodingThreadCountHelp": "Selecteer het maximale aantal draden die gebruikt mogen worden om te kunnen transcoderen. Bij een lager aantal zal het processorgebruik lager zijn, maar kan de afspeelkwaliteit minder zijn voor een vloeiende ervaring.", "LabelTunerIpAddress": "Tuner IP adres:", "LabelTunerType": "Soort Tuner:", "LabelTypeText": "Tekst", @@ -766,8 +766,8 @@ "LabelUser": "Gebruiker:", "LabelUserAgent": "User-agent:", "LabelUserLibrary": "Gebruikers Bibliotheek:", - "LabelUserLibraryHelp": "Selecteer welke gebruikers bibliotheek weergegeven moet worden op het apparaat. Laat leeg standaardinstelling te gebruiken.", - "LabelUserRemoteClientBitrateLimitHelp": "Schrijf de standaard globale waarde gedefineerd in de server afspeel instellingen.", + "LabelUserLibraryHelp": "Selecteer welke gebruikers bibliotheek weergegeven moeten worden op het apparaat. Laat leeg om de standaardinstelling te gebruiken.", + "LabelUserRemoteClientBitrateLimitHelp": "Schrijf de standaard globale waarde gedefinieerd in de server afspeel instellingen.", "LabelUsername": "Gebruikersnaam:", "LabelVaapiDevice": "VA API Apparaat:", "LabelVaapiDeviceHelp": "Dit is de render knooppunt dat wordt gebruikt voor hardwareversnelling.", @@ -792,11 +792,11 @@ "List": "Lijst", "LiveBroadcasts": "Live uitzendingen", "ManageLibrary": "Bibliotheek beheren", - "ManageRecording": "Beheren opnames", + "ManageRecording": "Beheer opnames", "MapChannels": "Map Kanalen", - "MarkPlayed": "Markeren als Afgespeeld", - "MarkUnplayed": "Markeren als Niet Afgespeeld", - "MaxParentalRatingHelp": "Media met een hogere classificatie wordt niet weergegeven", + "MarkPlayed": "Markeren als afgespeeld", + "MarkUnplayed": "Markeren als niet afgespeeld", + "MaxParentalRatingHelp": "Media met een hogere classificatie wordt niet weergegeven voor deze gebruiker.", "MediaInfoAnamorphic": "Anamorf", "MediaInfoAspectRatio": "Beeld verhouding", "MediaInfoBitDepth": "Bitdiepte", @@ -811,48 +811,48 @@ "MediaInfoPixelFormat": "Pixel formaat", "MediaInfoProfile": "Profiel", "MediaInfoResolution": "Resolutie", - "MediaInfoSampleRate": "Samplesnelheid", + "MediaInfoSampleRate": "Bemonsteringsfrequentie", "MediaInfoSize": "Grootte", "MediaInfoTimestamp": "Tijdstempel", - "MediaIsBeingConverted": "De media wordt geconverteerd naar een formaat dat compatible is met het apparaat dat wordt gebruikt om de media af te spelen.", - "MessageAlreadyInstalled": "Deze versie is al geïnstalleerd", - "MessageAreYouSureDeleteSubtitles": "Weet u zeker dat u dit ondertitelbestand wilt verwijderen?", + "MediaIsBeingConverted": "De media wordt geconverteerd naar een formaat dat leesbaar is met het apparaat dat wordt gebruikt om de media af te spelen.", + "MessageAlreadyInstalled": "Deze versie is al geïnstalleerd.", + "MessageAreYouSureDeleteSubtitles": "Weet u zeker dat u dit ondertitelingsbestand wilt verwijderen?", "MessageAreYouSureYouWishToRemoveMediaFolder": "Weet u zeker dat u deze media map wilt verwijderen?", "MessageConfirmDeleteGuideProvider": "Weet u zeker dat u deze gidsprovider wilt verwijderen?", "MessageConfirmDeleteTunerDevice": "Weet u zeker dat u dit apparaat wilt verwijderen?", "MessageConfirmProfileDeletion": "Weet u zeker dat u dit profiel wilt verwijderen?", - "MessageConfirmRecordingCancellation": "Opnemen annuleren?", + "MessageConfirmRecordingCancellation": "Opname annuleren?", "MessageConfirmRemoveMediaLocation": "Weet u zeker dat u deze locatie wilt verwijderen?", "MessageConfirmRestart": "Weet u zeker dat u Jellyfin Server wilt herstarten?", - "MessageConfirmRevokeApiKey": "Weet u zeker dat u deze api key in wilt trekken? De verbinding met Jellyfin Server zal direct verbroken worden.", + "MessageConfirmRevokeApiKey": "Weet u zeker dat u deze api sleutel in wilt trekken? De verbinding met Jellyfin Server zal direct verbroken worden.", "MessageConfirmShutdown": "Weet u zeker dat u de server wilt afsluiten?", "MessageContactAdminToResetPassword": "Neem contact op met de server beheerder om uw wachtwoord te resetten.", "MessageCreateAccountAt": "Maak een account bij {0}", - "MessageDeleteTaskTrigger": "Weet u zeker dat u deze taak trigger wilt verwijderen?", + "MessageDeleteTaskTrigger": "Weet u zeker dat u deze signaal taak wilt verwijderen?", "MessageDirectoryPickerBSDInstruction": "Voor BSD kan het noodzakelijk zijn opslag op uw FreeNAS Jail te configureren voordat Jellyfin het kan benaderen.", - "MessageDirectoryPickerInstruction": "Netwerk paden kunnen handmatig worden ingevoerd in het geval de Netwerk knop faalt om uw apparatuur te lokaliseren . Bijvoorbeeld: {0} of {1}.", + "MessageDirectoryPickerInstruction": "Netwerk paden kunnen handmatig worden ingevoerd in het geval de Netwerk knop faalt om uw apparatuur te lokaliseren. Bijvoorbeeld: {0} of {1}.", "MessageDirectoryPickerLinuxInstruction": "Voor Linux op Arch Linux, CentOS, Debian, Fedora, openSUSE, of Ubuntu, moet u de service-gebruiker ten minste leestoegang tot uw opslaglocaties verlenen.", "MessageDownloadQueued": "Download in de wachtrij geplaatst.", - "MessageEnablingOptionLongerScans": "Inschakelen van deze optie kan leiden tot langere doorlooptijd van bibliotheek scans.", + "MessageEnablingOptionLongerScans": "Het inschakelen van deze optie kan leiden tot langere doorlooptijd van bibliotheek scans.", "MessageFileReadError": "Er is een fout opgetreden bij het lezen van het bestand. Probeer het opnieuw.", - "MessageForgotPasswordFileCreated": "Het volgende bestand met instructies hoe nu verder te gaan is gemaakt:", + "MessageForgotPasswordFileCreated": "Het volgende bestand is gecreëerd op uw server en bevat instructies om verder te gaan:", "MessageForgotPasswordInNetworkRequired": "Probeer de wachtwoord herstel procedure opnieuw vanuit uw thuisnetwerk.", "MessageInstallPluginFromApp": "Deze plugin moet geïnstalleerd worden vanuit de app waarin u het wilt gebruiken.", "MessageInvalidForgotPasswordPin": "Er is een ongeldige of verlopen pincode ingegeven. Probeer opnieuw.", - "MessageInvalidUser": "Foutieve gebruikersnaam of wachtwoord. Probeer opnieuw.", + "MessageInvalidUser": "Incorrecte gebruikersnaam of wachtwoord. Probeer opnieuw.", "MessageItemSaved": "Item opgeslagen.", - "MessageItemsAdded": "Items toegevoegd", + "MessageItemsAdded": "Items toegevoegd.", "MessageLeaveEmptyToInherit": "Leeg laten om instellingen van bovenliggend item of de algemene waarde over te nemen.", "MessageNoAvailablePlugins": "Geen beschikbare Plugins.", "MessageNoMovieSuggestionsAvailable": "Er zijn momenteel geen film suggesties beschikbaar. Begin met het bekijken en waardeer uw films, kom daarna terug om uw aanbevelingen te bekijken.", - "MessageNoPluginsInstalled": "U heeft geen Plugins geïnstalleerd.", - "MessageNoTrailersFound": "Geen trailers gevonden. Installeer het Trailers kanaal en verbeter uw film ervaring door middel van een bibliotheek met internet trailers.", + "MessageNoPluginsInstalled": "U heeft geen plugins geïnstalleerd.", + "MessageNoTrailersFound": "Geen trailers gevonden. Installeer het Trailers kanaal om uw film ervaring te verbeteren door middel van het toevoegen van een bibliotheek met internet trailers.", "MessageNothingHere": "Lijst is leeg.", - "MessagePasswordResetForUsers": "De volgende gebruikers hebben hun wachtwoord laten resetten. Zij kunnen nu inloggen met de pin codes die gebruikt werden om de reset te voltooien.", + "MessagePasswordResetForUsers": "De volgende gebruikers hebben hun wachtwoord laten herstellen. Zij kunnen nu inloggen met de pin codes die gebruikt werden om de herstel te voltooien.", "MessagePlayAccessRestricted": "Afspelen hiervan is op dit moment niet toegestaan. Neem contact op met uw server beheerder voor meer informatie.", "MessagePleaseEnsureInternetMetadata": "Zorg ervoor dat het downloaden van internet-metadata is ingeschakeld.", "MessagePleaseWait": "Even geduld. Dit kan even duren.", - "MessagePluginConfigurationRequiresLocalAccess": "Meld svp. op de lokale server aan om deze plugin te configureren.", + "MessagePluginConfigurationRequiresLocalAccess": "Om deze plugin te configuren moet u zich aanmelden direct op de lokale server.", "MessagePluginInstallDisclaimer": "Plugins ontwikkeld door leden van de Jellyfin gemeenschap zijn een geweldige manier om uw Jellyfin ervaring met extra functies en voordelen te verbeteren. Alvorens te installeren, dient u zich bewust te zijn van de gevolgen die zij kunnen hebben op uw Jellyfin Server, zoals langere bibliotheek scans, extra achtergrondinformatie verwerking, en een verminderde stabiliteit van het systeem.", "MessageReenableUser": "Zie hieronder hoe opnieuw in te schakelen", "MessageSettingsSaved": "Instellingen opgeslagen.", @@ -882,13 +882,13 @@ "NewEpisodesOnly": "Alleen nieuwe afleveringen", "News": "Nieuws", "Next": "Volgende", - "NextUp": "Volgende", + "NextUp": "Hierna", "No": "Nee", "NoNewDevicesFound": "Er zijn geen nieuwe apparaten gevonden. Sluit dit melding en voer handmatig de apparaat gegevens in om een nieuwe tuner toe te voegen.", "NoNextUpItemsMessage": "Niets gevonden. Start met kijken!", "NoPluginConfigurationMessage": "Deze plugin heeft geen instellingen te configureren.", "NoSubtitleSearchResultsFound": "Geen resultaten gevonden.", - "NoSubtitles": "Geen ondertitels", + "NoSubtitles": "Geen ondertiteling", "NoSubtitlesHelp": "Ondertitels worden niet standaard weergegeven. Deze kunnen tijdens het afspelen handmatig worden ingeschakeld.", "None": "Geen", "Normal": "Normaal", @@ -896,14 +896,14 @@ "OneChannel": "Eén kanaal", "OnlyForcedSubtitles": "Alleen geforceerde ondertitels", "OnlyForcedSubtitlesHelp": "Alleen als geforceerd gemarkeerde ondertitels worden geladen.", - "OnlyImageFormats": "Alleen image formaten (VOBSUP, PGS, SUB, etc.)", + "OnlyImageFormats": "Alleen beeld formaten (VOBSUP, PGS, SUB, etc.)", "OptionAdminUsers": "Beheerders", "OptionAlbumArtist": "Albumartiest", "OptionAllUsers": "Alle gebruikers", "OptionAllowAudioPlaybackTranscoding": "Afspelen van geluid via transcoding toestaan", "OptionAllowBrowsingLiveTv": "Live TV toegang toestaan", "OptionAllowContentDownloading": "Media downloaden en synchroniseren toestaan", - "OptionAllowLinkSharing": "Sta social media delen toe", + "OptionAllowLinkSharing": "Sta het delen op social media toe", "OptionAllowLinkSharingHelp": "Alleen webpagina's met media-informatie worden gedeeld. Media-bestanden worden nooit publiekelijk gedeeld. Gedeelde items zijn beperkt in tijd en verlopen na {0} dagen.", "OptionAllowManageLiveTv": "Live TV opname beheer toestaan", "OptionAllowMediaPlayback": "Media afspelen toestaan", @@ -914,7 +914,7 @@ "OptionAllowSyncTranscoding": "Het downloaden en synchroniseren van media via transcoding toestaan", "OptionAllowUserToManageServer": "Deze gebruiker kan de server beheren", "OptionAllowVideoPlaybackRemuxing": "Sta afspelen toe van video die conversie vereist zonder re-encoding", - "OptionAllowVideoPlaybackTranscoding": "Afspelen van video via transcoding toestaan", + "OptionAllowVideoPlaybackTranscoding": "Afspelen van video die transcoderen vereisen toestaan", "OptionArtist": "Artiest", "OptionAscending": "Oplopend", "OptionAutomatic": "Automatisch", @@ -927,8 +927,8 @@ "OptionBlockMusic": "Muziek", "OptionBlockTvShows": "TV Series", "OptionBluray": "Blu-ray", - "OptionCommunityRating": "Gemeenschaps Waardering", - "OptionContinuing": "Wordt vervolgd...", + "OptionCommunityRating": "Algemene Waardering", + "OptionContinuing": "Wordt vervolgd", "OptionCriticRating": "Kritieken", "OptionCustomUsers": "Aangepast", "OptionDaily": "Dagelijks", @@ -937,7 +937,7 @@ "OptionDateAddedImportTime": "Gebruik scan datum", "OptionDatePlayed": "Datum afgespeeld", "OptionDescending": "Aflopend", - "OptionDisableUser": "Dit account uitschakelen", + "OptionDisableUser": "Deze gebruiker uitschakelen", "OptionDisableUserHelp": "Indien uitgeschakeld zal de server geen verbindingen van deze gebruiker toestaan. Bestaande verbindingen zullen abrupt worden beëindigd.", "OptionDislikes": "Niet leuk", "OptionDisplayFolderView": "Toon een mappenweergave als u gewoon Mediamappen wilt weergeven", @@ -956,10 +956,10 @@ "OptionEnableExternalContentInSuggestionsHelp": "Laat internet trailers en live-tv-programma's op te nemen binnen de voorgestelde inhoud.", "OptionEnableForAllTuners": "Inschakelen voor alle tuners", "OptionEnableM2tsMode": "M2ts-modus inschakelen", - "OptionEnableM2tsModeHelp": "m2ts-modus bij het encoderen naar mpegts inschakelen", + "OptionEnableM2tsModeHelp": "M2ts-modus bij het encoderen naar mpegts inschakelen.", "OptionEnded": "Gestopt", "OptionEquals": "Is gelijk aan", - "OptionEstimateContentLength": "Lengte schatten van de inhoud bij het transcoderen", + "OptionEstimateContentLength": "Lengte inschatten van de inhoud bij het transcoderen", "OptionEveryday": "Elke dag", "OptionExternallyDownloaded": "Externe download", "OptionExtractChapterImage": "Inschakelen uitpakken van hoofdstuk afbeeldingen", @@ -967,10 +967,10 @@ "OptionFriday": "Vrijdag", "OptionHasSpecialFeatures": "Extra's", "OptionHasSubtitles": "Ondertiteling", - "OptionHasThemeSong": "Herkenningsmelodie", + "OptionHasThemeSong": "Thema Lied", "OptionHasThemeVideo": "Thema Video", "OptionHideUser": "Verberg deze gebruiker op de aanmeldschermen", - "OptionHideUserFromLoginHelp": "Handig voor pivé of verborgen beheer accounts. De gebruiker zal handmatig m.b.v. gebruikersnaam en wachtwoord aan moeten melden.", + "OptionHideUserFromLoginHelp": "Handig voor pivé of verborgen beheer accounts. De gebruiker zal handmatig moeten inloggen met een gebruikersnaam en wachtwoord.", "OptionHlsSegmentedSubtitles": "HLS gesegmenteerde ondertiteling", "OptionHomeVideos": "Foto's", "OptionIgnoreTranscodeByteRangeRequests": "Transcodeer byte range-aanvragen negeren", @@ -980,7 +980,7 @@ "OptionMissingEpisode": "Ontbrekende Afleveringen", "OptionMonday": "Maandag", "OptionNameSort": "Naam", - "OptionNew": "Nieuw ...", + "OptionNew": "Nieuw…", "OptionNone": "Geen", "OptionOnAppStartup": "Op applicatie start", "OptionOnInterval": "Op interval", @@ -1020,9 +1020,9 @@ "OptionWeekly": "Wekelijks", "OriginalAirDateValue": "Originele uitzenddatum: {0}", "Overview": "Overzicht", - "PackageInstallCancelled": "{0} installatie geannuleerd.", - "PackageInstallCompleted": "{0} installatie voltooid.", - "PackageInstallFailed": "{0} installatie is mislukt.", + "PackageInstallCancelled": "{0} (versie {1}) installatie geannuleerd.", + "PackageInstallCompleted": "{0} (versie {1}) installatie voltooid.", + "PackageInstallFailed": "{0} (versie {1}) installatie is mislukt.", "ParentalRating": "Kijkwijzer classificatie", "PasswordMatchError": "Wachtwoord en wachtwoord bevestiging moeten hetzelfde zijn.", "PasswordResetComplete": "Het wachtwoord is opnieuw ingesteld.", @@ -1064,7 +1064,7 @@ "Raised": "Verhoogd", "Rate": "Waardeer", "RecentlyWatched": "Onlangs bekeken", - "RecommendationBecauseYouLike": "Omdat u {0} leuk vond.", + "RecommendationBecauseYouLike": "Omdat u {0} leuk vond", "RecommendationBecauseYouWatched": "Omdat u keek naar {0}", "RecommendationDirectedBy": "Geregisseerd door {0}", "RecommendationStarring": "In de hoofdrollen {0}", @@ -1072,12 +1072,12 @@ "RecordSeries": "Series Opnemen", "RecordingCancelled": "Opname geannuleerd.", "RecordingPathChangeMessage": "Bij het wijzigen van uw opnamemap zullen bestaande opnamen niet migreren van de oude locatie naar de nieuwe. U moet deze desgewenst handmatig verplaatsen.", - "RecordingScheduled": "Opname schema", + "RecordingScheduled": "Opname schema.", "Recordings": "Opnames", "Refresh": "Vernieuwen", "RefreshDialogHelp": "Metadata wordt vernieuwd op basis van de instellingen en internet diensten die zijn ingeschakeld in het dashboard van de Jellyfin Server.", "RefreshMetadata": "Metadata vernieuwen", - "RefreshQueued": "Vernieuwen wachtrij", + "RefreshQueued": "Verversen wachtrij.", "ReleaseDate": "Uitgave datum", "RememberMe": "Onthoud mij", "RemoveFromCollection": "Verwijder uit collectie", @@ -1085,7 +1085,7 @@ "Repeat": "Herhaling", "RepeatAll": "Alle herhalen", "RepeatEpisodes": "Herhaal afleveringen", - "RepeatMode": "Herhaal mode", + "RepeatMode": "Herhaal modus", "RepeatOne": "Eén herhalen", "ReplaceAllMetadata": "Alle metadata vervangen", "ReplaceExistingImages": "Bestaande afbeeldingen vervangen", @@ -1132,7 +1132,7 @@ "SkipEpisodesAlreadyInMyLibrary": "Neem geen afleveringen op die al in mijn bibliotheek aanwezig zijn", "SkipEpisodesAlreadyInMyLibraryHelp": "Afleveringen zullen worden vergeleken met behulp van seizoen en aflevering nummers, indien beschikbaar.", "Small": "Klein", - "SmallCaps": "Klein Kapitaal", + "SmallCaps": "Kleine letters", "Smaller": "Kleiner", "Smart": "Slim", "SmartSubtitlesHelp": "Ondertitels worden weergegeven in de voorkeurstaal als de audio in een andere taal zijn.", @@ -1201,7 +1201,7 @@ "ThemeSongs": "Themamuziek", "ThemeVideos": "Themavideo's", "TheseSettingsAffectSubtitlesOnThisDevice": "Deze instellingen betreffen ondertitels op dit apparaat", - "ThisWizardWillGuideYou": "Deze wizard helpt u door het setup-proces. Om te beginnen selecteert u eerst de gewenste taal.", + "ThisWizardWillGuideYou": "Deze helper helpt u door het opzet proces heen. Om te beginnen selecteert u eerst de gewenste taal.", "Thumb": "Miniatuur", "Thursday": "Donderdag", "TitleHardwareAcceleration": "Hardware versnelling", @@ -1249,7 +1249,7 @@ "XmlTvKidsCategoriesHelp": "Programma's met deze categorieën wordt weergegeven als programma's voor kinderen. Scheid meerdere met '|'.", "XmlTvMovieCategoriesHelp": "Programma's met deze categorieën wordt weergegeven als films. Scheid meerdere met '|'.", "XmlTvNewsCategoriesHelp": "Programma's met deze categorieën wordt weergegeven als nieuwsprogramma's. Scheid meerdere met '|'.", - "XmlTvPathHelp": "Een pad naar een XML-TV-bestand. Jellyfin zal dit bestand regelmatig lezen en controleren voor updates. U bent verantwoordelijk voor het maken en bijwerken van het bestand.", + "XmlTvPathHelp": "Een pad naar een XML-TV-bestand. Jellyfin zal dit bestand regelmatig lezen en controleren voor updates. U bent verantwoordelijk voor het maken en bijwerken van dit bestand.", "XmlTvSportsCategoriesHelp": "Programma's met deze categorieën wordt weergegeven als sportprogramma's. Scheid meerdere met '|'.", "Yes": "Ja", "Yesterday": "Gisteren", @@ -1271,8 +1271,8 @@ "Genre": "Genre", "Genres": "Genres", "HeaderAlbums": "Albums", - "HeaderCastAndCrew": "Cast & Crew", - "HeaderCastCrew": "Cast & Crew", + "HeaderCastAndCrew": "Acteurs en medewerkers", + "HeaderCastCrew": "Acteurs & medewerkers", "Art": "Afbeeldingen", "HeaderLiveTV": "Live TV", "HeaderDetails": "Details", @@ -1282,15 +1282,13 @@ "HeaderHttpHeaders": "HTTP Headers", "HeaderImageLogo": "Logo", "HeaderMenu": "Menu", - "HeaderOffline": "Offline", - "HeaderOfflineDownloads": "Offline Media", "HeaderResetTuner": "Ontvanger resetten", "HeaderReviews": "Beoordelingen", "HeaderStatus": "Status", "HeaderSync": "Synchronisatie", "HeaderTV": "TV", "HeaderTopPlugins": "Top Plugins", - "AuthProviderHelp": "Selecteer een Authenticatie Provider om het wachtwoord van deze gebruiker te verifiëren", + "AuthProviderHelp": "Selecteer een Authenticatie Provider om het wachtwoord van deze gebruiker te verifiëren.", "HeaderFavoriteMovies": "Favoriete Films", "HeaderFavoriteShows": "Favoriete shows", "HeaderFavoriteEpisodes": "Favoriete afleveringen", @@ -1298,7 +1296,7 @@ "HeaderFavoriteArtists": "Favoriete artiesten", "HeaderFavoriteSongs": "Favoriete nummers", "HeaderFavoriteVideos": "Favoriete Films", - "HeaderInstantMix": "Instant Mix", + "HeaderInstantMix": "Directe Mix", "HeaderItems": "Items", "HeaderJellyfinServer": "Jellyfin Server", "HeaderLiveTv": "Live TV", @@ -1322,23 +1320,23 @@ "LabelProfileCodecs": "Codecs:", "LabelProfileContainer": "Container:", "LabelProfileVideoCodecs": "Video codecs:", - "LabelProtocolInfo": "Protocool info:", + "LabelProtocolInfo": "Protocol info:", "LabelServerName": "Server naam:", - "LabelSkin": "Skin:", + "LabelSkin": "Uiterlijk:", "ButtonAddImage": "Voeg afbeelding toe", "LabelSize": "Grootte:", - "CopyStreamURLSuccess": "URL gekopieerd", - "CopyStreamURL": "Kopieer stream URL ", + "CopyStreamURLSuccess": "URL succesvol gekopieerd.", + "CopyStreamURL": "Kopieer Stream URL", "TabLiveTV": "Live TV", "ValueAlbumCount": "{0} albums", "FetchingData": "Meer data op aan het halen", "HeaderFavoriteBooks": "Favoriete Boeken", - "LabelUserLoginAttemptsBeforeLockout": "Mislukte login pogingen voordat de gebruiker buitengesloten wordt:", + "LabelUserLoginAttemptsBeforeLockout": "Mislukte aanmeld pogingen voordat de gebruiker buitengesloten wordt:", "OptionHasTrailer": "Trailer", "OptionMax": "Max", "LabelBaseUrl": "Basis URL:", - "LabelTranscodingProgress": "Transcoding voortgang:", - "LabelTriggerType": "Trigger Type:", + "LabelTranscodingProgress": "Transcoderen voortgang:", + "LabelTriggerType": "Signaal Type:", "LaunchWebAppOnStartup": "Lanceer de web interface wanneer de server start", "MediaInfoBitrate": "Bitrate", "MediaInfoInterlaced": "Interlaced", @@ -1358,13 +1356,13 @@ "SubtitleOffset": "Ondertiteling Compensatie", "LabelXDlnaDoc": "X-DLNA doc:", "LiveTV": "Live TV", - "LabelTag": "Tag:", + "LabelTag": "Label:", "Live": "Live", "OptionDvd": "DVD", "OptionResElement": "res element", "TV": "TV", "HeaderHome": "Thuis", - "MediaInfoStreamTypeEmbeddedImage": "Embedded Afbeelding", + "MediaInfoStreamTypeEmbeddedImage": "Ingeladen Afbeelding", "LabelTypeMetadataDownloaders": "{0} metadata downloaders:", "OptionLoginAttemptsBeforeLockout": "Bepaald hoeveel foutieve login pogingen plaats kunnen vinden voor dat de gebruiker buitengesloten wordt.", "Premiere": "Première", @@ -1372,7 +1370,7 @@ "LabelAudioSampleRate": "Audio sample rate:", "OptionIsHD": "HD", "OptionIsSD": "SD", - "OptionSpecialEpisode": "Specials", + "OptionSpecialEpisode": "Extra's", "TabContainers": "Containers", "TabDashboard": "Dashboard", "TabNetworking": "Netwerken", @@ -1387,7 +1385,7 @@ "OptionThumbCard": "Miniatuur kaart", "PlaybackData": "Afspeel Data", "PasswordResetProviderHelp": "Kies een wachtwoord reset provider om te gebruiken wanneer deze gebruiker een wachtwoord reset aanvraagt", - "Screenshots": "Screenshots", + "Screenshots": "Schermafdruk", "Series": "Series", "TabAlbums": "Albums", "TabGenres": "Genres", @@ -1401,25 +1399,25 @@ "MediaInfoCodec": "Codec", "Menu": "Menu", "OptionThumb": "Miniatuur", - "LabelTranscodingFramerate": "Transcoding framerate:", + "LabelTranscodingFramerate": "Transcoderen beeldverversing:", "LabelType": "Type:", "HeaderFavoritePeople": "Favoriete Mensen", "LabelAudioBitrate": "Audio bitrate:", "LabelAudioCodec": "Audio codec:", "LabelAudioChannels": "Audio kanalen:", "LabelBitrate": "Bitrate:", - "LabelBaseUrlHelp": "Hier kunt u een eigen subdirectory toevoegen om de server te bereiken doormiddel van een meer unieke URL.", + "LabelBaseUrlHelp": "Voegt een aangepaste submap toe aan de server-URL. Bijvoorbeeld: http://example.com/<baseurl>", "LabelFolder": "Folder:", "LabelLineup": "Lineup:", "LabelPlayer": "Speler:", "LabelPlayMethod": "Afspeel methode:", - "LabelPleaseRestart": "Verandering zullen toegepast worden na het handmatig herladen van de web cliënt.", + "LabelPleaseRestart": "De wijzigingen zullen worden toegepast na het handmatig herladen van de web cliënt.", "LabelStatus": "Status:", - "LabelTagline": "Tagline:", + "LabelTagline": "Label tekst:", "LabelTranscodingContainer": "Container:", - "LabelTranscodePath": "Transcode pad:", - "LabelTranscodes": "Transcodes:", - "DashboardOperatingSystem": "Besturingsysteem: {0}", + "LabelTranscodePath": "Transcodeer pad:", + "LabelTranscodes": "Transcoderen:", + "DashboardOperatingSystem": "Besturingssysteem: {0}", "LabelWeb": "Web:", "LaunchWebAppOnStartupHelp": "Open de web cliënt in uw standaard browser wanneer de server voor de eerste keer start. Dit zal niet voorkomen tijdens gebruik van de server herstart functie.", "LeaveBlankToNotSetAPassword": "U kunt dit veld leeg laten om geen wachtwoord in te stellen.", @@ -1438,17 +1436,17 @@ "Logo": "Logo", "MediaInfoCodecTag": "Codec tag", "MediaInfoContainer": "Container", - "MediaInfoFramerate": "Framerate", - "MediaInfoRefFrames": "Ref frames", + "MediaInfoFramerate": "Beeldverversing", + "MediaInfoRefFrames": "Ref beeld", "MediaInfoSoftware": "Software", - "MessageImageFileTypeAllowed": "Alleen JPEG en PNG bestanden zijn ondersteund.", - "MessageImageTypeNotSelected": "Selecteer alstublieft een afbeelding type van het drop-down menu.", + "MessageImageFileTypeAllowed": "Alleen JPEG en PNG bestanden worden ondersteund.", + "MessageImageTypeNotSelected": "Selecteer een afbeelding type van het menu alstublieft .", "MessageNoCollectionsAvailable": "Collecties staan u toe om te genieten van gepersonaliseerde groeperingen van Films, Series en Albums. Klik de + knop om te beginnen met het maken van collecties.", - "MessageNoServersAvailable": "Geen servers zijn gevonden doormiddel van het automatisch server ontdekken.", + "MessageNoServersAvailable": "Geen servers zijn gevonden door middel van het automatisch ontdekken van een server.", "Metadata": "Metadata", "MetadataManager": "Metadata Beheerder", "MusicAlbum": "Muziek Album", - "NumLocationsValue": "{0} folders", + "NumLocationsValue": "{0} mappen", "OptionBanner": "Banner", "OptionBlockTrailers": "Trailers", "OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)", @@ -1461,5 +1459,106 @@ "TabStreaming": "Streamen", "TabTrailers": "Trailers", "OptionAuto": "Auto", - "OptionProfileVideo": "Video" + "OptionProfileVideo": "Video", + "AlbumArtist": "Album Artiest", + "Album": "Album", + "DeinterlaceMethodHelp": "Selecteer de deinterlacingmethode die u wilt gebruiken bij het transcoderen van geïnterlinieerde inhoud.", + "CopyStreamURLError": "Er trad een fout op tijdens het kopieren van de URL.", + "ClientSettings": "Client instellingen", + "ButtonSplit": "Splitsen", + "BoxSet": "Box Set", + "AskAdminToCreateLibrary": "Vraag een beheerder om een bibliotheek te maken.", + "Artist": "Artiest", + "AllowFfmpegThrottlingHelp": "Wanneer een transcode of remux ver genoeg voorloopt op de huidige afspeelpositie, pauzeer het proces, zodat het minder middelen verbruikt. Dit is vooral handig wanneer u kijkt zonder vaak te zoeken. Schakel dit uit als u afspeelproblemen ondervindt.", + "AllowFfmpegThrottling": "Throttle Transcodes", + "LabelPlayerDimensions": "Afspeellengte:", + "LabelLibraryPageSizeHelp": "Kies het aantal artikelen dat wordt weergegeven op een bibliotheekpagina. Kies 0 om dit te verbergen.", + "LabelLibraryPageSize": "Bibliotheekpagina grootte:", + "LabelDroppedFrames": "Vervallen beelden:", + "LabelDeinterlaceMethod": "Deinterlacing methode:", + "LabelCorruptedFrames": "Corrupte beelden:", + "HeaderNavigation": "Navigeren", + "Episode": "Aflevering", + "Season": "Seizoen", + "ReleaseGroup": "Uitgave groep", + "PreferEmbeddedEpisodeInfosOverFileNames": "Verkies ingeladen afleveringsinformatie boven bestandsnaam", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Dit gebruikt de afleveringsinformatie van de ingeladen metadata als deze aanwezig is.", + "PlaybackErrorNoCompatibleStream": "Deze machine is niet leesbaar met de media en de server verstuurd geen leesbare media formaten.", + "Person": "Persoon", + "OtherArtist": "Andere Artiesten", + "OptionForceRemoteSourceTranscoding": "Forceer het transcoderen van op afstand bediende media bronnen (zoals LiveTV)", + "NoCreatedLibraries": "Het lijkt erop dat er geen bibliotheek is gecreëerd. {0}Wilt u er nu een aanmaken?{1}", + "Movie": "Film", + "MessageUnauthorizedUser": "U bent niet gemachtigd om toegang tot de server te krijgen op dit moment. Neem contact op met de server beheerder voor meer informatie.", + "MessageConfirmAppExit": "Wilt u afsluiten?", + "LabelVideoResolution": "Video resolutie:", + "LabelStreamType": "Stream type:", + "UnsupportedPlayback": "Jellyfin kan DRM beschermde inhoud niet ontsleutelen. Alle inhoud zal geprobeerd worden om te ontsleutelen inclusief beschermde titels. Sommige bestanden kunnen volledig zwart zijn vanwege de versleuteling of andere niet ondersteunde functies, zoals interactieve titels.", + "OnApplicationStartup": "Op het opstarten van de applicatie", + "EveryXHours": "Elke {0} uren", + "EveryHour": "Elk uur", + "EveryXMinutes": "Elke {0} minuten", + "OnWakeFromSleep": "Op het wakker worden vanuit slaapstand", + "WeeklyAt": "{0}s op {1}", + "DailyAt": "Dagelijks op {0}", + "LastSeen": "Laatst bekeken {0}", + "PersonRole": "als {0}", + "ListPaging": "{0}-{1} van de {2}", + "WriteAccessRequired": "De Jellyfin Server vereist schrijftoegang tot deze map. Zorg evoor dat Jellyfin schrijftoegang tot de map heeft en probeer opnieuw.", + "PathNotFound": "Het pad kan niet gevonden worden. Zorg ervoor dat het pad correct is en probeer opnieuw.", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "Track": "Nummer", + "SelectAdminUsername": "Selecteer een gebruikersnaam voor het beheerder account.", + "HeaderFavoritePlaylists": "Favoriete afspeellijsten", + "ButtonTogglePlaylist": "Afspeellijst", + "ButtonToggleContextMenu": "Meer", + "LabelRequireHttpsHelp": "Indien aangevinkt, zal de server alle verzoeken via HTTP automatisch omleiden naar HTTPS. Dit heeft geen effect als de server niet luistert op HTTPS.", + "EnableDetailsBanner": "Details Banner", + "MessageSyncPlayNoGroupsAvailable": "Geen groepen beschikbaar. Begin eerst iets te spelen.", + "EnableDetailsBannerHelp": "Toon een bannerafbeelding bovenaan de pagina met itemdetails.", + "TabDVR": "DVR", + "SyncPlayAccessHelp": "Selecteer het toegangsniveau dat deze gebruiker heeft tot de SyncPlay-functie. SyncPlay maakt het mogelijk om het afspelen met andere gebruikers te synchroniseren.", + "Filter": "Filter", + "New": "Nieuw", + "SaveChanges": "Wijzigingen opslaan", + "MessageSyncPlayErrorMedia": "Kan SyncPlay niet inschakelen! Media fout.", + "MessageSyncPlayErrorMissingSession": "Kan SyncPlay niet inschakelen! Ontbrekende sessie.", + "MessageSyncPlayErrorNoActivePlayer": "Geen actieve speler gevonden. SyncPlay is uitgeschakeld.", + "MessageSyncPlayErrorAccessingGroups": "Er is een fout opgetreden bij het openen van de groepslijst.", + "MessageSyncPlayLibraryAccessDenied": "Toegang tot deze inhoud is beperkt.", + "MessageSyncPlayJoinGroupDenied": "Toestemming vereist om SyncPlay te gebruiken.", + "MessageSyncPlayCreateGroupDenied": "Toestemming vereist om een groep te maken.", + "MessageSyncPlayGroupDoesNotExist": "Kan niet deelnemen aan de groep omdat deze niet bestaat.", + "MessageSyncPlayPlaybackPermissionRequired": "Afspeelrechten vereist.", + "MessageSyncPlayGroupWait": "{0} is aan het bufferen...", + "MessageSyncPlayUserLeft": "{0} i heeft de groep verlaten.", + "MessageSyncPlayUserJoined": "{0} is lid geworden van de groep.", + "MessageSyncPlayDisabled": "SyncPlay uitgeschakeld.", + "MessageSyncPlayEnabled": "SyncPlay ingeschakeld.", + "LabelSyncPlayAccess": "SyncPlay toegang", + "LabelSyncPlayAccessNone": "Uitgeschakeld voor deze gebruiker", + "LabelSyncPlayAccessJoinGroups": "Sta de gebruiker toe om groepen te maken", + "LabelSyncPlayAccessCreateAndJoinGroups": "Sta de gebruiker toe om groepen te maken en eraan deel te nemen", + "LabelSyncPlayLeaveGroupDescription": "SyncPlay uitschakelen", + "LabelSyncPlayLeaveGroup": "Groep verlaten", + "LabelSyncPlayNewGroupDescription": "Maak een nieuwe groep", + "LabelSyncPlayNewGroup": "Nieuwe groep", + "LabelSyncPlaySyncMethod": "Sync methode:", + "LabelSyncPlayPlaybackDiff": "Verschil in afspeeltijd:", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Tijd offset met de server:", + "LabelRequireHttps": "HTTPS verplichten", + "LabelNightly": "Nightly", + "LabelStable": "Stabiel", + "LabelChromecastVersion": "Chromecast versie", + "LabelEnableHttpsHelp": "Hiermee kan de server luisteren op de geconfigureerde HTTPS-poort. Hiervoor moet ook een geldig certificaat worden geconfigureerd.", + "LabelEnableHttps": "HTTPS inschakelen", + "HeaderSyncPlayEnabled": "SyncPlay ingeschakeld", + "HeaderSyncPlaySelectGroup": "Word lid van een groep", + "HeaderServerAddressSettings": "Server adres instellingen", + "HeaderRemoteAccessSettings": "Externe toegang instellingen", + "HeaderHttpsSettings": "HTTPS instellingen", + "HeaderDVR": "DVR", + "ApiKeysCaption": "Lijst met de momenteel ingeschakelde API-sleutels" } diff --git a/src/strings/pl.json b/src/strings/pl.json index 32e9a97980..f2aa4f9fa5 100644 --- a/src/strings/pl.json +++ b/src/strings/pl.json @@ -54,7 +54,7 @@ "BoxRear": "Pudełko (tył)", "Browse": "Przeglądaj", "BrowsePluginCatalogMessage": "Przejrzyj nasz katalog wtyczek żeby zobaczyć dostępne wtyczki.", - "BurnSubtitlesHelp": "Określa czy serwer powinien wypalać napisy podczas konwersji wideo, w zależności od formatu napisów. Unikanie wypalania napisów poprawia wydajność serwera. Wybierz Automatycznie, w celu wypalania zarówno napisów w formatach graficznych (np. VOBSUB, PGS, SUB/IDX, itp.), jak i pewnych napisów ASS/SSA.", + "BurnSubtitlesHelp": "Określa czy serwer powinien wypalać napisy podczas konwersji wideo, w zależności od formatu napisów. Unikanie wypalania napisów znacząco poprawia wydajność serwera. Wybierz Automatycznie, w celu wypalania zarówno napisów w formatach graficznych (np. VOBSUB, PGS, SUB/IDX), jak i pewnych napisów ASS lub SSA.", "ButtonAdd": "Dodaj", "ButtonAddMediaLibrary": "Dodaj media do biblioteki", "ButtonAddScheduledTaskTrigger": "Dodaj wyzwalacz", @@ -190,7 +190,7 @@ "DisplayInOtherHomeScreenSections": "Wyświetlaj na ekranie startowym sekcje Ostatnio dodane i Kontynuuj odtwarzanie", "DisplayMissingEpisodesWithinSeasons": "Wyświetlaj w sezonach brakujące odcinki", "DisplayMissingEpisodesWithinSeasonsHelp": "Ta opcja musi zostać dodatkowo aktywowana w bibliotece seriali, w konfiguracji serwera.", - "DisplayModeHelp": "Określa typ urządzenia, na którym uruchomiono Jellyfin.", + "DisplayModeHelp": "Wybierz styl układu interfejsu.", "DoNotRecord": "Nie nagrywaj", "Down": "W dół", "Download": "Pobierz", @@ -490,7 +490,7 @@ "Images": "Obrazy", "ImportFavoriteChannelsHelp": "Jeśli aktywne, tylko kanały oznaczone jako ulubione na tunerze, będą importowane.", "ImportMissingEpisodesHelp": "W przypadku aktywacji tej opcji, informacje o brakujących odcinkach zostaną zaimportowane do bazy Jellyfin i będą wyświetlane na listach sezonów i seriali. Może to jednak znacznie wydłużyć czas skanowania biblioteki.", - "InstallingPackage": "Instalowanie {0}", + "InstallingPackage": "Instalowanie {0} (wersja {1})", "InstantMix": "Szybki remiks", "ItemCount": "{0} pozycje", "Items": "Pozycje", @@ -953,16 +953,16 @@ "NoNextUpItemsMessage": "Nie znaleziono niczego. Zacznij oglądać swoje seriale!", "NoPluginConfigurationMessage": "Ta wtyczka nie ma żadnych ustawień.", "NoSubtitleSearchResultsFound": "Brak wyników wyszukiwania.", - "NoSubtitles": "Brak napisów", + "NoSubtitles": "Brak", "NoSubtitlesHelp": "Domyślnie napisy nie będą wczytywane. Można je ciągle włączyć ręcznie podczas odtwarzania.", "None": "Brak", "Normal": "Normalny", "NumLocationsValue": "{0} foldery", "Off": "Wyłączone", "OneChannel": "Jeden kanał", - "OnlyForcedSubtitles": "Tylko wymuszone napisy", + "OnlyForcedSubtitles": "Tylko wymuszone", "OnlyForcedSubtitlesHelp": "Tylko napisy oznaczone jako wymuszone będą wczytywane.", - "OnlyImageFormats": "Tylko formaty graficzne (VOBSUB, PGS, SUB, itp.)", + "OnlyImageFormats": "Tylko Formaty Graficzne (VOBSUB, PGS, SUB)", "OptionAdminUsers": "Administratorzy", "OptionAlbumArtist": "Wykonawca albumu", "OptionAllUsers": "Wszyscy użytkownicy", @@ -1100,7 +1100,7 @@ "OptionWeekly": "Cotygodniowo", "OriginalAirDateValue": "Data pierwszej emisji: {0}", "Overview": "Opis", - "PackageInstallCancelled": "Instalacja {0} anulowana.", + "PackageInstallCancelled": "Instalacja {0} (wersja {1}) anulowana.", "PackageInstallCompleted": "Instalacja {0} zakończona.", "PackageInstallFailed": "Instalacja {0} nieudana.", "ParentalRating": "Kategoria wiekowa", @@ -1466,6 +1466,20 @@ "NoCreatedLibraries": "Wygląda na to, że nie utworzyłeś jeszcze żadnych bibliotek. {0}Czy chcesz utworzyć jedną teraz?{1}", "LabelVideoResolution": "Rozdzielczość wideo:", "LabelStreamType": "Typ transmisji:", - "EnableFastImageFadeInHelp": "Włącz szybszą animację pojawiania się dla załadowanych obrazów", - "EnableFastImageFadeIn": "Szybkie pojawianie się obrazów" + "Artist": "Artysta", + "AlbumArtist": "Album artysty", + "Album": "Album", + "Person": "Osoba", + "OtherArtist": "Inny artysta", + "Movie": "Film", + "MessageUnauthorizedUser": "Nie masz dostępu do zasobów serwera. Skontaktuj się z administratorem sieci, aby uzyskać więcej informacji.", + "LabelLibraryPageSizeHelp": "Ustaw liczbę pozycji pokazywanych na stronie biblioteki. Ustaw 0, aby wyłączyć podział na strony.", + "LabelLibraryPageSize": "Rozmiar strony biblioteki:", + "LabelDeinterlaceMethod": "Metoda usuwania przeplotu:", + "HeaderFavoritePlaylists": "Ulubione Playlisty", + "Episode": "Odcinek", + "DeinterlaceMethodHelp": "Wybierz metodę usuwania przeplotu używaną podczas transkodowania.", + "ClientSettings": "Ustawienia klienta", + "ButtonTogglePlaylist": "Playlista", + "ButtonToggleContextMenu": "Więcej" } diff --git a/src/strings/pr.json b/src/strings/pr.json new file mode 100644 index 0000000000..91962aaa46 --- /dev/null +++ b/src/strings/pr.json @@ -0,0 +1,23 @@ +{ + "TabLogs": "Crow's Nest", + "HeaderAdmin": "Cap'n", + "WelcomeToProject": "Ahoy, matey! This be Jellyfin!", + "ButtonOk": "Aye", + "DisplayInMyMedia": "Show on the Poop Deck", + "HeaderCastAndCrew": "Mateys", + "HeaderMusicQuality": "Sea Shanty Strength", + "HeaderLatestMusic": "Latest Sea Shanties", + "FolderTypeMusic": "Sea Shanties", + "OptionBlockBooks": "Tall Tales", + "HeaderFavoriteBooks": "Fav'rit Tales", + "HeaderBooks": "Tall Tales", + "HeaderAudioBooks": "Spoken Tales", + "FolderTypeBooks": "Tall Tales", + "Books": "Tall Tales", + "LabelHomeNetworkQuality": "Sail strength:", + "Home": "Poop Deck", + "HeaderHome": "Poop Deck", + "DisplayInOtherHomeScreenSections": "Show on Poop Deck such as latest booty and continue plundering", + "ButtonHome": "Poop deck", + "HeaderCastCrew": "Mateys" +} diff --git a/src/strings/pt-br.json b/src/strings/pt-br.json index 45681762ca..b7948cb6a7 100644 --- a/src/strings/pt-br.json +++ b/src/strings/pt-br.json @@ -30,7 +30,7 @@ "AlwaysPlaySubtitlesHelp": "As legendas que combinarem com a preferência de idioma serão carregadas independente do idioma do áudio.", "AnyLanguage": "Qualquer idioma", "Anytime": "A qualquer momento", - "AroundTime": "Aproximadamente {0}", + "AroundTime": "Aproximadamente", "Art": "Arte", "Artists": "Artistas", "AsManyAsPossible": "Quantos forem possíveis", @@ -44,13 +44,13 @@ "BirthDateValue": "Nascimento: {0}", "BirthLocation": "Local de nascimento", "BirthPlaceValue": "Local de nascimento: {0}", - "BookLibraryHelp": "Livros de áudio e texto são suportados. Verifique o {0}guia de nomes de livros{1}.", + "BookLibraryHelp": "Livros de áudio e texto são suportados. Revise o {0}Guia de Nomes de Livros no Jellyfin{1}.", "Books": "Livros", "Box": "Caixa", "BoxRear": "Caixa (traseira)", "Browse": "Navegar", "BrowsePluginCatalogMessage": "Navegue pelo nosso catálogo de plugins para ver os plugins disponíveis.", - "BurnSubtitlesHelp": "Determina se o servidor deveria gravar as legendas no vídeo ao convertê-lo, dependendo do formato da legenda. Evitar a gravação da legenda irá melhorar a performance do servidor. Selecione Auto para gravar formatos de legenda baseados em imagem baseado nos formatos (ex. VOBSUB, PGS, SUB/IDX, etc.) e algumas legendas ASS/SSA.", + "BurnSubtitlesHelp": "Determina se o servidor deveria gravar as legendas no vídeo ao convertê-lo, dependendo do formato da legenda. Evitar a gravação da legenda irá melhorar a performance do servidor. Selecione Auto para gravar legendas baseados em imagem dos tipos (ex. VOBSUB, PGS, SUB/IDX, etc.) e algumas legendas ASS/SSA.", "ButtonAdd": "Adicionar", "ButtonAddMediaLibrary": "Adicionar Biblioteca de Mídia", "ButtonAddScheduledTaskTrigger": "Adicionar Disparador", @@ -182,7 +182,7 @@ "DisplayInOtherHomeScreenSections": "Exibir nas seções da tela inicial como mídia recente e continuar assistindo", "DisplayMissingEpisodesWithinSeasons": "Exibir episódios em falta nas temporadas", "DisplayMissingEpisodesWithinSeasonsHelp": "Isto também deve ser ativado para as bibliotecas de TV na configuração do servidor.", - "DisplayModeHelp": "Seleciona o tipo de tela para executar o Jellyfin.", + "DisplayModeHelp": "Selecione o estilo de layout que deseje para a interface.", "DoNotRecord": "Não gravar", "Down": "Baixo", "DrmChannelsNotImported": "Canais com DRM não serão importados.", @@ -218,7 +218,7 @@ "ErrorAddingTunerDevice": "Ocorreu um erro ao adicionar o sintonizador. Por favor, verifique se está acessível e tente novamente.", "ErrorAddingXmlTvFile": "Ocorreu um erro ao acessar o arquivo XmlTV. Por favor, verifique se o arquivo existe e tente novamente.", "ErrorDeletingItem": "Ocorreu um erro ao excluir o item do Servidor Jellyfin. Por favor, verifique se o Servidor Jellyfin possui acesso de gravação na pasta de mídia e tente novamente.", - "ErrorGettingTvLineups": "Ocorreu um erro ao baixar a programação da TV. Por favor, verifique se sua informação está correta e tente novamente.", + "ErrorGettingTvLineups": "Ocorreu um erro ao fazer download da programação de TV. Por favor, certifique-se que sua informação esteja correta e tente novamente.", "ErrorMessageStartHourGreaterThanEnd": "A hora final deve ser maior que a hora inicial.", "ErrorPleaseSelectLineup": "Por favor, selecione a programação e tente novamente. Se não houver programações disponíveis, verifique se o seu nome de usuário, senha e código postal estão corretos.", "ErrorSavingTvProvider": "Um erro ocorreu ao salvar o provedor de TV. Por favor, verifique se está acessível e tente novamente.", @@ -474,7 +474,7 @@ "Images": "Imagens", "ImportFavoriteChannelsHelp": "Se ativado, apenas canais que estão marcados como favoritos no sintonizador serão importados.", "ImportMissingEpisodesHelp": "Se ativado, as informações dos episódios que faltam serão importadas para seu banco de dados do Jellyfin e exibidas dentro das temporadas e séries. Isto pode causar lentidão no rastreamento da biblioteca.", - "InstallingPackage": "Instalando {0}", + "InstallingPackage": "Instalando {0} (versão{1})", "InstantMix": "Mix instântaneo", "ItemCount": "{0} itens", "Items": "itens", @@ -566,7 +566,7 @@ "LabelEmbedAlbumArtDidl": "Arte do álbum incorporada no Didl", "LabelEmbedAlbumArtDidlHelp": "Alguns dispositivos preferem este método para obter a arte do álbum. Outros podem falhar ao reproduzir com esta opção ativada.", "LabelEnableAutomaticPortMap": "Ativar mapeamento automático de portas", - "LabelEnableAutomaticPortMapHelp": "Tentativa de mapear automaticamente a porta pública para a porta local através de UPnP. Isto poderá não funcionar em alguns modelos de roteadores.", + "LabelEnableAutomaticPortMapHelp": "Tentar mapear automaticamente a porta pública para a porta local do seu servidor através de UPnP. Pode não funcionar em alguns modelos de roteadores. As mudanças não serão aplicadas até a reinicialização do servidor.", "LabelEnableBlastAliveMessages": "Mensagens ao vivo", "LabelEnableBlastAliveMessagesHelp": "Ative esta função se o servidor não for detectado por outros dispositivos UPnP em sua rede.", "LabelEnableDlnaClientDiscoveryInterval": "Intervalo para descoberta do cliente (segundos)", @@ -601,7 +601,7 @@ "LabelH264Crf": "CRF de codificação H264:", "LabelEncoderPreset": "Preset de codificação H264:", "LabelHardwareAccelerationType": "Aceleração de hardware:", - "LabelHardwareAccelerationTypeHelp": "Esta é uma função experimental disponível apenas em sistemas suportados.", + "LabelHardwareAccelerationTypeHelp": "Aceleração por hardware requer configurações adicionais.", "LabelHomeNetworkQuality": "Qualidade da rede local:", "LabelHomeScreenSectionValue": "Seção {0} da tela inicial:", "LabelHttpsPort": "Número da porta local de HTTPS:", @@ -626,7 +626,7 @@ "LabelKodiMetadataEnablePathSubstitution": "Ativar substituição de local", "LabelKodiMetadataEnablePathSubstitutionHelp": "Ativa a substituição do local das imagens usando as configurações de substituição de local do servidor.", "LabelKodiMetadataSaveImagePaths": "Salvar o local das imagens dentro dos arquivos nfo", - "LabelKodiMetadataSaveImagePathsHelp": "Isto é recomendado se os nomes dos arquivos de imagem não estão de acordo com as recomendações do Kodi.", + "LabelKodiMetadataSaveImagePathsHelp": "Isto é recomendado se os nomes dos arquivos de imagem não estão de acordo com as exigências do Kodi.", "LabelKodiMetadataUser": "Salvar informações do que o usuário assiste aos NFO's para:", "LabelKodiMetadataUserHelp": "Salva os dados para arquivos NFO para que outras aplicações possam usar.", "LabelLanNetworks": "Redes LAN:", @@ -637,7 +637,7 @@ "LabelLockItemToPreventChanges": "Bloquear este item para evitar alterações futuras", "LabelLoginDisclaimer": "Aviso legal no login:", "LabelLoginDisclaimerHelp": "Um aviso será exibido na parte inferior da página de login.", - "LabelManufacturer": "Fabricante", + "LabelManufacturer": "Fabricante:", "LabelManufacturerUrl": "URL do fabricante", "LabelMatchType": "Tipo de correspondência:", "LabelMaxBackdropsPerItem": "Número máximo de imagens de fundo por item:", @@ -662,7 +662,7 @@ "LabelMethod": "Método:", "LabelMinBackdropDownloadWidth": "Tamanho mínimo da imagem de fundo para download:", "LabelMinResumeDuration": "Duração mínima para retomar:", - "LabelMinResumeDurationHelp": "A menor duração de vídeo em segundos que salvará o local de reprodução e permitirá que retome.", + "LabelMinResumeDurationHelp": "Tempo mínimo do vídeo em segundos que permitirá continuar a reprodução a partir do ponto que parou.", "LabelMinResumePercentage": "Porcentagem mínima para retomar:", "LabelMinResumePercentageHelp": "Títulos são considerados como não reproduzidos se parados antes deste tempo.", "LabelMinScreenshotDownloadWidth": "Tamanho mínimo da captura de tela para download:", @@ -676,7 +676,7 @@ "LabelMoviePrefixHelp": "Se os títulos dos filmes devem ter um prefixo, digite-o aqui para que o servidor possa usá-lo corretamente.", "LabelMovieRecordingPath": "Local de gravação de filme (opcional):", "LabelMusicStreamingTranscodingBitrate": "Bitrate da transcodificação de músicas:", - "LabelMusicStreamingTranscodingBitrateHelp": "Define o bitrate máximo do streaming de músicas", + "LabelMusicStreamingTranscodingBitrateHelp": "Especifique uma taxa de bits máxima ao transmitir músicas.", "LabelName": "Nome:", "LabelNewName": "Novo nome:", "LabelNewPassword": "Nova senha:", @@ -688,7 +688,7 @@ "LabelNumberOfGuideDays": "Número de dias de dados do guia para baixar:", "LabelNumberOfGuideDaysHelp": "Baixar mais dias do guia da TV permite agendar com maior antecedência e visualizar mais listas, mas também levará mais tempo para baixar. Se selecionar Automático, será escolhido o período baseado no número de canais.", "LabelOptionalNetworkPath": "(Opcional) Pasta compartilhada em rede:", - "LabelOptionalNetworkPathHelp": "Se esta pasta estiver compartilhada em sua rede, fornecendo o local do compartilhamento em rede permitirá que os apps Jellyfin em outros dispositivos acessem arquivos de mídia diretamente.", + "LabelOptionalNetworkPathHelp": "Se esta pasta estiver compartilhada em sua rede, informar o caminho do compartilhamento permitirá que os apps Jellyfin em outros dispositivos acessem arquivos de mídia diretamente. Por exemplo, {0} ou {1}.", "LabelOriginalAspectRatio": "Proporção original da tela:", "LabelOriginalTitle": "Título original:", "LabelOverview": "Sinopse:", @@ -725,7 +725,7 @@ "LabelReasonForTranscoding": "Motivo da transcodificação:", "LabelRecord": "Gravar:", "LabelRecordingPath": "Local de gravação padrão:", - "LabelRecordingPathHelp": "Define o local padrão para salvar as gravações. Se deixar em branco, a pasta de dados do programa do servidor será usada.", + "LabelRecordingPathHelp": "Define o local padrão para salvar as gravações. Se deixar não preenchido, a pasta de dados do programa do servidor será usada.", "LabelRefreshMode": "Modo de atualização:", "LabelReleaseDate": "Data do lançamento:", "LabelRemoteClientBitrateLimit": "Limite do bitrate para streaming da internet (Mbps):", @@ -812,7 +812,7 @@ "LabelYoureDone": "Pronto!", "LabelZipCode": "CEP:", "LabelffmpegPath": "Local do FFmpeg:", - "LabelffmpegPathHelp": "O local para o arquivo de aplicação ffmpeg, ou pasta contendo ffmpeg.", + "LabelffmpegPathHelp": "O local para o programa ffmpeg, ou pasta contendo ffmpeg.", "LanNetworksHelp": "Lista separada por vírgula de endereços IP ou entradas IP/máscara de rede para redes que serão consideradas como redes locais ao forçar restrições de banda. Se definida, todos os outros endereços IP serão considerados como estando em uma rede externa e estarão sujeitos a restrições de banda externa. Se deixada em branco, apenas a sub-rede do servidor é considerada como rede local.", "Large": "Grande", "LatestFromLibrary": "Recentes {0}", @@ -867,7 +867,7 @@ "MessageDeleteTaskTrigger": "Deseja realmente excluir este disparador de tarefa?", "MessageDirectoryPickerBSDInstruction": "Para BSD, você precisará configurar o armazenamento dentro de seu Jail do FreeNAS para permitir que o Jellyfin tenha acesso a ele.", "MessageDirectoryPickerInstruction": "Os locais de rede podem ser inseridos manualmente caso o botão de rede falhe em localizar seus dispositivos. Por exemplo, {0} ou {1}.", - "MessageDirectoryPickerLinuxInstruction": "Para Linux no Arch Linux, CentOS, Debian, Fedora, openSUSE ou Ubuntu, você deve permitir que o usuário do serviço tenha ao menos acesso de leitura ao seu armazenamento.", + "MessageDirectoryPickerLinuxInstruction": "Sistemas operacionais Arch Linux, CentOS, Debian, Fedora, openSUSE ou Ubuntu, devem permitir que a conta de serviço tenha ao menos acesso de leitura nos locais de armazenamento.", "MessageDownloadQueued": "Download enfileirado.", "MessageEnablingOptionLongerScans": "Ativar esta opção pode resultar em rastreamentos de biblioteca significativamente mais demorados.", "MessageFileReadError": "Ocorreu um erro ao ler o arquivo. Por favor, tente novamente.", @@ -898,7 +898,7 @@ "MessageYouHaveVersionInstalled": "Você possui a versão {0} instalada.", "Metadata": "Metadados", "MetadataManager": "Gerenciador de Metadados", - "MetadataSettingChangeHelp": "Alterar as configurações dos metadados afetará o novo conteúdo que será adicionado. Para atualizar o conteúdo existente, abra a tela de detalhes e clique no botão de atualizar ou atualize usando o gerenciador de metadados.", + "MetadataSettingChangeHelp": "Alterar as configurações dos metadados afetará o novo conteúdo que será adicionado. Para atualizar o conteúdo existente, abra a tela de detalhes e clique no botão de atualizar, ou atualize usando o gerenciador de metadados.", "MinutesAfter": "minutos após", "MinutesBefore": "minutos antes", "Mobile": "Celular", @@ -924,9 +924,9 @@ "No": "Não", "NoNewDevicesFound": "Nenhum novo dispositivo encontrado. Para adicionar um novo sintonizador, feche esta mensagem e digite as informações do dispositivo manualmente.", "NoNextUpItemsMessage": "Nada encontrado. Comece a assistir suas séries!", - "NoPluginConfigurationMessage": "Este plugin não tem configurações disponíveis.", + "NoPluginConfigurationMessage": "Este plugin não permite alterar configurações.", "NoSubtitleSearchResultsFound": "Nenhum resultado encontrado.", - "NoSubtitles": "Nenhuma legenda", + "NoSubtitles": "Não há legendas", "NoSubtitlesHelp": "Legendas não serão carregadas por padrão. Elas podem ser carregadas manualmente durante a reprodução.", "None": "Nenhum(a)", "NumLocationsValue": "{0} pastas", @@ -949,10 +949,10 @@ "OptionAllowMediaPlaybackTranscodingHelp": "Restringir o acesso à transcodificação pode ocasionar falhas na reprodução nos apps do Jellyfin devido a formatos de mídias não suportados.", "OptionAllowRemoteControlOthers": "Permitir controle remoto de outros usuários", "OptionAllowRemoteSharedDevices": "Permitir controle remoto de dispositivos compartilhados", - "OptionAllowRemoteSharedDevicesHelp": "Dispositivos DLNA são considerados compartilhados até que um usuário comece a controlá-los.", + "OptionAllowRemoteSharedDevicesHelp": "Dispositivos DLNA são autorizados até que um usuário altere as permissões.", "OptionAllowSyncTranscoding": "Permitir download e sincronização de mídia que necessite de transcodificação", "OptionAllowUserToManageServer": "Permitir este usuário administrar o servidor", - "OptionAllowVideoPlaybackRemuxing": "Permitir reprodução de vídeos que requeiram conversão sem re-encodação", + "OptionAllowVideoPlaybackRemuxing": "Permitir reprodução de vídeos que requerem conversão sem recodificar", "OptionAllowVideoPlaybackTranscoding": "Permitir reprodução de vídeo que necessite de transcodificação", "OptionArtist": "Artista", "OptionAscending": "Crescente", @@ -976,15 +976,15 @@ "OptionDescending": "Decrescente", "OptionDisableUser": "Desativar este usuário", "OptionDisableUserHelp": "Se desativado, o servidor não permitirá nenhuma conexão deste usuário. Conexões existentes serão encerradas imediatamente.", - "OptionDislikes": "Não Curtidos", - "OptionDisplayFolderView": "Exibe uma visualização de pasta para exibir pastas de mídias", - "OptionDisplayFolderViewHelp": "Exibe pastas ao lado de suas outras biblioteca de mídia. Isto pode ser útil se quiser uma visualização por pasta.", + "OptionDislikes": "Não gostei", + "OptionDisplayFolderView": "Exibe visualização em pastas para exibir pastas de mídias", + "OptionDisplayFolderViewHelp": "Exibição em pastas ao lado das biblioteca de mídia. Isto pode ser útil para visualizar por pastas.", "OptionDownloadArtImage": "Arte", "OptionDownloadBackImage": "Traseira", "OptionDownloadBoxImage": "Caixa", "OptionDownloadDiscImage": "Disco", "OptionDownloadImagesInAdvance": "Fazer download de imagens antecipadamente", - "OptionDownloadImagesInAdvanceHelp": "Por padrão, a maioria das imagens são baixadas só quando um app Jellyfin solicita. Ativando esta opção, baixará todas as imagens antecipadamente, assim que novas mídias são importadas. Isto pode ocasionar um tempo maior para escanear a biblioteca.", + "OptionDownloadImagesInAdvanceHelp": "Por padrão, a maioria das imagens são baixadas somente quando um app Jellyfin solicita. Ativar esta opção, baixará todas as imagens antecipadamente, ao importar novas mídias. Isto pode ocasionar um tempo maior para escanear a biblioteca.", "OptionDownloadPrimaryImage": "Principal", "OptionDownloadThumbImage": "Miniatura", "OptionDvd": "DVD", @@ -1021,7 +1021,7 @@ "OptionMissingEpisode": "Episódios em Falta", "OptionMonday": "Segunda-feira", "OptionNameSort": "Nome", - "OptionNew": "Novo...", + "OptionNew": "Novo…", "OptionNone": "Nenhum", "OptionOnAppStartup": "Ao iniciar a aplicação", "OptionOnInterval": "Em um intervalo", @@ -1030,7 +1030,7 @@ "OptionPlainStorageFoldersHelp": "Se ativado, todas as pastas são representadas no DIDL como \"object.container.storageFolder\" ao invés de um tipo mais específico como, por exemplo, \"object.container.person.musicArtist\".", "OptionPlainVideoItems": "Exibir todos os vídeos como itens de vídeo", "OptionPlainVideoItemsHelp": "Se ativado, todos os vídeos são representados no DIDL como \"object.item.videoItem\" ao invés de um tipo mais específico como, por exemplo, \"object.item.videoItem.movie\".", - "OptionPlayCount": "Número de Reproduções", + "OptionPlayCount": "Contagem de Reproduções", "OptionPlayed": "Reproduzido", "OptionPremiereDate": "Data da Estréia", "OptionProfileAudio": "Áudio", @@ -1039,7 +1039,7 @@ "OptionProfileVideoAudio": "Áudio do Vídeo", "OptionReleaseDate": "Data de Lançamento", "OptionReportByteRangeSeekingWhenTranscoding": "Reportar que o servidor suporta busca de byte quando transcodificar", - "OptionReportByteRangeSeekingWhenTranscodingHelp": "Isto é necessário para alguns dispositivos que não buscam o tempo muito bem.", + "OptionReportByteRangeSeekingWhenTranscodingHelp": "Isto é necessário para avançar ou retroceder o tempo em alguns dispositivos.", "OptionRequirePerfectSubtitleMatch": "Fazer download apenas de legendas que correspondam exatamente aos meus arquivos de vídeo", "OptionRequirePerfectSubtitleMatchHelp": "Ao solicitar uma combinação perfeita, filtrará as legendas para incluir somente aquelas que foram testadas e verificadas com o arquivo de vídeo. Ao desmarcar isto, aumentará a quantidade de legendas baixadas, mas aumentará as chances de ter legendas que não estejam sincronizadas.", "OptionResElement": "elemento res", @@ -1063,9 +1063,9 @@ "OptionWeekly": "Semanal", "OriginalAirDateValue": "Data de exibição original: {0}", "Overview": "Sinopse", - "PackageInstallCancelled": "Instalação de {0} cancelada.", - "PackageInstallCompleted": "Instalação de {0} concluída.", - "PackageInstallFailed": "Instalação de {0} falhou.", + "PackageInstallCancelled": "Instalação da versão {0} cancelada.", + "PackageInstallCompleted": "Instalação da versão {0} concluída.", + "PackageInstallFailed": "A Instalação da versão {0} falhou.", "ParentalRating": "Classificação etária", "PasswordMatchError": "A senha e a confirmação da senha devem ser iguais.", "PasswordResetComplete": "A senha foi redefinida.", @@ -1121,7 +1121,7 @@ "RefreshMetadata": "Atualizar metadados", "RefreshQueued": "Atualização enfileirada.", "ReleaseDate": "Data de lançamento", - "RememberMe": "Lembre-me", + "RememberMe": "Lembrar-me", "RemoveFromCollection": "Remover da coletânea", "RemoveFromPlaylist": "Remover da lista de reprodução", "Repeat": "Repetir", @@ -1265,7 +1265,7 @@ "Up": "Para cima", "Upload": "Carregar", "UserAgentHelp": "Fornece um cabeçalho HTTP personalizado para o user-agent.", - "UserProfilesIntro": "O Jellyfin inclui suporte para perfis de usuários com configurações precisas de exibição, estado de reprodução e controle dos pais.", + "UserProfilesIntro": "O Jellyfin inclui suporte para perfis de usuário com configurações de exibição granular, estado de reprodução e controle dos pais.", "ValueAlbumCount": "{0} álbuns", "ValueAudioCodec": "Codec de Áudio: {0}", "ValueConditions": "Condições: {0}", @@ -1343,17 +1343,17 @@ "OptionAuto": "Automático", "AuthProviderHelp": "Seleciona um provedor de autenticação que será usado para autenticar a senha do usuário.", "HeaderFavoriteMovies": "Filmes Favoritos", - "HeaderFavoriteShows": "Séries Favoritas", - "HeaderFavoriteEpisodes": "Episódios Favoritos", + "HeaderFavoriteShows": "Séries favoritas", + "HeaderFavoriteEpisodes": "Episódios favoritos", "HeaderFavoriteAlbums": "Álbuns Favoritos", - "HeaderFavoriteArtists": "Artistas Favoritos", - "HeaderFavoriteSongs": "Músicas Favoritas", - "HeaderFavoriteVideos": "Vídeos Favoritos", + "HeaderFavoriteArtists": "Artistas favoritos", + "HeaderFavoriteSongs": "Músicas favoritas", + "HeaderFavoriteVideos": "Videos favoritos", "HeaderHome": "Inicio", - "HeaderRestartingServer": "Reiniciando Servidor", - "LabelAuthProvider": "Provedor de Autenticação:", + "HeaderRestartingServer": "Reiniciando servidor", + "LabelAuthProvider": "Provedor de autenticação:", "LabelServerName": "Nome do servidor:", - "LabelTranscodePath": "Local de transcodificação:", + "LabelTranscodePath": "Pasta de transcodificação:", "LabelTranscodes": "Transcodificação:", "LabelUserLoginAttemptsBeforeLockout": "Tentativas de login com falha antes que o usuário seja bloqueado:", "DashboardVersionNumber": "Versão: {0}", @@ -1361,7 +1361,7 @@ "DashboardOperatingSystem": "Sistema Operacional: {0}", "DashboardArchitecture": "Arquitetura: {0}", "LabelPasswordResetProvider": "Provedor para Redefinir a Senha:", - "LabelWeb": "Web: ", + "LabelWeb": "Web:", "OptionBluray": "Blu-ray", "LabelProfileContainer": "Formato:", "LabelTranscodingContainer": "Formato:", @@ -1392,7 +1392,7 @@ "OptionIsSD": "SD", "OptionList": "Lista", "OptionLoginAttemptsBeforeLockout": "Determinar quantas tentativas de logins incorretas podem ser feitas antes de ocorrer o bloqueio.", - "OptionLoginAttemptsBeforeLockoutHelp": "Um valor de zero significa herdar o padrão de três tentativas para usuários normais e cinco para administradores. Configurar para -1 desativará o recurso.", + "OptionLoginAttemptsBeforeLockoutHelp": "O valor zero significa herdar o padrão de três tentativas para usuários normais e cinco para administradores. Configurar para -1 desativará o recurso.", "OptionPoster": "Poster", "OptionPosterCard": "Cartaz", "OptionProtocolHls": "Streaming ao Vivo por HTTP", @@ -1434,7 +1434,7 @@ "LabelPlayMethod": "Método de Reprodução:", "LabelPlayer": "Reprodutor:", "LabelFolder": "Pasta:", - "LabelBaseUrlHelp": "Você pode adicionar um subdiretório aqui para acessar o servidor de uma única URL.", + "LabelBaseUrlHelp": "Você pode adicionar um subdiretório aqui para acessar o servidor de uma única URL. Por exemplo:http://exemplo.com/<baseurl>", "LabelBaseUrl": "URL Base:", "LabelBitrate": "Bitrate:", "LabelAudioSampleRate": "Taxa de amostragem do áudio:", @@ -1456,19 +1456,99 @@ "MessageConfirmAppExit": "Você quer sair?", "LabelVideoResolution": "Resolução de vídeo:", "LabelStreamType": "Tipo de stream:", - "EnableFastImageFadeIn": "Fade-in rápido da imagem", "LabelPlayerDimensions": "Dimensões do player:", "LabelCorruptedFrames": "Quadros corrompidos:", "HeaderNavigation": "Navegação", "CopyStreamURLError": "Houve um erro ao copiar a URL.", "ButtonSplit": "Dividir", "AskAdminToCreateLibrary": "Peça a um administrador para criar uma biblioteca.", - "AllowFfmpegThrottling": "Limitar transcodificação", - "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", + "AllowFfmpegThrottling": "Transcodes do Acelerador", + "PlaybackErrorNoCompatibleStream": "Este cliente não é compatível com a media e o servidor não está enviando um formato de mídia compatível.", "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 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" + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferir informações dos episódios incorporadas nos arquivos ao invés dos nomes", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Esta opção habilita as informações dos episódios incorporadas nos metadados dos arquivos(se estiverem disponíveis).", + "ClientSettings": "Configurações do cliente", + "OnApplicationStartup": "Na inicialização do aplicativo", + "EveryXHours": "A cada {0} horas", + "EveryHour": "A cada hora", + "EveryXMinutes": "A cada {0} minutos", + "OnWakeFromSleep": "Ao acordar da suspensão", + "WeeklyAt": "{0} às {1}", + "DailyAt": "Diariamente à {0}", + "LastSeen": "Visto pela última vez {0}", + "PersonRole": "como {0}", + "ListPaging": "{0}-{1} de {2}", + "WriteAccessRequired": "O servidor Jellyfin necessita de acesso de escrita para essa pasta. Garanta o acesso e tente novamente.", + "PathNotFound": "O caminho não pôde ser encontrado. Por favor certifique-se da validade e tente novamente.", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "Track": "Trilha", + "Season": "Temporada", + "ReleaseGroup": "Grupo de Lançamento", + "Person": "Pessoa", + "OtherArtist": "Outro Artista", + "Movie": "Filme", + "LabelLibraryPageSizeHelp": "Selecione a quantidade de itens a aparecer na página da biblioteca. Coloque 0 para desabilitar a paginação.", + "LabelLibraryPageSize": "Tamanho da página da biblioteca:", + "LabelDeinterlaceMethod": "Método de desentrelaçamento:", + "Episode": "Episódio", + "DeinterlaceMethodHelp": "Selecione o método de desentrelaçamento a ser usado ao transcodificar o conteúdo entrelaçado.", + "BoxSet": "Coleção", + "Artist": "Artista", + "AlbumArtist": "Artista do Album", + "Album": "Album", + "UnsupportedPlayback": "O Jellyfin não pode descriptografar conteúdo protegido por DRM, porém mesmo assim fará uma tentativa para todo tipo de conteúdo, incluindo títulos protegidos. A imagem de alguns arquivos pode aparecer completamente preta devido a criptografia ou outros recursos não suportados, como títulos interativos.", + "MessageUnauthorizedUser": "Você não está autorizado a acessar o servidor neste momento. Por favor, contate o administrador do servidor para mais informações.", + "ButtonTogglePlaylist": "Playlist", + "ButtonToggleContextMenu": "Mais", + "Filter": "Filtro", + "New": "Novo", + "HeaderFavoritePlaylists": "Playlists Favoritas", + "ApiKeysCaption": "Lista de chaves API ativadas no momento", + "TabDVR": "DVR", + "SaveChanges": "Salvar mudanças", + "LabelRequireHttpsHelp": "Se selecionado, o servidor vai automaticamente redirecionar todas as solicitações HTTP para HTTPS. Isso não terá efeito se o servidor não estiver escutando HTTPS.", + "LabelRequireHttps": "Necessita HTTPS", + "LabelNightly": "Nightly", + "LabelStable": "Estável", + "LabelChromecastVersion": "Versão do Chromecast", + "LabelEnableHttpsHelp": "Habilita que o servidor escute na porta HTTPS configurada. Um certificado válido também deve ser configurado para que isso entre em vigor.", + "LabelEnableHttps": "Habilitar HTTPS", + "HeaderServerAddressSettings": "Configurações da localização do servidor", + "HeaderRemoteAccessSettings": "Configurações de acesso remoto", + "HeaderHttpsSettings": "Configurações HTTPS", + "HeaderDVR": "DVR", + "LabelSyncPlayTimeOffset": "Diferença de tempo com o servidor:", + "SyncPlayAccessHelp": "Selecione o nível de acesso desse usuário aos recursos do SyncPlay. SyncPlay habilita a reprodução sincronizada com outros usuários.", + "MessageSyncPlayErrorMedia": "Falha ao ativar SyncPlay! Erro de mídia.", + "MessageSyncPlayErrorMissingSession": "Falha ao ativar SyncPlay! Sessão em falta.", + "MessageSyncPlayErrorNoActivePlayer": "Nenhum reprodutor ativo encontrado. SyncPlay foi desativado.", + "MessageSyncPlayErrorAccessingGroups": "Ocorreu um erro ao acessar a lista de grupos.", + "MessageSyncPlayLibraryAccessDenied": "O acesso a esse conteúdo é restrito.", + "MessageSyncPlayJoinGroupDenied": "Permissão necessária para usar SyncPlay.", + "MessageSyncPlayCreateGroupDenied": "Permissão necessária para criar um grupo.", + "MessageSyncPlayGroupDoesNotExist": "Falha ao participar de grupo pois o mesmo não existe.", + "MessageSyncPlayPlaybackPermissionRequired": "É necessária permissão de reprodução.", + "MessageSyncPlayNoGroupsAvailable": "Nenhum grupo disponível. Comece a reproduzir algo primeiro.", + "MessageSyncPlayGroupWait": "{0} está carregando...", + "MessageSyncPlayUserLeft": "{0} deixou o grupo.", + "MessageSyncPlayUserJoined": "{0} se juntou ao grupo.", + "MessageSyncPlayDisabled": "SyncPlay desativado.", + "MessageSyncPlayEnabled": "SyncPlay ativado.", + "LabelSyncPlayAccess": "Acesso ao SyncPlay", + "LabelSyncPlayAccessNone": "Desativado para esse usuário", + "LabelSyncPlayAccessJoinGroups": "Permitir que o usuário participe de grupos", + "LabelSyncPlayAccessCreateAndJoinGroups": "Permitir que o usuário crie e participe em grupos", + "LabelSyncPlayLeaveGroupDescription": "Desativar SyncPlay", + "LabelSyncPlayLeaveGroup": "Deixar grupo", + "LabelSyncPlayNewGroupDescription": "Criar novo grupo", + "LabelSyncPlayNewGroup": "Novo grupo", + "LabelSyncPlaySyncMethod": "Método de sincronização:", + "LabelSyncPlayPlaybackDiff": "Diferença no tempo de reprodução:", + "MillisecondsUnit": "ms", + "HeaderSyncPlayEnabled": "SyncPlay ativado", + "HeaderSyncPlaySelectGroup": "Entrar em um grupo", + "EnableDetailsBanner": "Banner de detalhes", + "EnableDetailsBannerHelp": "Exibe um banner na parte superior da página de detalhes do item." } diff --git a/src/strings/pt-pt.json b/src/strings/pt-pt.json index d4a81580f2..981b7a7b41 100644 --- a/src/strings/pt-pt.json +++ b/src/strings/pt-pt.json @@ -1428,7 +1428,7 @@ "LabelXDlnaDoc": "X-DLNA doc:", "LabelXDlnaCap": "X-DLNA cap:", "LabelVaapiDeviceHelp": "Este é o nó de renderização usado para aceleração de hardware.", - "LabelVaapiDevice": "VA API Dispositivo:", + "LabelVaapiDevice": "Dispositivo VAAPI:", "LabelTypeMetadataDownloaders": "{0} transferências de metadados:", "LabelTheme": "Tema:", "LabelTVHomeScreen": "TV modo ecrã de casa:", diff --git a/src/strings/pt.json b/src/strings/pt.json index 179c264aa4..7e263dbeb8 100644 --- a/src/strings/pt.json +++ b/src/strings/pt.json @@ -113,7 +113,7 @@ "Screenshot": "Captura de Ecrã", "Schedule": "Agendamentos", "ScanForNewAndUpdatedFiles": "Procurar ficheiros novos ou actualizados", - "SaveSubtitlesIntoMediaFoldersHelp": "Guardar ficheiros de legendas junto aos ficheiros vídeo facilita a gestão.", + "SaveSubtitlesIntoMediaFoldersHelp": "Salvar arquivos de legendas junto aos arquivos vídeo facilita o gerenciamento.", "SaveSubtitlesIntoMediaFolders": "Guardar legendas nas pastas multimédia", "Save": "Guardar", "Saturday": "Sábado", @@ -270,7 +270,7 @@ "LabelNewPassword": "Nova palavra-passe:", "LabelNewName": "Novo nome:", "LabelName": "Nome:", - "LabelMusicStreamingTranscodingBitrateHelp": "Defina a taxa máxima de transmissão de música", + "LabelMusicStreamingTranscodingBitrateHelp": "Especifique uma taxa de bits máxima ao transmitir músicas.", "LabelMusicStreamingTranscodingBitrate": "Taxa de transcodificação de música:", "LabelMovieRecordingPath": "Caminho para gravação de filmes (opcional):", "LabelMoviePrefixHelp": "Se aplicar um prefixo aos títulos dos filmes, introduza-o aqui para que o servidor consiga tratá-los corretamente.", @@ -307,7 +307,7 @@ "LabelMaxBackdropsPerItem": "Número máximo de imagens de fundo por item:", "LabelMatchType": "Tipo de correspondência:", "LabelManufacturerUrl": "URL do Fabricante", - "LabelManufacturer": "Fabricante", + "LabelManufacturer": "Fabricante:", "LabelLoginDisclaimerHelp": "Este aviso será mostrado na parte inferior da página de login.", "LabelLoginDisclaimer": "Aviso legal de login:", "LabelLockItemToPreventChanges": "Bloquear este item para evitar alterações futuras", @@ -404,12 +404,12 @@ "LabelDeathDate": "Data de falecimento:", "LabelRefreshMode": "Mode de actualização:", "LabelRecord": "Registo:", - "LabelPasswordResetProvider": "Provedor de actualização de palavra-passe", + "LabelPasswordResetProvider": "Provedor de redefinição de senha:", "LabelMetadataSaversHelp": "Escolha os formato em que deseja guardar os seus metadados.", "LabelMetadataReadersHelp": "Ordene as fontes locais de metadados por ordem de prioridade. O primeiro ficheiro a ser encontrado será lido.", "LabelMetadataReaders": "Leirores de metadados", "LabelMetadataDownloadersHelp": "Active e ordene os seus pesquisadores de metadados por ordem de prioridade. Pesquisadores com menor prioridade só serão utilizados para completar informação em falta.", - "LabelLogs": "Registos", + "LabelLogs": "Histórico:", "LabelKodiMetadataUserHelp": "Guardar dados de utilização em NFO para que outras aplicações os utilizem.", "LabelKodiMetadataUser": "Guardar dados de utilização em NFO para:", "LabelImageFetchersHelp": "Activar e ordenar os pesquisadores de imagens por ordem de preferência.", @@ -891,7 +891,7 @@ "EditImages": "Editar imagens", "Edit": "Editar", "EasyPasswordHelp": "O código PIN é utilizado para acesso off-line em clientes suportados e pode ser usado para um acesso fácil dentro da rede.", - "DropShadow": "Sombreado", + "DropShadow": "sombra projetada", "DrmChannelsNotImported": "Canais com proteção DRM não serão importados.", "DownloadsValue": "{0} transferências", "Download": "Transferir", @@ -914,7 +914,7 @@ "DirectPlaying": "Reprodução directa", "DeviceAccessHelp": "Apenas se aplica a dispositivos que podem ser identificados como únicos e que não impedem o acesso ao navegador. Filtrar o acesso a dispositivos a um utilizador, impede-o de utilizar novos dispositivos, até estes serem aprovados aqui.", "DetectingDevices": "Procurando dispositivos", - "Desktop": "Desktop", + "Desktop": "Área de Trabalho", "Descending": "Descendente", "Depressed": "Baixo relevo", "DeleteUserConfirmation": "Tem a certeza que deseja apagar este utilizador?", @@ -1079,7 +1079,7 @@ "BoxRear": "Caixa (verso)", "Box": "Caixa", "Books": "Livros", - "BookLibraryHelp": "Livros de texto e áudio são suportados. Consulte o guia de nomenclatura de livros{1}.", + "BookLibraryHelp": "Livros de texto e áudio são suportados. Consulte o guia {0} de nomenclatura de livros {1}.", "Blacklist": "Lista Negra", "BirthPlaceValue": "Local de nascimento: {0}", "BirthLocation": "Local de nascimento", @@ -1099,16 +1099,16 @@ "AnyLanguage": "Qualquer idioma", "Artists": "Artistas", "AsManyAsPossible": "Tantos quanto possível", - "AllowedRemoteAddressesHelp": "Lista de endereços IP ou IP/Máscara, separados por vírgulas, com permissão para se ligar remotamente. Se deixado em branco, todos os endereços remotos serão permitidos.", - "AllowRemoteAccessHelp": "Se esta opção não for seleccionada, todas as ligações remotas serão bloqueadas.", + "AllowedRemoteAddressesHelp": "Lista separada por vírgula de endereços IP ou entradas de máscara de IP/rede para redes que terão permissão para se conectar remotamente. Se deixado em branco, todos os endereços remotos serão permitidos.", + "AllowRemoteAccessHelp": "Se desmarcada, todas as conexões remotas serão bloqueadas.", "AllowRemoteAccess": "Permitir ligações remotas a este Servidor Jellyfin.", - "AllowOnTheFlySubtitleExtractionHelp": "Legendas integradas podem ser extraídas do vídeo e enviadas como texto simples para os clientes para evitar transcodificação. Em certos dispositivos, é uma operação demorada e pode causar paragens de reprodução durante o processo de extracção. Desactive esta opção para que as legendas sejam integradas no vídeo durante a conversão para um formato suportado pelo dispositivo de destino.", - "AllowOnTheFlySubtitleExtraction": "Permitir a extracção de legendas em tempo real", - "AllowHWTranscodingHelp": "Permitir o sintonizador converter emissões em tempo real. Poderá reduzir a necessidade do servidor converter o conteúdo.", + "AllowOnTheFlySubtitleExtractionHelp": "Legendas integradas podem ser extraídas do vídeo e enviadas como texto simples para os clientes de forma a evitar transcodificação. Em certos dispositivos, esta operação pode demorar algum tempo e causar paragens de reprodução durante o processo de extração. Desative esta opção para que as legendas sejam integradas no vídeo durante a conversão para um formato suportado pelo dispositivo de destino.", + "AllowOnTheFlySubtitleExtraction": "Permitir a extração de legendas em tempo real", + "AllowHWTranscodingHelp": "Permita que o sintonizador transcodifique os fluxos em tempo real. Isso pode ajudar a reduzir a transcodificação exigida pelo servidor.", "AllLibraries": "Todas as bibliotecas", "AllLanguages": "Todos os idiomas", "AllEpisodes": "Todos os episódios", - "AllComplexFormats": "Todos os formatos complexos (ASS, SSA, VOBSUB, PGS, SUB/IDX, etc.)", + "AllComplexFormats": "Todos os formatos complexos (ASS, SSA, VOBSUB, PGS, SUB, IDX, etc.)", "AllChannels": "Todos os canais", "All": "Todos", "Alerts": "Alertas", @@ -1119,14 +1119,14 @@ "AddedOnValue": "{0} foi adicionado", "AddToPlaylist": "Adicionar à lista de reprodução", "AddToPlayQueue": "Adicionar à fila de reprodução", - "AddToCollection": "Adicionar à colecção", - "AddItemToCollectionHelp": "Adicione itens às colecções pesquisando-os e utilizando o respetivo menu de toque ou clique direito para os adicionar a uma colecção.", + "AddToCollection": "Adicionar à coleção", + "AddItemToCollectionHelp": "Adicione itens às coleções pesquisando-os e utilizando o respetivo menu de toque ou clique direito para os adicionar a uma coleção.", "Add": "Adicionar", - "Actor": "Actor", - "AccessRestrictedTryAgainLater": "O acesso está actualmente restrito. Por favor, tente mais tarde.", + "Actor": "Ator", + "AccessRestrictedTryAgainLater": "O acesso está atualmente restrito. Por favor, tente mais tarde.", "Absolute": "Absoluto", - "AlwaysPlaySubtitlesHelp": "Legendas correspondentes à língua preferida serão sempre carregadas, independentemente do idioma do áudio.", - "SearchForMissingMetadata": "Procurar metadados em falta", + "AlwaysPlaySubtitlesHelp": "As legendas correspondentes à preferência de idioma serão carregadas, independentemente do idioma do áudio.", + "SearchForMissingMetadata": "Procurar metadados ausentes", "ScanLibrary": "Analisar biblioteca", "HeaderDeleteItem": "Remover item", "HeaderDeleteDevice": "Apagar Dispositivo", @@ -1175,7 +1175,7 @@ "HeaderActiveDevices": "Dispositivos Activos", "HeaderAccessScheduleHelp": "Crie uma restrição horária de acesso para limitar o acesso ao Jellyfin a determinadas horas.", "HeaderAccessSchedule": "Restrição Horária de Acesso", - "HardwareAccelerationWarning": "Activar a aceleração por hardware pode causar instabilidade em alguns ambientes. Garanta que o sistema operativo e os controladores da placa gráfica estão actualizados. Se tiver dificuldades em reproduzir vídeo depois de alterar esta opção, pode ser necessário repor a configuração \\\"None\\\".", + "HardwareAccelerationWarning": "A ativação da aceleração de hardware pode causar instabilidade em alguns ambientes. Verifique se o sistema operacional e os drivers de vídeo estão totalmente atualizados. Se você tiver dificuldade em reproduzir o vídeo depois de ativar isso, precisará alterar a configuração novamente para Nenhum.", "HandledByProxy": "Gerido pelo proxy inverso", "HDPrograms": "Programas HD", "EncoderPresetHelp": "Escolha um valor mais rápido para melhorar o desempenho, ou um valor mais lento para melhorar a qualidade.", @@ -1210,7 +1210,7 @@ "HeaderNavigation": "Navegar", "CopyStreamURLError": "Ocorreu um erro ao copiar o URL.", "ButtonSplit": "Dividir", - "AskAdminToCreateLibrary": "Peça a um administrador para criar a biblioteca.", + "AskAdminToCreateLibrary": "Peça a um administrador para criar uma biblioteca.", "AllowFfmpegThrottling": "Transcodificação com falhas", "DashboardOperatingSystem": "Sistema Operativo", "LabelUserLoginAttemptsBeforeLockout": "Número de tentativas de login falhadas antes do bloqueio do utilizador:", @@ -1227,14 +1227,14 @@ "MoreMediaInfo": "Informações sobre mídia", "MoreFromValue": "Mais de {0}", "MediaInfoRefFrames": "Quadros de referência", - "MediaInfoContainer": "Container", - "MediaInfoAnamorphic": "Anamorphic", + "MediaInfoContainer": "Recipiente", + "MediaInfoAnamorphic": "Anamórfico", "LabelVideoResolution": "Resolução do vídeo:", - "LabelTypeMetadataDownloaders": "{0} metadata downloaders:", + "LabelTypeMetadataDownloaders": "{0} download de metadados:", "LabelTranscodePath": "Caminho da transcodificação:", - "OnlyImageFormats": "Somente formatos de imagem (VOBSUB, PGS, SUB, etc)", + "OnlyImageFormats": "Somente formatos de imagem (VOBSUB, PGS, SUB)", "OnlyForcedSubtitlesHelp": "Apenas as legendas marcadas como forçadas serão carregadas.", - "OnlyForcedSubtitles": "Apenas legendas forçadas", + "OnlyForcedSubtitles": "Somente legendas forçadas", "Off": "Desligar", "NumLocationsValue": "{0} pastas", "Normal": "Normal", @@ -1276,10 +1276,10 @@ "Like": "Gostei", "LeaveBlankToNotSetAPassword": "Você pode deixar esse campo em branco para definir nenhuma senha.", "LearnHowYouCanContribute": "Aprenda como você pode contribuir.", - "LaunchWebAppOnStartupHelp": "Open the web client in your default web browser when the server initially starts. This will not occur when using the restart server function.", + "LaunchWebAppOnStartupHelp": "Abra o cliente da web no seu navegador da web padrão quando o servidor iniciar. Isso não ocorrerá ao usar a função de reinicialização do servidor.", "LaunchWebAppOnStartup": "Inicie a interface da web ao iniciar o servidor", "Large": "Amplo", - "LanNetworksHelp": "Comma separated list of IP addresses or IP/netmask entries for networks that will be considered on local network when enforcing bandwidth restrictions. If set, all other IP addresses will be considered to be on the external network and will be subject to the external bandwidth restrictions. If left blank, only the server's subnet is considered to be on the local network.", + "LanNetworksHelp": "Lista separada por vírgula de endereços IP ou entradas de máscara de rede/IP para redes que serão consideradas na rede local ao impor restrições de largura de banda. Se definido, todos os outros endereços IP serão considerados na rede externa e estarão sujeitos às restrições de largura de banda externa. Se deixado em branco, apenas a sub-rede do servidor é considerada na rede local.", "LabelffmpegPathHelp": "O caminho para o arquivo do aplicativo ffmpeg ou pasta que contém o ffmpeg.", "LabelffmpegPath": "FFmpeg caminho:", "LabelYear": "Ano:", @@ -1295,7 +1295,7 @@ "LabelVersion": "Versão:", "LabelVaapiDeviceHelp": "Este é o nó de renderização usado para aceleração de hardware.", "LabelVaapiDevice": "VA API Dispositivo:", - "LabelUserAgent": "Agente de usuário", + "LabelUserAgent": "Agente de usuário:", "LabelTranscodes": "Transcodificação:", "LabelTranscodingFramerate": "Transcodificação frame por segundo:", "LabelTranscodingProgress": "Progresso da transcodificação:", @@ -1311,11 +1311,9 @@ "LabelSpecialSeasonsDisplayName": "Nome de exibição da temporada especial:", "LabelSoundEffects": "Efeitos sonoros:", "LabelSortTitle": "Classificar título:", - "LabelSortOrder": "Ordem da Ordenação", + "LabelSortOrder": "Ordem de classificação:", "LabelSortBy": "Ordenar por:", - "LabelSkin": "Skin:", - "EnableFastImageFadeInHelp": "Habilite uma animação mais rápida para imagens carregadas", - "EnableFastImageFadeIn": "Efeito de imagem fade-in rápido", + "LabelSkin": "Pele:", "LabelRemoteClientBitrateLimitHelp": "Um limite opcional de taxa de bits por fluxo para todos os dispositivos fora da rede. Isso é útil para impedir que os dispositivos solicitem uma taxa de bits mais alta do que a sua conexão à Internet pode suportar. Isso pode resultar no aumento da carga da CPU no servidor para transcodificar vídeos em tempo real para uma taxa de bits mais baixa.", "LabelPlayerDimensions": "Dimensões do reprodutor:", "LabelParentNumber": "Número pai:", @@ -1326,5 +1324,99 @@ "ClientSettings": "Configurações do Cliente", "AllowFfmpegThrottlingHelp": "Quando uma transcodificação ou remux se aproximar da posição atual de reprodução, pause o processo para que consuma menos recursos. Isso é mais útil ao assistir sem procurar com frequência. Desative isso se você tiver problemas de reprodução.", "MySubtitles": "Minhas legendas", - "Name": "Nome" + "Name": "Nome", + "Never": "Nunca", + "Artist": "Artista", + "LabelDeinterlaceMethod": "Método de desentrelaçamento:", + "DeinterlaceMethodHelp": "Selecione o método de desentrelaçamento para converter conteúdo entrelaçado.", + "Movie": "Filme", + "LabelLibraryPageSize": "Tamanho da página da biblioteca:", + "Album": "Álbum", + "LabelLibraryPageSizeHelp": "Escolher a quantidade itens a exibir numa página de biblioteca. Escolha \"0\" para desabilitar a exibição em páginas.", + "Episode": "Episódio", + "OptionRequirePerfectSubtitleMatch": "Baixar apenas legendas que correspondem corretamente aos arquivos de vídeo", + "OptionRandom": "Aleatório", + "OptionPoster": "Encarte", + "OptionLoginAttemptsBeforeLockout": "Determinar a quantidade de tentativas de login incorretas até que ocorra bloqueio.", + "OptionList": "Listar", + "OptionIsSD": "Definição padrão", + "OptionIsHD": "Alta definição", + "OptionHomeVideos": "Fotos", + "OptionHasTrailer": "Trailer", + "OptionEnableExternalContentInSuggestions": "Habilitar sugestão de conteúdo externo", + "OptionDownloadMenuImage": "Menu", + "OptionDownloadLogoImage": "Logotipo", + "OptionDownloadBannerImage": "Encarte", + "OptionDisplayFolderViewHelp": "Exiba pastas ao lado de outras bibliotecas de mídia. Isso pode ser útil se você quiser ter uma visualização simples de pastas.", + "OptionDisplayFolderView": "Exibir uma exibição de pasta para mostrar pastas de mídia simples", + "OptionBluray": "Bluray", + "OptionBanner": "Poster", + "OptionAuto": "Automático", + "OptionAllowVideoPlaybackRemuxing": "Permitir execução de vídeo que requer conversão sem recodificar", + "OptionAllowLinkSharingHelp": "Somente páginas da web que contêm informações sobre mídia são compartilhadas. Os arquivos de mídia nunca são compartilhados publicamente. O tempo de compartilhamento é limitado e expira após {0} dias.", + "Option3D": "3D", + "NextUp": "Próximo", + "Next": "Próximo", + "NewEpisodesOnly": "apenas novos episódios", + "NewEpisodes": "Novos episódios", + "NewCollectionHelp": "Coleções permitem criar agrupamentos personalizados de filmes ou de outros conteúdos da biblioteca.", + "BoxSet": "Coleção", + "AlbumArtist": "Álbum do Artista", + "Quality": "Qualidade", + "Previous": "Anterior", + "PreferredNotRequired": "Preferível, mas não obrigatório", + "PictureInPicture": "vídeo destacado", + "OptionThumb": "Miniatura", + "OptionRequirePerfectSubtitleMatchHelp": "Solicitar a \"correspondência perfeita\" filtrará as legendas incluindo apenas aquelas que foram testadas com o arquivo de vídeo. Desmarcar isto aumentará a probabilidade de baixar legendas, mas poderá obter legendas incorretas ou não sincronizadas.", + "StopRecording": "Parar gravação", + "ShowYear": "Exibir ano", + "ShowTitle": "Exibir título", + "SettingsWarning": "Mudar estes valores pode causar instabilidade ou falhas de conexão. Se tiver problemas, recomendamos restaurar os valores originais.", + "ServerNameIsShuttingDown": "Servidor Jellyfin - {0} está desligando.", + "ServerNameIsRestarting": "Servidor Jellyfin - {0} está reiniciando.", + "SeriesYearToPresent": "{0} - Atualmente", + "SeriesCancelled": "Série cancelada.", + "SelectAdminUsername": "Por favor, selecione um usuário para a conta de administrador.", + "Season": "Temporada", + "RepeatEpisodes": "Repetir episódios", + "RepeatAll": "Repetir todos", + "RemoveFromCollection": "Remover da coleção", + "RememberMe": "Lembrar-me", + "ReleaseDate": "Data do lançamento", + "RefreshMetadata": "Atualizar metadados", + "RecentlyWatched": "Visto recentemente", + "OptionEnableForAllTuners": "Ativar para todos os dispositivos sintonizadores", + "OptionCaptionInfoExSamsung": "Informações da legenda (Samsung)", + "OptionBlockTrailers": "trechos de um filme", + "OptionAutomaticallyGroupSeriesHelp": "Se ativada, as séries espalhadas por várias pastas nesta biblioteca serão automaticamente mescladas em uma única série.", + "OptionAutomaticallyGroupSeries": "Mesclar automaticamente séries que estão espalhadas por várias pastas", + "OptionAllowSyncTranscoding": "Permitir download e sincronização de mídia que requeiram transcodificação", + "OptionForceRemoteSourceTranscoding": "Forçar a transcodificação de fontes de mídia remota (como LiveTV)", + "MessageUnauthorizedUser": "Você não está autorizado a acessar o servidor no momento. Entre em contato com o administrador do servidor para obter mais informações.", + "PreferEmbeddedTitlesOverFileNames": "Preferir títulos incorporados sobre nomes de arquivos", + "OptionSaveMetadataAsHiddenHelp": "Alterar isso será aplicado aos novos metadados salvos daqui para frente. Os arquivos de metadados existentes serão atualizados na próxima vez em que forem salvos pelo Jellyfin Server.", + "OptionRegex": "Regex", + "OptionLoginAttemptsBeforeLockoutHelp": "Um valor zero significa herdar o padrão de três tentativas para usuários normais e cinco para administradores. Definir como -1 desativará o recurso.", + "OptionExtractChapterImage": "Ativar extração de imagem de capítulo", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferir informações de episódios incorporados sobre nomes de arquivos", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Isso usa as informações do episódio dos metadados incorporados, se disponíveis.", + "PreferEmbeddedTitlesOverFileNamesHelp": "Isso determina o título quando nenhum metadado da Internet ou local está disponível.", + "PlaybackErrorNoCompatibleStream": "Este cliente não é compatível com a mídia e o servidor não está enviando um formato de mídia compatível.", + "Person": "Pessoa", + "OtherArtist": "Outro artista", + "OptionThumbCard": "Cartão de polegar", + "OptionPosterCard": "Cartão de pôster", + "LabelRequireHttpsHelp": "Se marcado, o servidor redirecionará automaticamente todas as solicitações por HTTP para HTTPS. Isso não terá efeito se o servidor não estiver escutando HTTPS.", + "LabelRequireHttps": "Requer HTTPS", + "LabelNightly": "À noite", + "LabelChromecastVersion": "Versão do Chromecast", + "LabelEnableHttpsHelp": "Permite que o servidor escute na postagem HTTPS configurada. Um certificado válido também deve ser configurado para que isso entre em vigor.", + "LabelEnableHttps": "Ativar HTTPS", + "HeaderServerAddressSettings": "Configurações de endereço do servidor", + "HeaderRemoteAccessSettings": "Configurações de acesso remoto", + "HeaderHttpsSettings": "Configurações HTTPS", + "HeaderDVR": "DVR", + "ApiKeysCaption": "Lista das chaves de API ativadas no momento", + "ButtonTogglePlaylist": "Lista de leitura", + "ButtonToggleContextMenu": "Mais" } diff --git a/src/strings/ro.json b/src/strings/ro.json index fd4927f4a1..3584741110 100644 --- a/src/strings/ro.json +++ b/src/strings/ro.json @@ -249,14 +249,14 @@ "ButtonStop": "Stop", "ButtonSubmit": "Trimite", "Collections": "Colecții", - "AllowRemoteAccess": "Permite conexiuni externe către serverul Jellyfin.", - "AllowRemoteAccessHelp": "Dacă este nebifat, toate conexiunile externe vor fi blocate.", + "AllowRemoteAccess": "Permite conexiuni externe către acest server Jellyfin.", + "AllowRemoteAccessHelp": "Dacă este neselectat, toate conexiunile externe vor fi blocate.", "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.", - "AroundTime": "Împrejur {0}", + "AroundTime": "Împrejur", "AsManyAsPossible": "Cât mai mulți cu putință", "Ascending": "Ascendent", "AspectRatio": "Raportul aspectului", @@ -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 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.", + "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", @@ -304,14 +304,14 @@ "Delete": "Șterge", "DeleteImage": "Șterge Imaginea", "DeleteUserConfirmation": "Sigur doriți să ștergeți acest utilizator?", - "Depressed": "Depresat", + "Depressed": "Deprimat", "Descending": "Descendent", "DetectingDevices": "Detectez dispozitive", "DirectPlaying": "Redare directă", "DirectStreamHelp2": "Transmiterea directă a unui fișier utilizează foarte puțină putere de procesare fără pierderi în calitatea video.", "DirectStreaming": "Transmitere directă", "Artists": "Artiști", - "BookLibraryHelp": "Cărți audio și text sunt suportate. Verifică {0}ghidul numirii cărților{1}.", + "BookLibraryHelp": "Cărți audio și text sunt suportate. Verifică {0} ghidul numirii cărților{1}.", "ButtonAddImage": "Adaugă Imagine", "ButtonArrowUp": "Sus", "ButtonAudioTracks": "Cale Audio", @@ -511,7 +511,7 @@ "GuestStar": "Vedeta invitata", "GuideProviderSelectListings": "Selectați Listări", "EncoderPresetHelp": "Alegeți o valoare mai rapidă pentru a îmbunătăți performanța sau o valoare mai lentă pentru a îmbunătăți calitatea.", - "HardwareAccelerationWarning": "Activarea accelerării hardware poate provoca instabilitate în anumite medii. Asigurați-vă că sistemul de operare și driverele video sunt complet actualizate. Dacă întâmpinați dificultăți pentru a reda video după activarea acestei opțiuni, va trebui să schimbați setarea la Nimic.", + "HardwareAccelerationWarning": "Activarea accelerării hardware poate provoca instabilitate în anumite medii. Asigurați-vă că sistemul de operare și driverele video sunt complet actualizate. Dacă întâmpinați dificultăți pentru a reda video după activarea acestei opțiuni, va trebui să schimbați setarea la inapoi la Nimic.", "HeaderAccessSchedule": "Program de Acces", "HeaderAccessScheduleHelp": "Creați un program de acces pentru a limita accesul la anumite ore.", "HeaderActiveDevices": "Dispozitive active", @@ -823,7 +823,7 @@ "LabelFailed": "Eșuat", "LabelExtractChaptersDuringLibraryScanHelp": "Generați imagini de capitol atunci când videoclipurile sunt importate în timpul scanării bibliotecii. În caz contrar, acestea vor fi extrase în timpul sarcinii programate de extragere a imaginilor capitolului, permițând scanarea bibliotecă obișnuită să se completeze mai rapid.", "LabelExtractChaptersDuringLibraryScan": "Extrageți imagini de capitol în timpul scanării bibliotecii", - "LabelBaseUrlHelp": "Puteți adăuga aici un subdirector personalizat pentru a accesa serverul de pe o adresă URL mai unică.", + "LabelBaseUrlHelp": "Adaugă aici un subdirector personalizat la URL-ul serverului. De exemplu: http://example.com/<baseurl>", "LabelBaseUrl": "Adresa URL de bază:", "LabelEveryXMinutes": "La fiecare:", "LabelEvent": "Eveniment:", @@ -1238,7 +1238,7 @@ "Repeat": "Repetă", "RemoveFromPlaylist": "Scoateți din lista de redare", "RemoveFromCollection": "Scoateți din colecție", - "RememberMe": "Ține-mă minte", + "RememberMe": "Ține-mă Minte", "ReleaseDate": "Data lansării", "RefreshQueued": "Actualizare adăugată în coadă.", "RefreshMetadata": "Actualizați metadatele", @@ -1334,7 +1334,7 @@ "OptionOnInterval": "La un interval", "OptionOnAppStartup": "La pornirea aplicației", "OptionNone": "Nici unul", - "OptionNew": "Nou...", + "OptionNew": "Nou…", "OptionMissingEpisode": "Episoade lipsă", "OptionMax": "Max", "OptionLoginAttemptsBeforeLockoutHelp": "O valoare zero înseamnă că va moșteni valoarea implicită de trei încercări pentru utilizatorii normali și cinci pentru administratori. Setarea acestei opțiuni la -1 va dezactiva funcția.", @@ -1359,9 +1359,9 @@ "OptionEnableExternalContentInSuggestions": "Activați conținut extern în sugestii", "OptionEmbedSubtitles": "Inclus în container", "OptionDownloadLogoImage": "Siglă", - "OptionDownloadImagesInAdvanceHelp": "În mod implicit, majoritatea imaginilor sunt descărcate numai la cererea unei aplicații din Jellyfin. Activați această opțiune pentru a descărca în prealabil toate imaginile, în timp ce fișierele media noi sunt importate. Acest lucru poate provoca scanări ale bibliotecii semnificativ mai lungi.", + "OptionDownloadImagesInAdvanceHelp": "În mod implicit, majoritatea imaginilor sunt descărcate numai la cererea unei aplicații din Jellyfin. Activați această opțiune pentru a descărca în prealabil toate imaginile, pe măsură ce fișierele media sunt importate. Acest lucru poate provoca scanări ale bibliotecii semnificativ mai lungi.", "OptionDownloadImagesInAdvance": "Descărcați imaginile în avans", - "OptionDownloadDiscImage": "Placă", + "OptionDownloadDiscImage": "Disc", "OptionDisplayFolderViewHelp": "Afișați dosarele alături de celelalte biblioteci media. Acest lucru poate fi util dacă doriți să aveți o vizualizare direct în dosar.", "OptionDisplayFolderView": "Afișați o vizualizare de dosar pentru a afișa dosarele media simple", "OptionDateAddedImportTime": "Utilizați data scanării în bibliotecă", @@ -1454,8 +1454,6 @@ "HeaderNavigation": "Navigare", "MessageConfirmAppExit": "Vrei să ieși?", "CopyStreamURLError": "A apărut o eroare la copierea adresei URL.", - "EnableFastImageFadeInHelp": "Activați animația mai rapidă de tranziție pentru imaginile încărcate", - "EnableFastImageFadeIn": "Tranziție a imaginii rapidă", "LabelVideoResolution": "Rezoluția video:", "LabelStreamType": "Tipul streamului:", "LabelPlayerDimensions": "Dimensiunile soft redare:", @@ -1465,13 +1463,13 @@ "NoCreatedLibraries": "Se pare că nu ați creat încă biblioteci. {0} Doriți să creați una acum? {1}", "AskAdminToCreateLibrary": "Cereți unui administrator să creeze o bibliotecă.", "PlaybackErrorNoCompatibleStream": "Clientul nu este compatibil cu formatul media, 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", + "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 util atunci când priviți fără a derula des. Dezactivați acestă opțiune dacă întâmpinați probleme de redare.", + "AllowFfmpegThrottling": "Limitare 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.", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferați informația despre episod încorporată în fișier decât numele fișierelor", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Aceasta folosește informația despre episod din metadatele încorporate, dacă sunt disponibile.", "Person": "Persoană", "OtherArtist": "Alt artist", "Movie": "Film", @@ -1493,8 +1491,63 @@ "ListPaging": "{0}-{1} din {2}", "WriteAccessRequired": "Jellyfin Server necesită acces de scriere la acest folder. Vă rugăm să vă asigurați accesul la scriere și încercați din nou.", "PathNotFound": "Calea nu a fost găsită. Vă rugăm să vă asigurați de validitatea căii și încercați din nou.", - "YadifBob": "Yadif Bob", - "Yadif": "Yadif", + "YadifBob": "Gigi Bob", + "Yadif": "YADIF", "LabelDeinterlaceMethod": "Metoda de intercalare:", - "DeinterlaceMethodHelp": "Selectați metoda de intercalat pe care să o utilizați la transcodarea conținutului intercalat." + "DeinterlaceMethodHelp": "Selectați metoda de intercalat pe care să o utilizați la transcodarea conținutului intercalat.", + "UnsupportedPlayback": "Jellyfin nu poate decripta conținut protejat de DRM, dar tot conținutul va fi încercat indiferent de titlurile protejate. Unele fișiere pot părea complet negre din cauza criptării sau a altor funcții neacceptate, cum ar fi titluri interactive.", + "LabelLibraryPageSizeHelp": "Setează cantitatea de elemente de afișat pe o pagină a bibliotecii. Setați la 0 pentru a dezactiva paginarea.", + "LabelLibraryPageSize": "Mărimea paginii Bibliotecă:", + "MessageUnauthorizedUser": "Nu sunteți autorizat să accesați serverul în acest moment. Vă rugăm să contactați administratorul serverului pentru mai multe informații.", + "ButtonTogglePlaylist": "Listă de redare", + "ButtonToggleContextMenu": "Mai mult", + "Filter": "Filtru", + "New": "Nou", + "HeaderFavoritePlaylists": "Listă Favorită", + "ApiKeysCaption": "Lista cheilor API active", + "LabelRequireHttpsHelp": "Dacă e selectat, serverul va redirecta automat toate cererile HTTP către HTTPS. Dacă nu se ascultă pe HTTPS, nu are niciun efect.", + "LabelRequireHttps": "Trebuie HTTPS", + "LabelNightly": "Ultimă", + "LabelStable": "Stabilă", + "LabelChromecastVersion": "Versiunea de Chromecast", + "LabelEnableHttpsHelp": "Activează serverul să asculte pe portul HTTPS configurat. Un certificat valid trebuie de asemenea configurat pentru ca să funcţioneze.", + "LabelEnableHttps": "Activați HTTPS", + "HeaderServerAddressSettings": "Setările adresei serverului", + "HeaderRemoteAccessSettings": "Setări pentru aces remote", + "HeaderHttpsSettings": "Setări https", + "TabDVR": "DVR", + "SaveChanges": "Salvează modificările", + "HeaderDVR": "DVR", + "SyncPlayAccessHelp": "Selectați nivelul de acces pe care îl are acest utilizator la funcția SyncPlay. SyncPlay permite sincronizarea redării cu alte dispozitive.", + "MessageSyncPlayErrorMedia": "Eroare la activarea SyncPlay! Eroare media.", + "MessageSyncPlayErrorMissingSession": "Eroare la activarea SyncPlay! Sesiune lipsă.", + "MessageSyncPlayErrorNoActivePlayer": "Nu a fost găsit niciun soft de redare activ. SyncPlay a fost dezactivat.", + "MessageSyncPlayErrorAccessingGroups": "A apărut o eroare la accesarea listei de grupuri.", + "MessageSyncPlayLibraryAccessDenied": "Accesul la acest conținut este restricționat.", + "MessageSyncPlayJoinGroupDenied": "Permisiune necesară pentru a utiliza SyncPlay.", + "MessageSyncPlayCreateGroupDenied": "Permisiune necesară pentru crearea unui grup.", + "MessageSyncPlayGroupDoesNotExist": "Nu a reușit să se alăture grupului, deoarece nu există.", + "MessageSyncPlayPlaybackPermissionRequired": "Este necesară permisiunea de redare.", + "MessageSyncPlayNoGroupsAvailable": "Nu există grupuri disponibile. Începe să redai ceva mai întâi.", + "MessageSyncPlayGroupWait": "{0} se încarcă...", + "MessageSyncPlayUserLeft": "{0} a părăsit grupul.", + "MessageSyncPlayUserJoined": "{0} s-a alăturat grupului.", + "MessageSyncPlayDisabled": "SyncPlay dezactivat.", + "MessageSyncPlayEnabled": "SyncPlay activat.", + "LabelSyncPlayAccess": "Acces SyncPlay", + "LabelSyncPlayAccessNone": "Dezactivat pentru acest utilizator", + "LabelSyncPlayAccessJoinGroups": "Permiteți utilizatorului să se alăture grupurilor", + "LabelSyncPlayAccessCreateAndJoinGroups": "Permiteți utilizatorului să creeze și să se alăture grupurilor", + "LabelSyncPlayLeaveGroupDescription": "Dezactivează SyncPlay", + "LabelSyncPlayLeaveGroup": "Parăsește grup", + "LabelSyncPlayNewGroupDescription": "Crează un grup nou", + "LabelSyncPlayNewGroup": "Grup nou", + "LabelSyncPlaySyncMethod": "Metoda de sincronizare:", + "LabelSyncPlayPlaybackDiff": "Diferența de timp de redare:", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Decalare de timp cu serverul:", + "HeaderSyncPlayEnabled": "SyncPlay activat", + "HeaderSyncPlaySelectGroup": "Alăturați-vă unui grup", + "EnableDetailsBannerHelp": "Afișați o imagine de bandou în partea de sus a paginii cu detalii ale articolului.", + "EnableDetailsBanner": "Detalii Bandou" } diff --git a/src/strings/ru.json b/src/strings/ru.json index d50bcaccaa..dcc3a0f5d6 100644 --- a/src/strings/ru.json +++ b/src/strings/ru.json @@ -3,19 +3,19 @@ "AccessRestrictedTryAgainLater": "В настоящее время доступ запрещён. Повторите попытку позже.", "Actor": "Актёр", "Add": "Добавить", - "AddItemToCollectionHelp": "Добавляйте элементы в коллекции проведя их поиск, и с помощью правого щелчка по ним или по касанию меню, чтобы присоединить ко коллекции.", + "AddItemToCollectionHelp": "Добавляйте элементы в коллекции, выполняя их поиск, и с помощью правой кнопки мыши или касания меню присоедините их к коллекции.", "AddToCollection": "Добавить в коллекцию", "AddToPlayQueue": "Добавить в очередь воспроизведения", "AddToPlaylist": "Добавить в плей-лист", "AddedOnValue": "Добавлено {0}", - "AdditionalNotificationServices": "Просмотрите каталог плагинов, чтобы установить дополнительные услуги для уведомлений.", + "AdditionalNotificationServices": "Просмотрите каталог плагинов, чтобы установить дополнительные сервисы уведомлений.", "AirDate": "Дата эфира", "Aired": "Эфирный", "Albums": "Альбомы", "Alerts": "Оповещения", "All": "Все", "AllChannels": "Все каналы", - "AllComplexFormats": "Все комлексные форматы (ASS, SSA, VOBSUB, PGS, SUB/IDX и т.д.)", + "AllComplexFormats": "Все комплексные форматы (ASS, SSA, VOBSUB, PGS, SUB, IDX и др.)", "AllEpisodes": "Все эпизоды", "AllLanguages": "Все языки", "AllLibraries": "Все медиатеки", @@ -23,38 +23,38 @@ "AllowMediaConversion": "Разрешить преобразование медиаданных", "AllowMediaConversionHelp": "Предоставить или запретить доступ к компоненте преобразования медиаданных.", "AllowOnTheFlySubtitleExtraction": "Разрешить динамическое извлечение субтитров", - "AllowOnTheFlySubtitleExtractionHelp": "Внедрённые субтитры возможно извлекать из видео и доставлять клиентам в виде обычного текста, в целях предотвращения перекодировки видео. На некоторых системах это может занять продолжительное время и вызывать задержки воспроизведения видео в процессе извлечения. Отключите это, для прошивки внедрённых субтитров во время перекодировки видео, при отсутствии встроенной поддержки их в клиентском устройстве.", - "AllowRemoteAccess": "Разрешение удалённого доступа к данному серверу Jellyfin Server.", + "AllowOnTheFlySubtitleExtractionHelp": "Встроенные субтитры могут быть извлечены из видео и доставлены клиентам в виде обычного текста, в целях предотвращения перекодировки видео. На некоторых системах это может занять продолжительное время и вызвать задержки воспроизведения видео в процессе извлечения. Отключите этот параметр, чтобы встроенные субтитры записывались при перекодировании видео, если они изначально не поддерживаются клиентским устройством.", + "AllowRemoteAccess": "Разрешить удалённый доступ к данному серверу Jellyfin Server.", "AllowRemoteAccessHelp": "Если флажок снят, то все удалённые подключения будут заблокированы.", - "AllowedRemoteAddressesHelp": "Список разделённых запятыми IP-адресов или записей IP/netmask для сетей, которым разрешено удалённое соединение. Если не заполнять, то будут использованы все внешние адреса.", - "AlwaysPlaySubtitles": "Всегда воспроизводить с субтитрами", + "AllowedRemoteAddressesHelp": "Список разделённых запятыми IP-адресов или записей IP/netmask сетей, которым разрешено удалённое соединение. Если оставить это поле пустым, то будут разрешены все удаленные адреса.", + "AlwaysPlaySubtitles": "Воспроизводить всегда", "AlwaysPlaySubtitlesHelp": "Субтитры, соответствующие настройке языка, будут загружаться независимо от языка аудио.", "AnyLanguage": "Любой язык", "Anytime": "В любое время", - "AroundTime": "Около {0}", + "AroundTime": "Около", "Art": "Виньетка", "Artists": "Исполнители", "AsManyAsPossible": "Как можно больше", "Ascending": "По возрастанию", - "AspectRatio": "Соот-ие сторон", + "AspectRatio": "Соотношение сторон", "AttributeNew": "Новинка", "Audio": "Аудио", "Auto": "Авто", "AutoBasedOnLanguageSetting": "Авто (на основе настройки языка)", - "Backdrop": "Задник", - "Backdrops": "Задники", + "Backdrop": "Фон", + "Backdrops": "Фоны", "Banner": "Баннер", "BirthDateValue": "Дата рождения: {0}", "BirthLocation": "Место рождения", "BirthPlaceValue": "Место рождения: {0}", "Blacklist": "Чёрный список", - "BookLibraryHelp": "Поддерживаются аудио и текстовые книги. Просмотрите {0}руководство по именованию книг{1}.", + "BookLibraryHelp": "Поддерживаются аудио и текстовые книги. Просмотрите {0} руководство по именованию книг {1}.", "Books": "Книги", "Box": "Коробка", - "BoxRear": "Спинка коробки", + "BoxRear": "Коробка (задняя часть)", "Browse": "Навигация", "BrowsePluginCatalogMessage": "Просмотрите каталог плагинов, чтобы ознакомиться с имеющимися плагинами.", - "BurnSubtitlesHelp": "Определяется, должен ли сервер внедрять субтитры при преобразовании видео в зависимости от формата субтитров. Избегание внедрения субтитров улучшит производительность сервера. Выберите «Авто» для записи основанных на графике форматов (VOBSUB, PGS, SUB/IDX и т.п.) и некоторых субтитров ASS/SSA.", + "BurnSubtitlesHelp": "Определяется, должен ли сервер внедрять субтитры при перекодировании. Избежание этого значительно улучшит производительность. Выберите «Авто» для записи основанных на графике форматов (VOBSUB, PGS, SUB, IDX и др.) и некоторых субтитров ASS или SSA.", "ButtonAdd": "Добавить", "ButtonAddMediaLibrary": "Добавить медиатеку", "ButtonAddScheduledTaskTrigger": "Добавить триггер", @@ -70,12 +70,12 @@ "ButtonChangeServer": "Сменить сервер", "ButtonConnect": "Подсоединиться", "ButtonDelete": "Удалить", - "ButtonDeleteImage": "Удалить рисунок", + "ButtonDeleteImage": "Удалить изображение", "ButtonDown": "Вниз", "ButtonDownload": "Загрузить", "ButtonEdit": "Править", - "ButtonEditImages": "Править рисунки", - "ButtonEditOtherUserPreferences": "Править профиль, рисунок и личные настройки этого пользователя.", + "ButtonEditImages": "Править изображения", + "ButtonEditOtherUserPreferences": "Править профиль, изображение и персональные настройки этого пользователя.", "ButtonFilter": "Фильтр", "ButtonForgotPassword": "Напомнить пароль", "ButtonFullscreen": "Полный экран", @@ -83,21 +83,21 @@ "ButtonGuide": "Телегид", "ButtonHelp": "Справка", "ButtonHome": "Главное", - "ButtonInfo": "Инфо...", + "ButtonInfo": "Инфо", "ButtonLearnMore": "Подробнее", "ButtonLibraryAccess": "Доступ к медиатеке", "ButtonManualLogin": "Войти вручную", "ButtonMore": "Ещё", "ButtonNetwork": "Сеть", "ButtonNew": "Новое", - "ButtonNextTrack": "След. дорожка", + "ButtonNextTrack": "Следующая дорожка", "ButtonOff": "Откл", "ButtonOk": "Ок", "ButtonOpen": "Открыть", - "ButtonParentalControl": "Управлять содержанием", + "ButtonParentalControl": "Родительский контроль", "ButtonPause": "Пауза", - "ButtonPlay": "Воспр.", - "ButtonPreviousTrack": "Пред. дорожка", + "ButtonPlay": "Воспроизведение", + "ButtonPreviousTrack": "Предыдущая дорожка", "ButtonProfile": "Профиль", "ButtonQuickStartGuide": "Руководство по запуску", "ButtonRefresh": "Обновить", @@ -127,6 +127,8 @@ "ButtonStop": "Остановить", "ButtonSubmit": "Подтвердить", "ButtonSubtitles": "Субтитры", + "ButtonToggleContextMenu": "Ещё", + "ButtonTogglePlaylist": "Плей-лист", "ButtonTrailer": "Трейлер", "ButtonUninstall": "Удалить", "ButtonUp": "Вверх", @@ -147,27 +149,27 @@ "CommunityRating": "Общественная оценка", "Composer": "Композитор", "ConfigureDateAdded": "Как конфигурировать дату добавления определяется в Панели Jellyfin Server в параметрах Медиатеки", - "ConfirmDeleteImage": "Удалить рисунок?", + "ConfirmDeleteImage": "Удалить изображение?", "ConfirmDeleteItem": "При удалении данного элемента, он удалится и из файловой системы, и из медиатеки. Вы действительно хотите продолжить?", "ConfirmDeleteItems": "При удалении данных элементов, он удалится и из файловой системы, и из медиатеки. Вы действительно хотите продолжить?", "ConfirmDeletion": "Подтверждение удаления", "ConfirmEndPlayerSession": "Вы хотите завершить работу Jellyfin на {0}?", - "Connect": "Подсоединиться", + "Connect": "Соединиться", "ContinueWatching": "Продолжение просмотра", "Continuing": "Продолжающееся", "CriticRating": "Оценка критиков", "CustomDlnaProfilesHelp": "Создайте настраиваемый профиль, назначаемый для нового устройства или переопределите системный профиль.", "DateAdded": "Дата добавления", "DatePlayed": "Дата воспроизведения", - "DeathDateValue": "Кончина: {0}", - "Default": "Умолчание", + "DeathDateValue": "Дата смерти: {0}", + "Default": "По умолчанию", "DefaultErrorMessage": "Произошла ошибка при обработке запроса. Повторите попытку позже.", "DefaultMetadataLangaugeDescription": "Они являются значениями по умолчанию и могут быть подстроены индивидуально для каждой медиатеки.", "DefaultSubtitlesHelp": "Загрузки субтитров определяются флагами \"По умолчанию\" и \"Форсированные\" во внедрённых метаданных. Языковые настройки учитываются при наличии нескольких опций.", "Delete": "Удалить", "DeleteDeviceConfirmation": "Вы действительно хотите удалить данное устройство? Оно появится снова в следующий раз, когда пользователь войдёт с него.", - "DeleteImage": "Удалить рисунок", - "DeleteImageConfirmation": "Вы действительно хотите удалить данный рисунок?", + "DeleteImage": "Удалить изображение", + "DeleteImageConfirmation": "Вы действительно хотите удалить данное изображение?", "DeleteMedia": "Удалить медиаданные", "DeleteUser": "Удалить пользователя", "DeleteUserConfirmation": "Вы действительно хотите удалить этого пользователя?", @@ -191,7 +193,7 @@ "DisplayInOtherHomeScreenSections": "Показывать в разделах главного экрана (нпр., новейшие медиаданные, продолжение просмотра и т.п.)", "DisplayMissingEpisodesWithinSeasons": "Отображать отсутствующие эпизоды в пределах сезонов", "DisplayMissingEpisodesWithinSeasonsHelp": "Это также должно быть включено для ТВ-медиатек в конфигурации сервера.", - "DisplayModeHelp": "Выберите тип экрана, где запущен Jellyfin.", + "DisplayModeHelp": "Выберите желательный стиль разметки для интерфейса.", "DoNotRecord": "Не записывать", "Down": "Вниз", "Download": "Загрузить", @@ -200,11 +202,11 @@ "DropShadow": "Теневая", "EasyPasswordHelp": "Простой PIN-код используется для автономного доступа на поддерживаемых клиентах и может также использоваться для удобного внутрисетевого входа.", "Edit": "Правка", - "EditImages": "Править рисунки", + "EditImages": "Править изображения", "EditMetadata": "Править метаданные", "EditSubtitles": "Править субтитры", - "EnableBackdrops": "Задники", - "EnableBackdropsHelp": "Задники будут отображаются фоном на некоторых страницах при просмотре медиатеки.", + "EnableBackdrops": "Фоны", + "EnableBackdropsHelp": "Фоны будут отображаются на заднем плане на некоторых страницах при просмотре медиатеки.", "EnableCinemaMode": "Режим кинозала", "EnableColorCodedBackgrounds": "Обозначеннные цветом фоны", "EnableDisplayMirroring": "Дублирование отображения", @@ -214,8 +216,8 @@ "EnableNextVideoInfoOverlay": "Показывать сведения о следующем видео во время воспроизведения", "EnableNextVideoInfoOverlayHelp": "В конце видео отображать информацию о последующем видео в текущем плей-листе.", "EnablePhotos": "Отображать фотографии", - "EnablePhotosHelp": "Рисунки будут обнаруживаться и отображаться наряду с другими медиафайлами.", - "EnableStreamLooping": "Автоциклирование трансляций", + "EnablePhotosHelp": "Изображения будут обнаруживаться и отображаться наряду с другими медиафайлами.", + "EnableStreamLooping": "Зацикливание трансляций", "EnableStreamLoopingHelp": "Включайте, если трансляции содержат данные только на несколько секунд и необходимо непрерывно их запрашивать. Включение этого без необходимости может породить проблемы.", "EnableThemeSongs": "Тематические композиции", "EnableThemeSongsHelp": "Воспроизведение тематических композиций в фоновом режиме при навигации по медиатеке.", @@ -236,7 +238,7 @@ "EveryNDays": "Каждые {0} дней", "ExitFullscreen": "Выход с полного экрана", "ExtraLarge": "Очень крупный", - "ExtractChapterImagesHelp": "Извлечение рисунков сцен предоставляет возможности клиентам для отображения графических меню выбора сцены. Данный процесс может быть медленным, потребляет ресурсы, и могут понадобиться несколько гигабайт пространства. Он работает при обнаружении видеофайлов, а также, как задача, назначенная на ночь. Расписание возможно перенастроить в области Назначенных задач. Не рекомендуется запускать данную задачу в часы пик.", + "ExtractChapterImagesHelp": "Извлечение изображений сцен предоставляет возможности клиентам для отображения графических меню выбора сцены. Данный процесс может быть медленным, потребляет ресурсы, и могут понадобиться несколько гигабайт пространства. Он работает при обнаружении видеофайлов, а также, как задача, назначенная на ночь. Расписание возможно перенастроить в области Назначенных задач. Не рекомендуется запускать данную задачу в часы пик.", "Extras": "Допматериалы", "FFmpegSavePathNotFound": "Мы не смогли обнаружить FFmpeg по введённому вами пути. FFprobe также необходим и должен быть в той же самой папке. Эти компоненты обычно поставляются вместе в одном загрузочном пакете. Проверьте путь и повторите попытку.", "FastForward": "Быстро вперёд", @@ -263,11 +265,11 @@ "Genres": "Жанры", "GroupBySeries": "Группирование по сериалам", "GroupVersions": "Сгруппировать версии", - "GuestStar": "Пригл. актёр", + "GuestStar": "Приглашенный актёр", "Guide": "Телегид", "GuideProviderLogin": "Вход", "GuideProviderSelectListings": "Выбор перечней", - "H264CrfHelp": "Постоянное значение оценки (Constant Rate Factor, CRF) - параметр качества по умолчанию для кодёра x264. Возможно задавать значения от 0 до 51, где меньшие значения привели бы к улучшению качества (за счёт бо́льших размеров файлов). Разумными являются значения от 18 до 28. Стандартно для x264 - 23, так что вы можете использовать это в качестве отправной точки.", + "H264CrfHelp": "Постоянное значение оценки (Constant Rate Factor, CRF) - параметр качества по умолчанию для кодёра x264. Возможно задавать значения от 0 до 51, где меньшие значения привели бы к улучшению качества (за счёт увеличения размеров файлов). Приемлемыми являются значения от 18 до 28. Стандартно для x264 - 23, так что вы можете использовать это в качестве отправной точки.", "EncoderPresetHelp": "Выберите значение быстрее для улучшения производительности, или значение медленнее для улучшения качества.", "HDPrograms": "HD-передачи", "HandledByProxy": "Обрабатывается обратным прокси", @@ -280,7 +282,7 @@ "HeaderAddScheduledTaskTrigger": "Добавление триггера", "HeaderAddToCollection": "Добавить в коллекцию", "HeaderAddToPlaylist": "Добавление в плей-лист", - "HeaderAddUpdateImage": "Добавление/Обновление рисунка", + "HeaderAddUpdateImage": "Добавление/Обновление изображения", "HeaderAddUser": "Добавить пользователя", "HeaderAdditionalParts": "Дополнительные части", "HeaderAdmin": "Администрирование", @@ -305,7 +307,7 @@ "HeaderCastCrew": "Снимались и снимали", "HeaderChannelAccess": "Доступ ко каналам", "HeaderChannels": "Каналы", - "HeaderChapterImages": "Рисунки сцен", + "HeaderChapterImages": "Изображения сцен", "HeaderCodecProfile": "Профиль кодеков", "HeaderCodecProfileHelp": "Профили кодеков обозначают ограничения устройства при воспроизведении с определёнными кодеками. Если применяется ограничение, то медиаданные перекодируются, даже если кодек настроен для прямого воспроизведения.", "HeaderConfigureRemoteAccess": "Конфигурирование удалённого доступа", @@ -335,7 +337,7 @@ "HeaderDisplay": "Отображение", "HeaderDownloadSync": "Загрузка и синхро", "HeaderEasyPinCode": "Простой PIN-код", - "HeaderEditImages": "Править рисунки", + "HeaderEditImages": "Править изображения", "HeaderEnabledFields": "Включённые поля", "HeaderEnabledFieldsHelp": "Снимите флажок, чтобы зафиксировать поле и защитить его данные от изменнений.", "HeaderEpisodes": "Эпизоды", @@ -343,7 +345,7 @@ "HeaderExternalIds": "Внешние идентификаторы:", "HeaderFeatureAccess": "Доступ к компонентам", "HeaderFeatures": "Материалы", - "HeaderFetchImages": "Отборка рисунков:", + "HeaderFetchImages": "Отборка изображений:", "HeaderFetcherSettings": "Параметры отборщика", "HeaderFilters": "Фильтры", "HeaderForKids": "Детям", @@ -356,8 +358,8 @@ "HeaderIdentificationCriteriaHelp": "Введите хотя бы одно условие распознания.", "HeaderIdentificationHeader": "Заголовок для распознания", "HeaderIdentifyItemHelp": "Введите одно или несколько условий поиска. Изымите условие, чтобы прирастить найденные результаты.", - "HeaderImageOptions": "Опции рисунка", - "HeaderImageSettings": "Параметры рисунков", + "HeaderImageOptions": "Параметры изображения", + "HeaderImageSettings": "Настройки изображения", "HeaderInstall": "Установка", "HeaderInstantMix": "Автомикс", "HeaderItems": "Элементы", @@ -404,7 +406,7 @@ "HeaderPeople": "Люди", "HeaderPhotoAlbums": "Фотоальбомы", "HeaderPinCodeReset": "Сброс PIN-кода", - "HeaderPlayAll": "Воспр. все", + "HeaderPlayAll": "Воспроизвести все", "HeaderPlayOn": "Воспроизведение", "HeaderPlayback": "Воспроизведение медиаданных", "HeaderPlaybackError": "Ошибка воспроизведения", @@ -467,10 +469,10 @@ "HeaderTranscodingProfileHelp": "Добавьте профили перекодировки, чтобы указать, какие форматы следует использовать, когда требуется перекодировка.", "HeaderTunerDevices": "Тюнерные устройства", "HeaderTuners": "Тюнеры", - "HeaderTypeImageFetchers": "{0} отборщики рисунков", + "HeaderTypeImageFetchers": "{0} отборщики изображений", "HeaderTypeText": "Ввод текста", "HeaderUpcomingOnTV": "Ожидаемое на ТВ", - "HeaderUploadImage": "Выкладка рисунка", + "HeaderUploadImage": "Загрузка изображения", "HeaderUser": "Пользователь", "HeaderUsers": "Пользователи", "HeaderVideoQuality": "Качество видео", @@ -489,10 +491,10 @@ "Horizontal": "Горизонтально", "HttpsRequiresCert": "Чтобы включить HTTPS для внешних подключений, вам нужно будет предоставить доверенный SSL-cертификат, например, Let's Encrypt. Предоставьте сертификат или отключите защищенные соединения.", "Identify": "Распознать", - "Images": "Рисунки", + "Images": "Изображения", "ImportFavoriteChannelsHelp": "При включении, будут импортированы только каналы, которые обозначены как избранное на тюнерном устройстве.", "ImportMissingEpisodesHelp": "При включении, информация об отсутствующих эпизодах будет импортирована в вашу базу данных Jellyfin и отображаться в пределах сезонов и сериалов. Это может увеличить время сканирования медиатеки.", - "InstallingPackage": "Устанавливается {0}", + "InstallingPackage": "Устанавливается {0} (версия {1})", "InstantMix": "Автомикс", "ItemCount": "{0} элемент(а/ов)", "Items": "Элементы", @@ -508,7 +510,7 @@ "LabelAirsBeforeEpisode": "Эпизод airs_before:", "LabelAirsBeforeSeason": "Сезон airs_before:", "LabelAlbum": "Альбом:", - "LabelAlbumArtHelp": "PN используемое для альбомных обложек, внутри атрибута dlna:profileID при upnp:albumArtURI. Некоторым устройствам требуется специфическое значение, вне зависимости от размера рисунка.", + "LabelAlbumArtHelp": "PN используемое для альбомных обложек, внутри атрибута dlna:profileID при upnp:albumArtURI. Некоторым устройствам требуется специфическое значение, вне зависимости от размера изображения.", "LabelAlbumArtMaxHeight": "Макс. высота облома альбома:", "LabelAlbumArtMaxHeightHelp": "Максимальное разрешение обложек альбома, представляемых с помощью upnp:albumArtURI.", "LabelAlbumArtMaxWidth": "Макс. ширина обложки альбома:", @@ -538,7 +540,7 @@ "LabelBurnSubtitles": "Внедрение субтитров:", "LabelCache": "Кэш:", "LabelCachePath": "Путь к кешу:", - "LabelCachePathHelp": "Укажите произвольное расположение для файлов серверного кэша, например, рисунков. Оставьте поле незаполненным, чтобы использовать значение по умолчанию.", + "LabelCachePathHelp": "Укажите произвольное расположение для файлов серверного кэша, например, изображений. Оставьте поле незаполненным, чтобы использовать значение по умолчанию.", "LabelCancelled": "Отменено", "LabelCertificatePassword": "Пароль сертификата:", "LabelCertificatePasswordHelp": "Если для вашего сертификата требуется пароль, то введите его здесь.", @@ -579,13 +581,13 @@ "LabelDownMixAudioScale": "Коэффициент усиления при понижающем микшировании:", "LabelDownMixAudioScaleHelp": "Коэффициент компенсирующего усиления звука при понижающем до стерео микшировании. Значение 1 сохраняет исходный уровень.", "LabelDownloadLanguages": "Загружаемые языки:", - "LabelDropImageHere": "Перетащите рисунок сюда или щёлкните для навигации.", + "LabelDropImageHere": "Перетащите изображение сюда или щёлкните для навигации.", "LabelDropShadow": "Окантовка:", "LabelEasyPinCode": "Простой PIN-код:", "LabelEmbedAlbumArtDidl": "Внедрять альбомные обложки в DIDL", "LabelEmbedAlbumArtDidlHelp": "Для некоторых устройств данный метод получения альбомных обложек является предпочтительным. Остальные могут быть не в состоянии воспроизводить, при включении данной опции.", "LabelEnableAutomaticPortMap": "Включить автоматическое сопоставление портов", - "LabelEnableAutomaticPortMapHelp": "Попытаться автоматически сопоставить публичный порт с локальным портом с помощью UPnP. Это может не работать с некоторыми моделями маршрутизаторов. Изменения не применяются до перезапуска сервера.", + "LabelEnableAutomaticPortMapHelp": "Автоматическое перенаправление публичных портов маршрутизатора на локальные порты сервера через UPnP. Это может не работать с некоторыми моделями маршрутизаторов или сетевых конфигураций. Изменения не применяются до перезапуска сервера.", "LabelEnableBlastAliveMessages": "Бомбардировать сообщениями проверки активности", "LabelEnableBlastAliveMessagesHelp": "Включите, если сервер надёжно не обнаруживается иными UPnP устройствами в своей сети.", "LabelEnableDlnaClientDiscoveryInterval": "Интервал обнаружения клиентов", @@ -599,14 +601,14 @@ "LabelEnableHardwareDecodingFor": "Включить аппаратное декодирование для:", "LabelEnableRealtimeMonitor": "Включить отслеживание в реальном времени", "LabelEnableRealtimeMonitorHelp": "В поддерживаемых файловых системах правки файлов будут обрабатываться незамедлительно.", - "LabelEnableSingleImageInDidlLimit": "Лимитировать до единственного внедрённого рисунка", - "LabelEnableSingleImageInDidlLimitHelp": "На некоторых устройствах не отрисовывается нормально, если внедрены несколько рисунков внутри DIDL.", + "LabelEnableSingleImageInDidlLimit": "Ограничить единственным встроенным изображением", + "LabelEnableSingleImageInDidlLimitHelp": "На некоторых устройствах не отрисовывается нормально, если встроено несколько изображений внутри DIDL.", "LabelEndDate": "Конечная дата:", "LabelEpisodeNumber": "Номер эпизода:", "LabelEvent": "Событие:", "LabelEveryXMinutes": "Каждые:", - "LabelExtractChaptersDuringLibraryScan": "Извлекать рисунки сцен в процессе сканирования медиатеки", - "LabelExtractChaptersDuringLibraryScanHelp": "Генерируются рисунки сцен при импорте видео в процессе сканирования медиатеки. В противном случае, они будут извлечены в процессе назначенной задачи «Рисунки сцен», позволяя регулярному сканированию медиатеки завершаться быстрее.", + "LabelExtractChaptersDuringLibraryScan": "Извлекать изображения сцен в процессе сканирования медиатеки", + "LabelExtractChaptersDuringLibraryScanHelp": "Генерируются изображения сцен при импорте видео в процессе сканирования медиатеки. В противном случае, они будут извлечены в процессе назначенной задачи «Изображения сцен», позволяя регулярному сканированию медиатеки завершаться быстрее.", "LabelFailed": "Неудачно", "LabelFileOrUrl": "Файл или URL:", "LabelFinish": "Завершить", @@ -630,8 +632,8 @@ "LabelIconMaxWidth": "Макс. ширина значка:", "LabelIconMaxWidthHelp": "Максимальное разрешение значков представляемых с помощью upnp:icon.", "LabelIdentificationFieldHelp": "Подстрока без учёта регистра, либо регулярное выражение.", - "LabelImageFetchersHelp": "Включите и ранжируйте предпочитаемые отборщики рисунков в порядке приоритета.", - "LabelImageType": "Тип рисунка:", + "LabelImageFetchersHelp": "Включите и ранжируйте предпочитаемые отборщики изображений в порядке приоритета.", + "LabelImageType": "Тип изображения:", "LabelImportOnlyFavoriteChannels": "Ограничиваться каналами обозначенными как избранное", "LabelInNetworkSignInWithEasyPassword": "Включить внутрисетевой вход со своим простым PIN-кодом", "LabelInNetworkSignInWithEasyPasswordHelp": "Используется простой PIN-код для входа в клиенты внутри своей локальной сети. Ваш обычный пароль будет необходим только вне дома. Если PIN-код не заполнен, то внутри своей домашней сети не потребуется пароль.", @@ -641,11 +643,11 @@ "LabelKodiMetadataDateFormat": "Формат даты выпуска:", "LabelKodiMetadataDateFormatHelp": "Все даты в пределах NFO-файлов будут разбираться по данному формату.", "LabelKodiMetadataEnableExtraThumbs": "Копировать extrafanart в поле extrathumbs", - "LabelKodiMetadataEnableExtraThumbsHelp": "При загрузке рисунков, их возможно сохранять внутрь extrafanart и extrathumbs для максимальной совместимости с оболочкой Kodi.", + "LabelKodiMetadataEnableExtraThumbsHelp": "Загружаемые изображения могут быть сохранены внутри полей extrafanart и extrathumbs для максимальной совместимости с оболочкой Kodi.", "LabelKodiMetadataEnablePathSubstitution": "Включить подстановки путей", - "LabelKodiMetadataEnablePathSubstitutionHelp": "Включаются подстановки путей к рисункам с помощью параметров подстановки путей сервера.", - "LabelKodiMetadataSaveImagePaths": "Сохранять пути рисунков в пределах NFO-файлов", - "LabelKodiMetadataSaveImagePathsHelp": "Рекомендуется, если имена файлов рисунков не соответствуют руководящим принципам Kodi.", + "LabelKodiMetadataEnablePathSubstitutionHelp": "Включаются подстановки путей к изображениям с помощью параметров подстановки путей сервера.", + "LabelKodiMetadataSaveImagePaths": "Сохранять пути изображений в пределах NFO-файлов", + "LabelKodiMetadataSaveImagePathsHelp": "Рекомендуется, если имена файлов изображений не соответствуют руководящим принципам Kodi.", "LabelKodiMetadataUser": "Сохранение в NFO-файле данных о просмотре пользователем:", "LabelKodiMetadataUserHelp": "Сохраняет данные о просмотрах в NFO-файлах для использования в других приложениях.", "LabelLanNetworks": "Домашние сети:", @@ -660,7 +662,7 @@ "LabelManufacturer": "Производитель:", "LabelManufacturerUrl": "URL производителя", "LabelMatchType": "Тип соответствия:", - "LabelMaxBackdropsPerItem": "Макс. число задников на элемент:", + "LabelMaxBackdropsPerItem": "Максимальное число фонов на элемент:", "LabelMaxChromecastBitrate": "Качество трансляции Chromecast:", "LabelMaxParentalRating": "Макс. допустимая возрастная категория:", "LabelMaxResumePercentage": "Макс. доля для возобновления, %:", @@ -680,12 +682,12 @@ "LabelMetadataSavers": "Хранители метаданных:", "LabelMetadataSaversHelp": "Выберите форматы файлов, куда будут сохраняться метаданные.", "LabelMethod": "Метод:", - "LabelMinBackdropDownloadWidth": "Мин. ширина загружаемого задника:", - "LabelMinResumeDuration": "Мин. длительность для возобновления:", + "LabelMinBackdropDownloadWidth": "Минимальная ширина загружаемого фона:", + "LabelMinResumeDuration": "Минимальная длительность для возобновления:", "LabelMinResumeDurationHelp": "Наименьшая длительность видео в секундах, при которой сохраняется позиция воспроизведения и позволяется возобновление.", - "LabelMinResumePercentage": "Мин. доля для возобновления, %:", + "LabelMinResumePercentage": "Минимальная доля для возобновления, %:", "LabelMinResumePercentageHelp": "Произведения предполагаются не воспроизведёнными, при остановке до данного момента.", - "LabelMinScreenshotDownloadWidth": "Мин. ширина загружаемого снимка экрана:", + "LabelMinScreenshotDownloadWidth": "Минимальная ширина загружаемого снимка экрана:", "LabelModelDescription": "Описание модели", "LabelModelName": "Наименование модели", "LabelModelNumber": "Номер модели", @@ -708,8 +710,8 @@ "LabelNumberOfGuideDays": "Число дней для загрузки данных телегида:", "LabelNumberOfGuideDaysHelp": "Больше дней загрузки данных телегида обеспечивает возможность заблаговременно назначать расписание и просматривать больше перечней, однако это займёт больше времени для загрузки. При значении «Авто» выбор определяется числом каналов.", "LabelOptionalNetworkPath": "(Необязательно) Общедоступная сетевая папка:", - "LabelOptionalNetworkPathHelp": "Если данная папка общедоступна в своей сети, предоставление пути к сетевой папке может позволить Jellyfin-приложениям на других устройствах получить прямой доступ к медиафайлам.", - "LabelOriginalAspectRatio": "Исходное соот-ие сторон:", + "LabelOptionalNetworkPathHelp": "Если данная папка является общей в сети, указание пути к сетевой папке может позволить Jellyfin-приложениям на других устройствах иметь прямой доступ к медиафайлам. Например, {0} или {1}.", + "LabelOriginalAspectRatio": "Исходное соотношение сторон:", "LabelOriginalTitle": "Оригинальное название:", "LabelOverview": "Обзор:", "LabelParentNumber": "Родительский номер:", @@ -833,7 +835,7 @@ "LabelVideo": "Видео", "LabelXDlnaCap": "Свойства X-Dlna:", "LabelXDlnaCapHelp": "Определяется содержание из элемента X_DLNACAP во пространстве имён urn:schemas-dlna-org:device-1-0.", - "LabelXDlnaDoc": "Схема X-Dlna:", + "LabelXDlnaDoc": "Схема X-DLNA:", "LabelXDlnaDocHelp": "Определяется содержание из элемента X_DLNADOC во пространстве имён urn:schemas-dlna-org:device-1-0.", "LabelYear": "Год:", "LabelYourFirstName": "Ваше имя:", @@ -856,18 +858,18 @@ "ManageLibrary": "Управление медиатекой", "ManageRecording": "Управлять записью", "MapChannels": "Сопоставить каналы", - "MarkPlayed": "Отметить как воспр-ое", - "MarkUnplayed": "Отметить как невоспр-ое", + "MarkPlayed": "Отметить как воспроизведенное", + "MarkUnplayed": "Отметить как невоспроизведенное", "MaxParentalRatingHelp": "Содержание с более высокой возр. категорией будет скрыто от этого пользователя.", "MediaInfoAnamorphic": "Анаморфность", - "MediaInfoAspectRatio": "Соот-ие сторон", + "MediaInfoAspectRatio": "Соотношение сторон", "MediaInfoBitDepth": "Глубина цвета", "MediaInfoBitrate": "Поток. ск-ть", "MediaInfoChannels": "Каналы", "MediaInfoCodec": "Кодек", "MediaInfoCodecTag": "Тег кодека", "MediaInfoContainer": "Контейнер", - "MediaInfoDefault": "Умолчание", + "MediaInfoDefault": "По умолчанию", "MediaInfoExternal": "Внешние", "MediaInfoForced": "Форсир-ые", "MediaInfoFramerate": "Ч-та кадров", @@ -880,7 +882,7 @@ "MediaInfoProfile": "Профиль", "MediaInfoRefFrames": "Опорные кадры", "MediaInfoResolution": "Разрешение", - "MediaInfoSampleRate": "Ч-та дискр-ии", + "MediaInfoSampleRate": "Частота дискретизации", "MediaInfoSize": "Размер", "MediaInfoTimestamp": "Метка времени", "MediaIsBeingConverted": "Медиаданные преобразуются в формат, совместимый с устройством, которое воспроизводит эти медиаданные.", @@ -961,16 +963,16 @@ "NoNextUpItemsMessage": "Ничего не найдено. Начните смотреть свои ТВ-передачи!", "NoPluginConfigurationMessage": "В данном плагине нет параметров конфигурирования.", "NoSubtitleSearchResultsFound": "Результатов не найдено.", - "NoSubtitles": "Без субтитров", + "NoSubtitles": "Ничего", "NoSubtitlesHelp": "По умолчанию, субтитры не будут загружаться. Они могут быть все ещё включены вручную во время воспроизведения.", "None": "Ничего", "Normal": "Обычный", "NumLocationsValue": "{0} пап(ки/ок)", "Off": "Выкл", "OneChannel": "Один канал", - "OnlyForcedSubtitles": "Только форсированные субтитры", + "OnlyForcedSubtitles": "Только форсированные", "OnlyForcedSubtitlesHelp": "Загружены будут только форсированные субтитры.", - "OnlyImageFormats": "Только графические форматы (VOBSUB, PGS, SUB и т.д.)", + "OnlyImageFormats": "Только графические форматы (VOBSUB, PGS и SUB)", "OptionAdminUsers": "Администраторы", "OptionAlbum": "Альбом", "OptionAlbumArtist": "Исп. альбома", @@ -1003,7 +1005,7 @@ "OptionBlockMusic": "Музыка", "OptionBlockTrailers": "Трейлеры", "OptionBlockTvShows": "ТВ-передачи", - "OptionBluray": "BluRay", + "OptionBluray": "Blu-ray", "OptionCommunityRating": "Пользовательский рейтинг", "OptionContinuing": "Продолжающееся", "OptionCriticRating": "Оценка критиков", @@ -1020,12 +1022,12 @@ "OptionDisplayFolderView": "Отображать аспект Папки для просмотра обычных медиапапок", "OptionDisplayFolderViewHelp": "Отображение аспекта \"Папки\" рядом с другими вашими медиатеками. Это может быть полезно, если вы хотите вид обычных папок.", "OptionDownloadArtImage": "Виньетка", - "OptionDownloadBackImage": "Задник", + "OptionDownloadBackImage": "Фон", "OptionDownloadBannerImage": "Баннер", "OptionDownloadBoxImage": "DVD-бокс", "OptionDownloadDiscImage": "Диск", - "OptionDownloadImagesInAdvance": "Загружать рисунки заблаговременно", - "OptionDownloadImagesInAdvanceHelp": "По умолчанию, большинство рисунков загружаются только при запросе от Jellyfin-приложения. Включите данную опцию, чтобы загружать все рисунки заблаговременно, при импорте новых медиаданных. Это может привести к существенно длительным сканированиям медиатеки.", + "OptionDownloadImagesInAdvance": "Загружать изображения заблаговременно", + "OptionDownloadImagesInAdvanceHelp": "По умолчанию, большинство изображений загружаются только при запросе от Jellyfin-приложения. Включите данную опцию, чтобы загружать все изображения заблаговременно, при импорте новых медиаданных. Это может привести к существенно длительным сканированиям медиатеки.", "OptionDownloadLogoImage": "Логотип", "OptionDownloadMenuImage": "Меню", "OptionDownloadPrimaryImage": "Основной", @@ -1045,7 +1047,7 @@ "OptionEstimateContentLength": "Рассчитывать длину содержимого при перекодировке", "OptionEveryday": "Ежедневно", "OptionExternallyDownloaded": "Внешние загружаемые", - "OptionExtractChapterImage": "Включить извлечение рисунков сцен", + "OptionExtractChapterImage": "Включить извлечение изображений сцен", "OptionFavorite": "Избранное", "OptionFriday": "пятница", "OptionHasSpecialFeatures": "Доп. материалы", @@ -1065,7 +1067,7 @@ "OptionMissingEpisode": "Отсутствующие эпизоды", "OptionMonday": "понедельник", "OptionNameSort": "Название", - "OptionNew": "Новое...", + "OptionNew": "Новое…", "OptionNone": "Ничего", "OptionOnAppStartup": "При запуске приложения", "OptionOnInterval": "В интервале", @@ -1092,7 +1094,7 @@ "OptionResumable": "Возможно возобновление", "OptionRuntime": "Длительность", "OptionSaturday": "суббота", - "OptionSaveMetadataAsHidden": "Сохранять метаданные и рисунки в виде скрытых файлов", + "OptionSaveMetadataAsHidden": "Сохранять метаданные и изображения в виде скрытых файлов", "OptionSaveMetadataAsHiddenHelp": "Это изменение будет применено к новым метаданным сохраняемым в будущем. Существующие файлы метаданных будут обновлены в следующий раз, когда они будут сохраняться на Jellyfin Server.", "OptionSpecialEpisode": "Спецэпизоды", "OptionSubstring": "Подстрока", @@ -1110,10 +1112,10 @@ "OptionWeekly": "Еженедельно", "OriginalAirDateValue": "Дата исходного эфира: {0}", "Overview": "Обзор", - "PackageInstallCancelled": "Установка {0} отменена.", - "PackageInstallCompleted": "Установка {0} завершена.", - "PackageInstallFailed": "Установка {0} неудачна.", - "ParentalRating": "Возр. кат-ия", + "PackageInstallCancelled": "Установка {0} (версия {1}) отменена.", + "PackageInstallCompleted": "Установка {0} (версия {1}) завершена.", + "PackageInstallFailed": "Установка {0} (версия {1}) неудачна.", + "ParentalRating": "Возрастная категория", "PasswordMatchError": "Пароль и подтверждение пароля должны совпадать.", "PasswordResetComplete": "Пароль был сброшен.", "PasswordResetConfirmation": "Вы действительно хотите сбросить пароль?", @@ -1126,10 +1128,10 @@ "PinCodeResetComplete": "PIN-код был сброшен.", "PinCodeResetConfirmation": "Вы действительно хотите сбросить PIN-код?", "PlaceFavoriteChannelsAtBeginning": "Разместить избранные каналы в начале", - "Play": "Воспр.", - "PlayAllFromHere": "Воспр. все отсюда", + "Play": "Воспроизведение", + "PlayAllFromHere": "Воспроизвести все отсюда", "PlayCount": "Кол. воспроизведений", - "PlayFromBeginning": "Воспр. с начала", + "PlayFromBeginning": "Воспроизвести с начала", "PlayNext": "Воспроизвести следующее", "PlayNextEpisodeAutomatically": "Воспроизводить последующий эпизод автоматически", "Played": "Воспроизведено", @@ -1179,7 +1181,7 @@ "RepeatMode": "Режим повтора", "RepeatOne": "Повторить раз", "ReplaceAllMetadata": "Замена всех метаданных", - "ReplaceExistingImages": "Замена имеющихся рисунков", + "ReplaceExistingImages": "Замена имеющихся изображений", "RequiredForAllRemoteConnections": "Требуется для всех внешних подключений", "RestartPleaseWaitMessage": "Подождите, пока Jellyfin Server выключится и перезапустится. Это может занять минуту или две.", "ResumeAt": "Возобновить с {0}", @@ -1221,6 +1223,8 @@ "ShowYear": "Отображать год", "Shows": "Передачи", "Shuffle": "Перемешать", + "New": "Новинка", + "Filter": "Фильтр", "SimultaneousConnectionLimitHelp": "Максимальное количество разрешённых одновременных потоков. Введите 0, чтобы снять ограничения.", "SkipEpisodesAlreadyInMyLibrary": "Не записывать эпизоды, которые уже находятся в моей медиатеке", "SkipEpisodesAlreadyInMyLibraryHelp": "Эпизоды будут сравниваться с помощью номеров сезонов и эпизодов, когда они имеются.", @@ -1243,7 +1247,7 @@ "Subtitles": "Субтитры", "Suggestions": "Предлагаемое", "Sunday": "воскресенье", - "Sync": "Синхро", + "Sync": "Синхронизация", "SystemDlnaProfilesHelp": "Системные профили доступны только для чтения. Правки системного профиля будут сохранены в новом настраиваемом профиле.", "TV": "ТВ", "TabAccess": "Доступ", @@ -1258,7 +1262,7 @@ "TabContainers": "Контейнеры", "TabDashboard": "Панель", "TabDevices": "Устройства", - "TabDirectPlay": "Прямое воспр-ие", + "TabDirectPlay": "Прямое воспроизведение", "TabDisplay": "Отображение", "TabEpisodes": "Эпизоды", "TabFavorites": "Избранное", @@ -1346,7 +1350,7 @@ "ValueSeconds": "{0} сек", "ValueSeriesCount": "{0} сериал(а/ов)", "ValueSongCount": "{0} композици(и/й)", - "ValueSpecialEpisodeName": "Спецэпизод - {0}", + "ValueSpecialEpisodeName": "Специальный эпизод - {0}", "ValueTimeLimitMultiHour": "Временной лимит: {0} час(а/ов)", "ValueTimeLimitSingleHour": "Временной лимит: 1 час", "ValueVideoCodec": "Видео кодек: {0}", @@ -1373,7 +1377,7 @@ "LabelDynamicExternalId": "{0} Ид:", "LeaveBlankToNotSetAPassword": "Оставьте пустым, чтобы не назначать пароль.", "MessageImageFileTypeAllowed": "Поддерживаются только файлы JPEG и PNG.", - "MessageImageTypeNotSelected": "Выберите тип рисунка из выпадающего меню.", + "MessageImageTypeNotSelected": "Выберите тип изображения из выпадающего меню.", "OptionResElement": "res-элемент", "AuthProviderHelp": "Выберите поставщика аутентификации, который будет использоваться для аутентификации пароля этого пользователя.", "HeaderFavoriteMovies": "Избранные фильмы", @@ -1401,7 +1405,7 @@ "MediaInfoSoftware": "ПО", "MediaInfoStreamTypeAudio": "Аудио", "MediaInfoStreamTypeData": "Данные", - "MediaInfoStreamTypeEmbeddedImage": "Внедрённый рисунок", + "MediaInfoStreamTypeEmbeddedImage": "Встроенное изображение", "MediaInfoStreamTypeSubtitle": "Субтитры", "MediaInfoStreamTypeVideo": "Видео", "MessageNoCollectionsAvailable": "Коллекции позволяют вам наслаждаться персонализированными группами фильмов, сериалов и альбомов. Нажмите кнопку +, чтобы начать создавать коллекции.", @@ -1419,13 +1423,13 @@ "OptionLoginAttemptsBeforeLockoutHelp": "При значении 0 наследуются по умолчанию три попытки для обычных пользователей и пять для администратора. При установке этого значения в -1 функция отключается.", "OptionPoster": "Постер", "OptionPosterCard": "Постер-карта", - "OptionThumb": "Бегунок", - "OptionThumbCard": "Бегунок-карта", + "OptionThumb": "Эскиз", + "OptionThumbCard": "Эскиз-карта", "PasswordResetProviderHelp": "Выберите поставщика сброса пароля, который будет использоваться, когда этот пользователь запрашивает сброс пароля", "PlaybackData": "Данные воспроизведения", "SubtitleOffset": "Сдвиг субтитров", "TabNetworking": "Работа в сети", - "LabelBaseUrlHelp": "Здесь вы можете добавить пользовательский подкаталог для доступа к серверу с более уникального URL.", + "LabelBaseUrlHelp": "Добавляется пользовательский подкаталог к URL сервера. Например: http://example.com/<baseurl>", "LabelPlayer": "Проигрыватель:", "MoreMediaInfo": "О медиаданных", "LabelVideoCodec": "Видео кодек:", @@ -1436,7 +1440,7 @@ "LabelPlayMethod": "Метод воспроизведения:", "LabelFolder": "Папка:", "LabelBaseUrl": "Базовый URL:", - "LabelBitrate": "Поток. ск-ть:", + "LabelBitrate": "Битрейт:", "LabelAudioSampleRate": "Частота дискретизации аудио:", "LabelAudioCodec": "Аудио кодек:", "LabelAudioChannels": "Аудио каналы:", @@ -1445,10 +1449,10 @@ "HeaderFavoriteBooks": "Избранные книги", "CopyStreamURL": "Копировать URL потока", "LabelPleaseRestart": "Изменения вступят в силу после перезагрузки веб-клиента вручную.", - "CopyStreamURLSuccess": "URL скопирован успешно.", + "CopyStreamURLSuccess": "URL скопирован успешно.", "MusicLibraryHelp": "Просмотрите {0}руководство по именованию музыки{1}.", "FetchingData": "Выборка дополнительных данных", - "ButtonAddImage": "Добавить рисунок", + "ButtonAddImage": "Добавить изображение", "HeaderFavoritePeople": "Избранные люди", "OptionRandom": "Случайный", "ButtonSplit": "Разделить", @@ -1456,8 +1460,6 @@ "HeaderNavigation": "Навигация", "LabelVideoResolution": "Разрешение видео:", "LabelStreamType": "Тип потока:", - "EnableFastImageFadeInHelp": "Включить быстрое появление анимации для загруженных рисунков", - "EnableFastImageFadeIn": "Быстрое появление рисунка", "LabelPlayerDimensions": "Размеры проигрывателя:", "LabelDroppedFrames": "Пропущенные кадры:", "LabelCorruptedFrames": "Испорченные кадры:", @@ -1466,6 +1468,89 @@ "NoCreatedLibraries": "Похоже, вы еще не создали ни одной медиатеки. {0}Желаете создать её сейчас?{1}", "AskAdminToCreateLibrary": "Попросите администратора создать медиатеку.", "AllowFfmpegThrottling": "Притормаживать перекодировку", - "PlaybackErrorNoCompatibleStream": "Возникла проблема с профилем клиента, и сервер не отправляет совместимый формат мультимедиа.", - "AllowFfmpegThrottlingHelp": "Когда перекодирование или переупаковка достаточно далеко опережают текущую позицию воспроизведения, процесс приостанавливается, так что он использует меньше ресурсов. Это наиболее полезно, когда вы редко меняете позиции в видео. Выключите это, если у вас возникли проблемы с воспроизведением." + "PlaybackErrorNoCompatibleStream": "Этот клиент несовместим с медиаданными, а сервер не отправляет медиаданные в совместимом формате.", + "AllowFfmpegThrottlingHelp": "Когда перекодирование или переупаковка достаточно далеко опережают текущую позицию воспроизведения, процесс приостанавливается, так что он использует меньше ресурсов. Это наиболее полезно, когда вы редко меняете позиции в видео. Выключите это, если у вас возникли проблемы с воспроизведением.", + "OnWakeFromSleep": "При пробуждении ото сна", + "YadifBob": "YADIF с удвоением", + "OnApplicationStartup": "При запуске приложения", + "EveryXHours": "Каждые {0} часов", + "EveryHour": "Каждый час", + "EveryXMinutes": "Каждые {0} мин", + "WeeklyAt": "{0} в {1}", + "DailyAt": "Ежедневно в {0}", + "PersonRole": "как {0}", + "ListPaging": "{0}-{1} из {2}", + "Yadif": "YADIF", + "Track": "Дорожка", + "Season": "Сезон", + "Person": "Персона", + "OtherArtist": "Другой исполнитель", + "Movie": "Фильм", + "LabelLibraryPageSize": "Размер страницы медиатеки:", + "Episode": "Эпизод", + "ClientSettings": "Параметры клиента", + "BoxSet": "Коллекция", + "Artist": "Исполнитель", + "AlbumArtist": "Исполнитель альбома", + "Album": "Альбом", + "LastSeen": "Последний раз был {0}", + "WriteAccessRequired": "Jellyfin Server требуются права на запись в эту папку. Обеспечьте доступ для записи и попробуйте снова.", + "PathNotFound": "Путь не может быть найден. Убедитесь, что путь правильный и попробуйте снова.", + "ReleaseGroup": "Релиз-группа", + "PreferEmbeddedEpisodeInfosOverFileNames": "Предпочитать встроенную информацию эпизода вместо имён файлов", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Используется информация об эпизоде из встроенных метаданных, если они доступны.", + "LabelLibraryPageSizeHelp": "Устанавливается количество элементов для отображения на странице медиатеки. Установите 0 для отключения нумерации страниц.", + "LabelDeinterlaceMethod": "Метод устранения гребёнки:", + "DeinterlaceMethodHelp": "Выберите метод устранения гребёнки, который будет использоваться при перекодировании чересстрочного содержания.", + "UnsupportedPlayback": "Jellyfin не может расшифровать содержимое, защищенное DRM, но в любом случае будет предпринята попытка расшифровки всего содержимого, включая защищенные заголовки. Некоторые файлы могут выглядеть полностью черными из-за шифрования или других неподдерживаемых функций, таких как интерактивные заголовки.", + "MessageUnauthorizedUser": "В настоящее время у вас нет доступа к серверу. Пожалуйста, свяжитесь с администратором сервера для получения дополнительной информации.", + "HeaderFavoritePlaylists": "Избранные плей-листы", + "LabelRequireHttpsHelp": "Если этот флажок установлен, сервер будет автоматически перенаправлять все запросы через HTTP на HTTPS. Это не имеет никакого эффекта, если сервер не слушает HTTPS.", + "LabelEnableHttpsHelp": "Позволяет серверу слушать сконфигурированный HTTPS-порт. Действительный сертификат также должен быть сконфигурирован для того, чтобы это вступило в силу.", + "ApiKeysCaption": "Список действующих текущих API-ключей", + "TabDVR": "DVR", + "SaveChanges": "Сохранить изменения", + "LabelRequireHttps": "Требуется HTTPS", + "LabelNightly": "Ночная", + "LabelStable": "Стабильная", + "LabelChromecastVersion": "Версия Chromecast", + "LabelEnableHttps": "Включить HTTPS", + "HeaderServerAddressSettings": "Параметры адреса сервера", + "HeaderRemoteAccessSettings": "Параметры удалённого доступа", + "HeaderHttpsSettings": "Параметры HTTPS", + "HeaderDVR": "DVR", + "MessageSyncPlayJoinGroupDenied": "Требуется разрешение для использования SyncPlay.", + "MessageSyncPlayDisabled": "SyncPlay отключен.", + "MessageSyncPlayEnabled": "SyncPlay включён.", + "LabelSyncPlayAccess": "Доступ к SyncPlay", + "LabelSyncPlayLeaveGroupDescription": "Отключить SyncPlay", + "HeaderSyncPlayEnabled": "SyncPlay включён", + "HeaderSyncPlaySelectGroup": "Присоединить группу", + "EnableDetailsBanner": "Баннер подробностей", + "EnableDetailsBannerHelp": "Отображает рисунок баннера вверху страницы подробностей элемента.", + "MessageSyncPlayErrorAccessingGroups": "Произошла ошибка при попытке доступа к списку групп.", + "MessageSyncPlayLibraryAccessDenied": "Доступ к данному содержанию ограничен.", + "MessageSyncPlayCreateGroupDenied": "Требуется разрешение для создания группы.", + "MessageSyncPlayGroupDoesNotExist": "Не удалось присоединиться к группе, поскольку она не существует.", + "MessageSyncPlayPlaybackPermissionRequired": "Требуется разрешение на воспроизведение.", + "MessageSyncPlayNoGroupsAvailable": "Никакие группы не доступны. Сначала начните воспроизводить что-нибудь.", + "MessageSyncPlayGroupWait": "{0} буферизуется...", + "MessageSyncPlayUserLeft": "{0} покинул группу.", + "MessageSyncPlayUserJoined": "{0} присоединил группу.", + "LabelSyncPlayAccessNone": "Отключено для данного пользователя", + "LabelSyncPlayAccessJoinGroups": "Разрешить пользователю присоединяться к группам", + "LabelSyncPlayAccessCreateAndJoinGroups": "Разрешить пользователю создавать и присоединять группы", + "LabelSyncPlayLeaveGroup": "Покинуть группу", + "LabelSyncPlayNewGroupDescription": "Создание новой группы", + "LabelSyncPlayNewGroup": "Новая группа", + "LabelSyncPlaySyncMethod": "Метод синхронизации:", + "LabelSyncPlayPlaybackDiff": "Разница времени воспроизведения:", + "MillisecondsUnit": "мс", + "LabelSyncPlayTimeOffset": "Сдвиг времени относительно сервера:", + "SyncPlayAccessHelp": "Выберите уровень доступа данного пользователя к функциональности SyncPlay. SyncPlay позволяет синхронизировать воспроизведение с другими устройствами.", + "MessageSyncPlayErrorMedia": "Не удалось включить SyncPlay! Ошибка медиаданных.", + "MessageSyncPlayErrorMissingSession": "Не удалось включить SyncPlay! Отсутствует сеанс.", + "MessageSyncPlayErrorNoActivePlayer": "Активный проигрыватель не найден. SyncPlay был отключен.", + "ShowMore": "Показать больше", + "ShowLess": "Показать меньше" } diff --git a/src/strings/sk.json b/src/strings/sk.json index bbe23001fa..eb236ff9ec 100644 --- a/src/strings/sk.json +++ b/src/strings/sk.json @@ -5,7 +5,7 @@ "Albums": "Albumy", "All": "Všetko", "AllChannels": "Všetky kanály", - "AllComplexFormats": "Všetky komplexné formáty (ASS, SSA, VOBSUB, PGS, SUB, IDX)", + "AllComplexFormats": "Všetky komplexné formáty (ASS, SSA, VOBSUB, PGS, SUB, IDX, …)", "AllEpisodes": "Všetky epizódy", "AllLanguages": "Všetky jazyky", "AllLibraries": "Všetky knižnice", @@ -14,7 +14,7 @@ "AllowRemoteAccessHelp": "Nezaškrtnuté znamená, že všetky vzdialené pripojenia budú blokované.", "AlwaysPlaySubtitles": "Vždy prehrať", "AnyLanguage": "Akýkoľvek jazyk", - "AroundTime": "Okolo {0}", + "AroundTime": "Okolo", "Artists": "Umelci", "AsManyAsPossible": "Najviac ako je možné", "Ascending": "Vzostupne", @@ -25,7 +25,7 @@ "BirthDateValue": "Narodený/á: {0}", "BirthLocation": "Miesto narodenia", "BirthPlaceValue": "Miesto narodenia: {0}", - "BookLibraryHelp": "Audioknihy a učebnice sú podporované. Prečítajte si {0}pravidlá pre názvy kníh v Jellyfine{1}.", + "BookLibraryHelp": "Audioknihy a učebnice sú podporované. Prečítajte si {0} pravidlá pre názvy kníh v Jellyfine {1}.", "Books": "Knihy", "ButtonAdd": "Pridať", "ButtonAddMediaLibrary": "Pridať knižnicu médií", @@ -648,7 +648,7 @@ "OptionMissingEpisode": "Chýbajúce epizódy", "OptionMonday": "Pondelok", "OptionNameSort": "Názov", - "OptionNew": "Nové...", + "OptionNew": "Nové…", "OptionNone": "Žiadne", "OptionOnAppStartup": "Pri spustení aplikácie", "OptionParentalRating": "Rodičovské hodnotenie", @@ -721,7 +721,7 @@ "Refresh": "Obnoviť", "RefreshMetadata": "Obnoviť metadáta", "ReleaseDate": "Dátum vydania", - "RememberMe": "Zapamätať si ma", + "RememberMe": "Zapamätaj si ma", "RemoveFromCollection": "Odobrať z kolekcie", "Repeat": "Opakovať", "RepeatAll": "Opakovať všetko", @@ -1180,7 +1180,7 @@ "CancelSeries": "Ukončiť seriál", "ButtonSplit": "Rozdeliť", "ButtonAddImage": "Pridať obrázok", - "BurnSubtitlesHelp": "Určuje, či má server vpáliť titulky počas transkódovania videa. Vynechanie tejto možnosti výrazne zvýši výkon. Vyberte možnosť Auto, pokiaľ chcete vpáliť do obrazu titulky v grafickom formáte (VOBSUB, PGS, SUB, IDX) a niektoré ASS alebo SSA titulky.", + "BurnSubtitlesHelp": "Určuje, či má server vpáliť titulky počas transkódovania videa. Vynechanie tejto možnosti výrazne zvýši výkon. Vyberte možnosť Auto, pokiaľ chcete vpáliť do obrazu titulky v grafickom formáte (VOBSUB, PGS, SUB, IDX, …) a niektoré ASS alebo SSA titulky.", "BrowsePluginCatalogMessage": "Prehliadnite si náš katalóg dostupných zásuvných modulov.", "Browse": "Prechádzať", "Blacklist": "Blacklist", @@ -1189,7 +1189,7 @@ "Art": "Umenie", "AlwaysPlaySubtitlesHelp": "Titulky odpovedajúce jazykovej preferencií sa načítajú bez ohľadu na jazyk zvuku.", "AllowedRemoteAddressesHelp": "Zoznam IP adries alebo IP/netmask záznamov pre siete oddelené čiarkami z ktorých sa dá vzdialene pripojiť. Pokiaľ zoznam bude prázdny, všetky adresy budú povolené.", - "AllowOnTheFlySubtitleExtractionHelp": "Vložené titulky môžu byť extrahované z videa a prenesené na klienta vo forme jednoduchého textu aby sa zabránilo transkódovaniu videa. Na niektorých systémoch táto operácia môže trvať dlhší čas a a spôsobiť sekanie videa počas počas extrahovania. Vypnutie tejto funkcie bude mať za následok, že titulky budú počas transkódovania vypálené do samotného videa pokiaľ klientské zariadenie natívne nepodporuje ich formát.", + "AllowOnTheFlySubtitleExtractionHelp": "Vložené titulky môžu byť extrahované z videa a prenesené na klienta vo forme jednoduchého textu, aby sa zabránilo transkódovaniu videa. Na niektorých systémoch táto operácia môže trvať dlhší čas a a spôsobiť sekanie videa počas počas extrahovania. Vypnutie tejto funkcie bude mať za následok, že titulky budú počas transkódovania vypálené do samotného videa pokiaľ klientské zariadenie natívne nepodporuje ich formát.", "Watched": "Pozreté", "TvLibraryHelp": "Pozrite sa na {0}sprievodcu pomenovania TV programov{1}.", "LabelLineup": "Lineup:", @@ -1389,7 +1389,7 @@ "LabelPersonRoleHelp": "Príklad: Vodič nákladiaku so zmrzlinou", "LabelPasswordResetProvider": "Poskytovateľ obnovy hesla:", "LabelParentNumber": "Číslo rodiča:", - "LabelOptionalNetworkPathHelp": "Pokiaľ je tento priečinok zdielaný vo vašej sieti, môže poskytovanie cesty k zdielanému priečinku umožniť Jellyfin aplikáciám priamy prístup k mediálnym súborom.", + "LabelOptionalNetworkPathHelp": "Pokiaľ je tento priečinok zdielaný vo vašej sieti, môže poskytovanie cesty k zdielanému priečinku umožniť Jellyfin aplikáciám priamy prístup k mediálnym súborom. Napríklad, {0} alebo {1}.", "LabelNumberOfGuideDaysHelp": "Stiahnutím viacerých dní umožní sprievodca naplánovať a zobraziť viac vecí do budúcnosti, sťahovanie však môže trvať dlhšie. Auto vyberie možnosť podľa počtu kanálov.", "LabelNumberOfGuideDays": "Počet dní pre stiahnutie dát sprievodcu:", "LabelMusicStreamingTranscodingBitrateHelp": "Špecifikujte maximálny dátový tok pre streamovanie hudby.", @@ -1433,7 +1433,7 @@ "LabelFriendlyName": "Priateľský názov:", "LabelFolder": "Priečinok:", "LabelExtractChaptersDuringLibraryScanHelp": "Generovať obrázky kapitol počas toho, ako sú videá importované v prvotnom prehľadávaní knižnice. Inak sa budú extrahovať počas naplánovanej úlohy generovania obrázkov kapitol, čo dovoľuje rýchlejšie dokončenie bežného prehľadávania knižnice.", - "LabelBaseUrlHelp": "Tu môžete pridať vlastný podpriečinok, aby bolo možné pristupovať k serveru z viac unikátnej URL.", + "LabelBaseUrlHelp": "Pridá vlastný reťazec na URL adresu serveru, napr: http://priklad.sk/<vlastnyretazec>", "LabelBaseUrl": "Východzia URL:", "LabelEveryXMinutes": "Každý:", "LabelEnableSingleImageInDidlLimitHelp": "Niektoré zariadenia nebudú zobrazovať správne pokiaľ je viacero obrázkov uložených v Didl.", @@ -1442,7 +1442,7 @@ "LabelEnableDlnaDebugLogging": "Povoliť loggovanie DLNA debugu", "LabelEnableDlnaClientDiscoveryIntervalHelp": "Určuje dobu trvania v sekundách medzi SSDP vyhľadávaniami vykonanými Jellyfinom.", "LabelEnableDlnaClientDiscoveryInterval": "Interval pre objavenie klienta (sekundy)", - "LabelEnableAutomaticPortMapHelp": "Pokus o automatické namapovanie vejerného portu na lokálny port cez UPnP. Toto nemusí fungovať so všetkými modelmi routerov. Zmeny sa vykonajú až po reštarte servera.", + "LabelEnableAutomaticPortMapHelp": "Automatické namapovanie vejerného portu na lokálny port serveru cez UPnP. Toto nemusí fungovať so všetkými modelmi routerov alebo sieťových konfigurácií. Zmeny sa vykonajú až po reštarte servera.", "LabelEmbedAlbumArtDidlHelp": "Niektoré zariadenia preferujú túto metódu pre získavanie obrázku albumu. Ostatným môže zlyhať prehrávanie pokiaľ je táto možnosť povolená.", "LabelBlastMessageIntervalHelp": "Určuje dobu v sekundách medzi vysielaniami správ o serveri.", "LabelBindToLocalNetworkAddressHelp": "Voliteľné. Prepísať lokálnu IP adresu viazanú na http server. Pokiaľ zostane prázdna, server sa naviaže na všetky dostupné adresy. Pri zmene tejto hodnoty sa vyžaduje reštart Jellyfin Servera.", @@ -1457,8 +1457,6 @@ "MessageConfirmAppExit": "Chceli by ste odísiť?", "LabelVideoResolution": "Rozlíšenie videa:", "LabelStreamType": "Typ streamu:", - "EnableFastImageFadeInHelp": "Povoliť animáciu rýchleho rozjasnenia pre nahrané obrázky", - "EnableFastImageFadeIn": "Rýchle rozjasnenie obrázku", "LabelPlayerDimensions": "Rozmery prehrávača:", "LabelDroppedFrames": "Vynechané snímky:", "LabelCorruptedFrames": "Poškodené snímky:", @@ -1469,8 +1467,8 @@ "PlaybackErrorNoCompatibleStream": "Tento klient nie je kompatibilný s médiom a server 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", - "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.", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferovať vložen[ informáciu o epizóde pred názvom súboru", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Toto využíva informáciu o epizóde z vložených metadát, pokiaľ sú dostupne.", "ClientSettings": "Nastavenie klienta", "Album": "Album", "DeinterlaceMethodHelp": "Vyberte metódu odstránenia prekladania obrazu videa pri transkódovaní prekladaného obsahu.", @@ -1486,8 +1484,8 @@ "ListPaging": "{0}-{1} z {2}", "WriteAccessRequired": "Jellyfin Server vyžaduje práva na zapisovanie do tohoto priečinku. Prosím, uistite sa že má práva na zapisovanie a skúste to znova.", "PathNotFound": "Táto cesta nebola nájdená. Prosím, uistite sa že cesta je správna a skúste to znovu.", - "YadifBob": "Yadif Bob", - "Yadif": "Yadif", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", "Track": "Stopa", "Season": "Séria", "ReleaseGroup": "Vydavateľ", @@ -1498,5 +1496,60 @@ "Episode": "Epizóda", "BoxSet": "Box Set", "Artist": "Umelec", - "AlbumArtist": "Umelec albumu" + "AlbumArtist": "Umelec albumu", + "LabelLibraryPageSizeHelp": "Určuje množstvo položiek na zobrazenie na stránke knižnice. Nastavte 0 pre vypnutie stránkovania.", + "LabelLibraryPageSize": "Veľkosť stránky knižnice:", + "MessageUnauthorizedUser": "Momentálne nemáte oprávnenie na prístup k serveru. Prosím, kontaktujte svojho administrátora serveru pre viac informácií.", + "UnsupportedPlayback": "Jellyfin nemôže dešifrovať obsah chránený technológiou DRM, ale pokúsi sa o to, vrátane chránených titulov. Niektoré súbory sa môžu zobraziť ako kompletne čierne z dôvodu, že sú zašifrované alebo obsahujú nepodporované funckie, ako napríklad interaktívne funkcie.", + "Filter": "Filter", + "New": "Nové", + "HeaderFavoritePlaylists": "Obľúbené playlisty", + "ButtonTogglePlaylist": "Playlist", + "ButtonToggleContextMenu": "Viac", + "ApiKeysCaption": "Zoznam v súčasnosti povolených API kľúčov", + "LabelNightly": "Nočná", + "LabelStable": "Stabilná", + "LabelChromecastVersion": "Chromecast verzia", + "TabDVR": "DVR", + "LabelRequireHttpsHelp": "Pokiaľ je zaškrtnutý, server bude automaticky presmerovávať všetky HTTP požiadavky cez HTTPS. Toto nastavenie nemá žiadny efekt, pokiaľ server nepočúva na HTTPS.", + "LabelRequireHttps": "Vyžadovať HTTPS", + "LabelEnableHttpsHelp": "Umožní serveru počúvať na nastavenom HTTPS porte. K správnemu fungovaniu je nutné nakonfigurovať aj platný certifikát.", + "LabelEnableHttps": "Povoliť HTTPS", + "HeaderServerAddressSettings": "Nastavenie adresy servera", + "HeaderRemoteAccessSettings": "Nastavenie vzdialeného prístupu", + "HeaderHttpsSettings": "Nastavenia HTTPS", + "HeaderDVR": "DVR", + "SaveChanges": "Uložiť zmeny", + "MessageSyncPlayErrorMedia": "Povolenie synchronizácie prehrávania zlyhalo! Chyba média.", + "MessageSyncPlayErrorMissingSession": "Zapnutie synchronizácie prehrávania zlyhalo! Aktívna relácia nebola nájdená.", + "MessageSyncPlayErrorNoActivePlayer": "Nebol nájdený žiadny aktívny prehrávač. Synchronizácia prehrávania bola vypnutá.", + "MessageSyncPlayErrorAccessingGroups": "Pri načítaní zoznamu skupín sa vyskytla chyba.", + "MessageSyncPlayLibraryAccessDenied": "Prístup k tomuto obsahuje je obmedzený.", + "MessageSyncPlayJoinGroupDenied": "K použitiu synchronizácie prehrávania je vyžadované povolenie.", + "MessageSyncPlayCreateGroupDenied": "K vytvoreniu skupiny je požadované povolenie.", + "MessageSyncPlayGroupDoesNotExist": "Pripojenie ku skupine zlyhalo, pretože skupina neexistuje.", + "MessageSyncPlayPlaybackPermissionRequired": "K prehrávaniu je potrebné povolenie.", + "MessageSyncPlayNoGroupsAvailable": "Nie je dostupná žiadna skupina. Skúste najskôr začať niečo prehrávať.", + "MessageSyncPlayGroupWait": "Prehrávanie používateľa {0} sa načítava...", + "MessageSyncPlayUserLeft": "Používateľ {0} opustil skupinu.", + "MessageSyncPlayUserJoined": "Používateľ {0} sa pripojil k skupine.", + "MessageSyncPlayDisabled": "Synchronizácia prehrávania zakázana.", + "MessageSyncPlayEnabled": "Synchronizácia prehrávania povolená.", + "LabelSyncPlayAccess": "Prístup k synchronizácií prehrávania", + "LabelSyncPlayAccessNone": "Zakázať pre tohoto používateľa", + "LabelSyncPlayAccessJoinGroups": "Povoliť použivateľovi pripájať sa do skupín", + "LabelSyncPlayAccessCreateAndJoinGroups": "Povoliť používateľovi vytvárať a pripájať sa do skupín", + "LabelSyncPlayLeaveGroupDescription": "Zakázať synchronizáciu prehrávania", + "LabelSyncPlayLeaveGroup": "Opustiť skupinu", + "LabelSyncPlayNewGroupDescription": "Vytvoriť novú skupinu", + "LabelSyncPlayNewGroup": "Nová skupina", + "LabelSyncPlaySyncMethod": "Spôsob synchronizácie:", + "LabelSyncPlayPlaybackDiff": "Rozdiel v dobe prehrávania:", + "MillisecondsUnit": "ms", + "LabelSyncPlayTimeOffset": "Časový rozdiel so serverom:", + "HeaderSyncPlayEnabled": "Synchronizácia prehrávania je povolená", + "HeaderSyncPlaySelectGroup": "Pripojiť sa k skupine", + "SyncPlayAccessHelp": "Vyberte úroveň prístupu pre tohto používateľa k funkcií synchronizácie prehrávania. Synchronizácia prehrávania umožňuje zosynchronizovať prehrávanie s ostatnými zariadeniami.", + "EnableDetailsBannerHelp": "Zobrazí banner na vrchnej časti detailu položky.", + "EnableDetailsBanner": "Detail banneru" } diff --git a/src/strings/sl-si.json b/src/strings/sl-si.json index a692edbd3f..dde5007a51 100644 --- a/src/strings/sl-si.json +++ b/src/strings/sl-si.json @@ -100,10 +100,8 @@ "HeaderLiveTV": "TV v živo", "HeaderNextUp": "Sledi", "HeaderRecordingGroups": "Zbirke posnetkov", - "LabelIpAddressValue": "IP naslov: {0}", "LabelRunningTimeValue": "Čas trajanja: {0}", "MessageApplicationUpdated": "Jellyfin Server je bil posodobljen", - "MessageServerConfigurationUpdated": "Nastavitve strežnika so bile posodobljene", "Movies": "Filmi", "AddItemToCollectionHelp": "Dodajte elemente v zbirke tako, da jih poiščete in jih z desnim klikom ali dotikom menija dodate v zbirko.", "AllowedRemoteAddressesHelp": "Z vejico ločen seznam IP naslovov ali IP/maska omrežij, ki jim je dovoljen oddaljeni dostop. Če pustite prazno, bodo dovoljeni vsi oddaljeni naslovi.", @@ -127,10 +125,6 @@ "AudioSampleRateNotSupported": "Frekvenca vzorčenja zvoka ni podprta", "Auto": "Samodejno", "AutoBasedOnLanguageSetting": "Samodejno (na podlagi nastavitve jezika)", - "AutomaticallyConvertNewContent": "Samodejno pretvori novo vsebino", - "AutomaticallyConvertNewContentHelp": "Nova vsebina dodana v to mapo bo samodejno pretvorjena.", - "AutomaticallySyncNewContent": "Samodejno prenesi novo vsebino", - "AutomaticallySyncNewContentHelp": "Nova vsebina dodana v to mapo bo samodejno prenesena na to napravo.", "Backdrop": "Ozadje", "Backdrops": "Ozadja", "BestFit": "Najboljše prileganje", diff --git a/src/strings/sr.json b/src/strings/sr.json index 5691cc68eb..dc329c1309 100644 --- a/src/strings/sr.json +++ b/src/strings/sr.json @@ -36,7 +36,7 @@ "ButtonOpen": "Отвори", "ButtonOk": "Ок", "ButtonOff": "Искључи", - "ButtonNextTrack": "Следећа трака", + "ButtonNextTrack": "Следећа нумера", "ButtonNew": "Ново", "ButtonNetwork": "Мрежа", "ButtonMore": "Више", @@ -51,7 +51,7 @@ "ButtonFullscreen": "Пун екран", "ButtonForgotPassword": "Заборављена шифра", "ButtonFilter": "Филтер", - "ButtonDownload": "Скини (даунлоадуј)", + "ButtonDownload": "Преузми", "ButtonDown": "Доле", "ButtonDeleteImage": "Обриши слику", "ButtonDelete": "Обриши", @@ -75,7 +75,7 @@ "BoxRear": "Омот (позади)", "Box": "Омот", "Blacklist": "Црна листа", - "BirthPlaceValue": "Место рођења", + "BirthPlaceValue": "Место рођења: {0}", "BirthLocation": "Место рођења", "BirthDateValue": "Рођен", "AutoBasedOnLanguageSetting": "Аутоматски (зависи од подешавања језика)", @@ -96,5 +96,103 @@ "All": "Све", "Alerts": "Упозорења", "Aired": "Емитовано", - "Absolute": "Апсолутно" + "Absolute": "Апсолутно", + "Default": "Уобичајено", + "DeathDateValue": "Преминуо: {0}", + "DatePlayed": "Датум емитовања", + "DateAdded": "Датум додавања", + "CustomDlnaProfilesHelp": "Направите прилагођени профил да бисте циљали на нови уређај или прегазили системски профил.", + "CriticRating": "Оцена критике", + "CopyStreamURLError": "Десила се грешка приликом копирања адресе.", + "CopyStreamURLSuccess": "Адреса копирана успешно.", + "CopyStreamURL": "Копирајте адресу стрим-а", + "Continuing": "Наставља", + "ContinueWatching": "Натавите гледање", + "Connect": "Повежи", + "ConfirmEndPlayerSession": "Да ли желите да искључите Јеллифин {0}?", + "ConfirmDeletion": "ПОтврдите брисање", + "ConfirmDeleteItems": "Брисањем ових ставки избрисаћете их и из система и из ваше библиотеке медија. Јесте ли сигурни да желите да наставите?", + "ConfirmDeleteItem": "Брисањем ове ставке избрисаћете је и из система и из ваше библиотеке медија. Јесте ли сигурни да желите да наставите?", + "ConfirmDeleteImage": "Обришите слику?", + "ConfigureDateAdded": "Конфигуришите како се датум додавања одређује на контролној табли Јеллифин Сервер-а под Подешавања библиотеке", + "Composer": "Композитор", + "CommunityRating": "Рејтинг заједнице", + "ColorTransfer": "Промена боја", + "ColorSpace": "Простор боја", + "ColorPrimaries": "Основне боје", + "ClientSettings": "Подешавања клијента", + "CinemaModeConfigurationHelp": "Режим биоскопа доноси позоришно искуство директно у вашу дневну собу са могућношћу приказинвања трејлера и прилагођених увода пре главне проекције.", + "ChannelNumber": "Број канала", + "ChannelNameOnly": "Само {0} канал", + "ChannelAccessHelp": "Изаберите канале које желите да делите са овим корисником. Администратори ће моћи да уређују све канале помоћу менаџера метаподатака.", + "ChangingMetadataImageSettingsNewContent": "Промене метаподатака или поставки преузимања уметничког дела примењују се само на нови садржај додат у вашу библиотеку. Да бисте применили промене на постојеће наслове, мора ћете ручно освежити њихове метаподатке.", + "Categories": "Категорије", + "CancelSeries": "Откажи серију", + "CancelRecording": "Откажи снимање", + "ButtonWebsite": "Веб сајт", + "ButtonViewWebsite": "Погледајте веб сајт", + "ButtonUp": "Горе", + "ButtonUninstall": "Деинсталирај", + "ButtonTrailer": "Трејлер", + "ButtonSubtitles": "Титлови", + "ButtonSubmit": "Пошаљите", + "ButtonSplit": "Подели", + "ButtonStop": "Заустави", + "ButtonStart": "Почни", + "ButtonSort": "Сортирај", + "ButtonSignOut": "Одјавите се", + "ButtonSignIn": "Пријавите се", + "ButtonShutdown": "Искључи", + "ButtonShuffle": "Промешај", + "ButtonSettings": "Подешавања", + "ButtonSend": "Пошаљи", + "ButtonSelectView": "Изаберите приказ", + "ButtonSelectServer": "Иѕаберите сервер", + "ButtonSelectDirectory": "Изаберите Директоријум", + "ButtonSearch": "Тражи", + "ButtonScanAllLibraries": "Скенирај све библиотеке", + "ButtonSave": "Сачувај", + "ButtonRevoke": "Опозови", + "ButtonResume": "Настави", + "ButtonRestart": "Покрени поново", + "ButtonResetPassword": "Ресетуј шифру", + "ButtonResetEasyPassword": "Ресетујте једноставан ПИН код", + "ButtonRepeat": "Пусти поново", + "ButtonRename": "Преименуј", + "ButtonRemove": "Уклони", + "ButtonRefreshGuideData": "Освежи податке водича", + "ButtonRefresh": "Освежи", + "ButtonQuickStartGuide": "Кратки водич за почетак", + "ButtonProfile": "Профил", + "ButtonPreviousTrack": "Претходна нумера", + "ButtonPlay": "Пусти", + "ButtonEditOtherUserPreferences": "Уредите профил корисника, слику и личне поставке.", + "ButtonEditImages": "Уреди слике", + "ButtonEdit": "Уреди", + "BurnSubtitlesHelp": "Одређује да ли сервер треба да кодира титлове приликом транскодирања видео записа. Избегавање тога увелико ће побољшати перформансе. Изаберите Аутоматски да бисте снимили формате засноване на слици (VOBSUB, PGS, SUB, IDX) и одређене ASS или SSA титлове.", + "BoxSet": "Бокс сет", + "BookLibraryHelp": "Подржани су аудио и текстуалне књиге. Прегледајте {0} водич за именовање књига {1}.", + "Banner": "Банер", + "Backdrops": "Позадине", + "Backdrop": "Позадина", + "Auto": "Ауто", + "AuthProviderHelp": "Изаберите провајдера аутентификације који ће се користити за аутентификацију лозинке овог корисника.", + "AspectRatio": "Однос страна", + "AskAdminToCreateLibrary": "Затражите од администратора да креира библиотеку.", + "Ascending": "Узлазни", + "AsManyAsPossible": "Што је више могуће", + "Artist": "Уметник", + "Art": "Уметност", + "Anytime": "Било када", + "AlwaysPlaySubtitlesHelp": "Титлови који одговарају језичким преференцијама учитават ће се без обзира на језик звука.", + "AllowedRemoteAddressesHelp": "Листа одвојена зарезима ИП адреса или уноса ИП / мрежне маске за мреже којима ће се омогућити даљинско повезивање. Ако је празно, све удаљене адресе ће бити дозвољене.", + "AllowRemoteAccessHelp": "Ако није укључено, све удаљене везе биће блокиране.", + "AllowFfmpegThrottlingHelp": "Кад се транскодовање довољно удаљи од тренутне позиције репродукције, паузирајте поступак тако да троши мање ресурса. Ово је најкорисније када се гледа без тражења. Искључите ово ако имате проблема са репродукцијом.", + "AllowFfmpegThrottling": "Регулише Транскодовање", + "AllowOnTheFlySubtitleExtractionHelp": "Уграђени титлови се могу издвојити из видео записа и доставити клијентима у обичном тексту како би се спречило транскодирање видео записа. У неким системима ово може дуго трајати и може проузроковати да се репродукција видео записа заустави током екстракције. Онемогућите ово да се уграђени титлови уграђују видео транскодирањем када их клијент не подржава.", + "AllComplexFormats": "Сви сложени формати (ASS, SSA, VOBSUB, PGS, SUB, IDX)", + "AlbumArtist": "Извођач албума", + "Album": "Албум", + "AirDate": "Премијера", + "AdditionalNotificationServices": "Прегледајте каталог додатака да бисте инсталирали сервисе за обавештења." } diff --git a/src/strings/sv.json b/src/strings/sv.json index 7f6e1abc68..a2b1c68e0c 100644 --- a/src/strings/sv.json +++ b/src/strings/sv.json @@ -2,7 +2,7 @@ "AccessRestrictedTryAgainLater": "För närvarande är åtkomsten begränsad. Försök igen senare.", "Actor": "Skådespelare", "Add": "Lägg till", - "AddItemToCollectionHelp": "Lägg till objekt till samlingar genom att söka efter dem och använda deras högerklick- eller knack/tryck-meny för att lägga till dem.", + "AddItemToCollectionHelp": "Lägg till objekt i samlingar genom att söka efter dem och använda deras högerklick- eller pekmeny för att lägga till dem i en samling.", "AddToCollection": "Lägg till i samling", "AddToPlayQueue": "Lägg till i spelkö", "AddToPlaylist": "Lägg till i spellista", @@ -13,7 +13,7 @@ "Albums": "Album", "All": "Alla", "AllChannels": "Alla kanaler", - "AllComplexFormats": "Alla komplexa format (ASS, SSA, VOBSUB, PGS, SUB/IDX, etc.)", + "AllComplexFormats": "Alla komplexa format (ASS, SSA, VOBSUB, PGS, SUB/IDX, ...)", "AllEpisodes": "Alla avsnitt", "AllLanguages": "Alla språk", "AllLibraries": "Alla bibliotek", @@ -22,11 +22,11 @@ "AllowOnTheFlySubtitleExtractionHelp": "Inbäddade undertexter kan extraheras ur videor och skickas till klienter i textformat för att förhindra omkodning. I vissa system kan detta ta en lång tid och stoppa videouppspelningen under extraheringsprocessen. Avaktivera detta för att bränna in inbäddade undertexter genom omkodning när de inte stöds av klienten.", "AllowRemoteAccess": "Tillåt fjärranslutningar till denna Jellyfin-server.", "AllowRemoteAccessHelp": "Om avaktiverat så blockeras alla fjärranslutningar.", - "AlwaysPlaySubtitles": "Visa alltid undertexter", + "AlwaysPlaySubtitles": "Visa alltid", "AlwaysPlaySubtitlesHelp": "Undertexter på det önskade språket kommer att laddas oavsett ljudspårets språk.", "AnyLanguage": "Alla språk", "Anytime": "När som helst", - "AroundTime": "Runt {0}", + "AroundTime": "Runt", "Art": "Grafik", "Artists": "Artister", "AsManyAsPossible": "Så många som möjligt", @@ -40,13 +40,13 @@ "BirthDateValue": "Född: {0}", "BirthLocation": "Födelseort", "BirthPlaceValue": "Födelseort:{0}", - "BookLibraryHelp": "Ljud- och textböcker stöds. Läs {0}boknamngivningsguide{1}.", + "BookLibraryHelp": "Ljud- och textböcker stöds. Läs {0} boknamngivningsguiden {1}.", "Books": "Böcker", "Box": "Omslag", "BoxRear": "Omslag (baksida)", "Browse": "Bläddra", "BrowsePluginCatalogMessage": "Besök katalogen för att se tillgängliga tillägg.", - "BurnSubtitlesHelp": "Avgör ifall servern ska \"bränna in\" undertexterna under transkodning. Att undvika detta förbättrar prestandan avsevärt. Välj \"Automatisk\" för att bränna bild-baserade format (ex. VOBSUB, PGS, SUB/IDX, etc.) och vissa ASS/SSA-undertexter.", + "BurnSubtitlesHelp": "Avgör ifall servern ska \"bränna in\" undertexterna under transkodning. Att undvika detta förbättrar prestandan avsevärt. Välj \"Automatisk\" för att bränna bild-baserade format (ex. VOBSUB, PGS, SUB/IDX, ...) och vissa ASS/SSA-undertexter.", "ButtonAdd": "Lägg till", "ButtonAddMediaLibrary": "Lägg till mediabibliotek", "ButtonAddScheduledTaskTrigger": "Lägg till utlösare", @@ -898,7 +898,7 @@ "NumLocationsValue": "{0} mappar", "Off": "Av", "OneChannel": "En kanal", - "OnlyForcedSubtitles": "Endast tvingande undertexter", + "OnlyForcedSubtitles": "Endast påtvingad", "OnlyForcedSubtitlesHelp": "Endast undertexter markerade som tvingande kommer att laddas.", "OnlyImageFormats": "Endast bildbaserade format (VOBSUB, PGS, SUB, etc)", "OptionAdminUsers": "Administratörer", @@ -988,7 +988,7 @@ "OptionMissingEpisode": "Saknade avsnitt", "OptionMonday": "Måndag", "OptionNameSort": "Namn", - "OptionNew": "Ny...", + "OptionNew": "Ny…", "OptionNone": "Inga", "OptionOnAppStartup": "När servern startar", "OptionOnInterval": "Med visst intervall", @@ -1344,8 +1344,6 @@ "LabelSize": "Storlek:", "LabelServerName": "Servernamn:", "LabelSecureConnectionsMode": "Säker uppkopplings läge:", - "EnableFastImageFadeInHelp": "Aktivera snabbare fade-in animationer för laddade bilder", - "EnableFastImageFadeIn": "Snabb bild fade-in", "LabelPostProcessorArgumentsHelp": "Använd {path} som sökväg till inspelade filen.", "LabelPostProcessorArguments": "Post-processor kommandoradsargument:", "LabelDroppedFrames": "Tappade ramar:", @@ -1489,5 +1487,33 @@ "Episode": "Avsnitt", "ClientSettings": "Klientinställningar", "BoxSet": "Samlingsbox", - "Artist": "Artist" + "Artist": "Artist", + "ButtonTogglePlaylist": "Spellista", + "ButtonToggleContextMenu": "Mer", + "AlbumArtist": "Albumartist", + "LabelLibraryPageSize": "Bibliotekets sidstorlek:", + "LabelDeinterlaceMethod": "Deinterlacing-metod:", + "WeeklyAt": "{0}s vid {1}", + "LastSeen": "Senast sedd {0}", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", + "Filter": "Filter", + "New": "Ny", + "MessageUnauthorizedUser": "Du har inte behörighet att komma åt servern just nu. Kontakta din serveradministratör för mer information.", + "HeaderFavoritePlaylists": "Favoritspellista", + "OnWakeFromSleep": "Vid start från vilande läge", + "UnsupportedPlayback": "Jellyfin kan inte dekryptera inehåll skyddat av DRM men allt inehåll kommer ändå försökas, även skyddade titlar. Vissa filer kan se helt svart ut på grund av kryptering eller andra funktioner som inte stöds, till exempel interaktiva titlar.", + "LabelLibraryPageSizeHelp": "Sätter en begränsad sidstorlek i bibliotek. Sätt 0 för att avaktivera begränsad sidstorlek.", + "ApiKeysCaption": "Lista av aktiva API-nycklar", + "DeinterlaceMethodHelp": "Välj metod för borttagning av inflätning vid konvertering av inflätat inehåll.", + "TabDVR": "PVR", + "SaveChanges": "Spara ändringar", + "LabelRequireHttps": "Kräv HTTPS", + "LabelChromecastVersion": "Chromecast-version", + "LabelEnableHttpsHelp": "Gör det möjligt för servern att lyssna på den konfigurerade HTTPS-porten. Ett giltigt certifikat måste också konfigureras för att detta ska fungera.", + "LabelEnableHttps": "Aktivera HTTPS", + "HeaderServerAddressSettings": "Serveradressinställningar", + "HeaderRemoteAccessSettings": "Inställningar för fjärråtkomst", + "HeaderHttpsSettings": "HTTPS-inställningar", + "HeaderDVR": "PVR" } diff --git a/src/strings/tr.json b/src/strings/tr.json index 509c9be86d..d69391ce9a 100644 --- a/src/strings/tr.json +++ b/src/strings/tr.json @@ -317,7 +317,7 @@ "Director": "Yönetmen", "Directors": "Yöneticiler", "Disabled": "Deaktif", - "DisplayModeHelp": "Jellyfin’i çalıştırdığınız ekran türünü seçin.", + "DisplayModeHelp": "Arayüz için kullanılmasını istediğiniz şablonu seçin.", "DoNotRecord": "Kaydetme", "Down": "Aşağı", "Download": "İndir", @@ -365,7 +365,7 @@ "DisplayInMyMedia": "Ana ekranda görüntüleme", "DisplayInOtherHomeScreenSections": "En son medya gibi ana ekran bölümlerinde görüntüleyin ve izlemeye devam edin", "EnableBackdrops": "Arka planında", - "BurnSubtitlesHelp": "Altyazı formatına bağlı olarak video dönüştürülürken sunucunun altyazılarda yazıp yazmayacağını belirler. Altyazılarda yanmaktan kaçınmak, sunucu performansını iyileştirir. Görüntü tabanlı biçimleri (VOBSUB, PGS, SUB / IDX, vb.) Ve bazı ASS / SSA altyazılarını yazmak için Otomatik'i seçin.", + "BurnSubtitlesHelp": "Sunucunun video işlendiği esnada, altyazının görüntüye gömülmesini sağlar. Performansı çok düşürür, zorunda kalmadıkça bu özelliği seçmeyin. Görüntü tabanlı biçimleri (VOBSUB, PGS, SUB / IDX, vb.) Ve bazı ASS / SSA altyazıların görüntüye gömülmesi için Otomatik'i seçin.", "ConfirmDeleteItem": "Bu öğeyi silmek, onu hem dosya sisteminden hem de medya kütüphanenizden siler. Devam etmek istediğinize emin misiniz?", "ValueSpecialEpisodeName": "Özel - {0}", "DeviceAccessHelp": "Bu, yalnızca benzersiz şekilde tanımlanabilen ve tarayıcı erişimini engellemeyen cihazlar için geçerlidir. Kullanıcı cihazlarına erişimin filtrelenmesi, burada onaylanana kadar yeni cihazları kullanmalarını önler.", @@ -663,7 +663,7 @@ "Label3DFormat": "3D Formatı:", "Kids": "Çocuklar", "ItemCount": "{0} nesne", - "InstallingPackage": "Yükleniyor {0}", + "InstallingPackage": "Yükleniyor {0} (versiyon {1})", "Images": "Resimler", "Identify": "Tanımla", "Horizontal": "Yatay", @@ -672,12 +672,12 @@ "HeaderYears": "Yıl", "HeaderXmlSettings": "Xml Ayarları", "HeaderXmlDocumentAttributes": "Xml Döküman Öznitelikleri", - "HeaderXmlDocumentAttribute": "Xml Dökümanı Öznitelik", + "HeaderXmlDocumentAttribute": "Xml Döküman Özniteliği", "HeaderUser": "Kullanıcı", "HeaderUploadImage": "Resim Yükle", "HeaderUpcomingOnTV": "TV'de Yaklaşan", "HeaderTypeText": "Metin Gir", - "HeaderTunerDevices": "Tuner Cihazları", + "HeaderTunerDevices": "Alıcı Cihazları", "HeaderThisUserIsCurrentlyDisabled": "Bu kullanıcı şu anda pasif", "HeaderTags": "Etiketler", "HeaderSubtitleProfiles": "Altyazı Profilleri", @@ -712,5 +712,31 @@ "HeaderNavigation": "Navigasyon", "AllowFfmpegThrottling": "Video Kodlamasını Limitle", "Artist": "Oyuncu", - "Album": "Albüm" + "Album": "Albüm", + "EveryXMinutes": "Her {0} dakika", + "OnWakeFromSleep": "Uyku modundan çıkılarak uyanıldığında", + "DailyAt": "Her gün {0} 'de", + "LastSeen": "Son görülme {0}", + "WriteAccessRequired": "Jellyfin Sunucusu bu dizine yazma iznine ihtiyaç duymakta. Lütfen yazma izin ayarlarını kontrol edip tekrar deneyiniz.", + "PathNotFound": "Gösterilen yol bulunamadı. Lütfen gösterdiğiniz yolun doğru olduğundan emin olun ve tekrar deneyin.", + "EveryHour": "Her saat", + "EveryXHours": "Her {0} saatte bir", + "OnApplicationStartup": "Uygulama başladığında", + "HttpsRequiresCert": "Güvenli bağlantı kurulabilmesi için, Let's Encrypt gibi güvenilir bir sertifika sağlayıcısından edinilmiş sertifikayı tanıtmanız gerekmektedir. Lütfen bir sertifika tanımlayın ya da güvenli bağlantı seçeneğini devre dışı bırakın.", + "Home": "Anasayfa", + "HideWatchedContentFromLatestMedia": "İzlenmiş içeriği En Son Eklenenler klasöründe gösterme", + "HeaderTypeImageFetchers": "Görüntü İndirici", + "HeaderTracks": "Parçalar", + "HeaderSeriesStatus": "Dizi Durumu", + "HeaderSeriesOptions": "Dizi Seçenekleri", + "Episode": "Bölüm", + "DeinterlaceMethodHelp": "Tarama tipi görüntülerde titreşimi yok etmek için kullanılmasını istediğiniz metodu seçin.", + "ClientSettings": "Kullanıcı Ayarları", + "BoxSet": "Seri Filmler", + "AskAdminToCreateLibrary": "Kütüphane oluşturmak için yöneticiden izin iste.", + "AllowFfmpegThrottlingHelp": "Video dönüşüm işlemleri yeterince ilerlediyse kaynak tüketimini azaltmak için durdur. İleri/geri sarma işlemlerinin az yapıldığı durumlarda çok kullanışlıdır. Oynatım sorunları ile karşılaşırsanız bu özelliği kapatın.", + "AlbumArtist": "Sanatçı", + "HeaderTuners": "Alıcılar", + "HeaderTranscodingProfileHelp": "Kodlama gerekince hangi formatın kullanılacağını belirtmek için kodlama profili ekle.", + "ButtonTogglePlaylist": "Liste" } diff --git a/src/strings/uk.json b/src/strings/uk.json index 57bd678643..5a859a4aee 100644 --- a/src/strings/uk.json +++ b/src/strings/uk.json @@ -20,9 +20,9 @@ "HeaderAutomaticUpdates": "Автоматичне оновлення", "HeaderBooks": "Книги", "HeaderDeleteDevice": "Видалить пристрій", - "HeaderLatestEpisodes": "Останні епізоди", - "HeaderLatestMedia": "Останні медіа", - "HeaderLatestMovies": "Останні фільми", + "HeaderLatestEpisodes": "Нещодавно переглянуті серії", + "HeaderLatestMedia": "Нещодавно переглянуті", + "HeaderLatestMovies": "Нещодавні фільми", "HeaderLatestMusic": "Остання музика", "HeaderMovies": "Фільми", "HeaderSeasons": "Сезони", @@ -93,21 +93,71 @@ "ValueSeriesCount": "{0} серій", "ValueSongCount": "{0} пісень", "AddToPlaylist": "Додати до списку відтворення", - "AccessRestrictedTryAgainLater": "Доступ тимчасово заборонений. Спробуйте пізніше.", + "AccessRestrictedTryAgainLater": "На цей момент доступ заборонений. Повторіть спробу пізніше.", "Actor": "Виконавець", - "AllLanguages": "Всі мови", - "AllLibraries": "Всі бібліотеки", + "AllLanguages": "Усі мови", + "AllLibraries": "Усі бібліотеки", "AddToCollection": "Додати до колекції", "AddToPlayQueue": "Додати до черги відтворення", "All": "Всі", "AllChannels": "Всі канали", "AllEpisodes": "Всі епізоди", - "AllowRemoteAccess": "Дозволити віддалене підключення до цього сервера Jellyfin", - "AlwaysPlaySubtitles": "Завжди відтворювати субтитри", + "AllowRemoteAccess": "Дозволити віддалене підключення до цього сервера Jellyfin.", + "AlwaysPlaySubtitles": "Завжди вмикати субтитри", "AnyLanguage": "Будь-яка мова", "Anytime": "Завжди", "Add": "Додати", "AddedOnValue": "Додано", "Albums": "Альбоми", - "Absolute": "Абсолютний" + "Absolute": "Абсолютний", + "HeaderFavoriteEpisodes": "Улюблені серії", + "Movies": "Фільми", + "Collections": "Колекції", + "Folders": "Директорії", + "HeaderNextUp": "Наступний", + "HeaderAlbumArtists": "Виконавці альбомів", + "HeaderFavoriteSongs": "Улюблені пісні", + "Favorites": "Улюблені", + "HeaderFavoriteAlbums": "Улюблені альбоми", + "Genres": "Жанри", + "Books": "Книги", + "Artists": "Виконавці", + "HeaderLiveTV": "Ефірне ТБ", + "Channels": "Канали", + "HeaderFavoriteArtists": "Улюблені виконавці", + "HeaderFavoriteShows": "Улюблені шоу", + "HeaderContinueWatching": "Продовжити перегляд", + "AddItemToCollectionHelp": "Додайте елементи до колекції за допомогою пошуку або кліком правої кнопкої миші чи натисненням на меню.", + "AllowedRemoteAddressesHelp": "Список з комами, в якості розділювачів, визначає IP-адреси та IP/мережеві маски для мереж, яким дозволено підключатись віддалено. Якщо залишити строку пустою, то усі віддалені підключення будуть дозволені.", + "AllowRemoteAccessHelp": "Якщо не відмічено прапорцем, усі віддалені підключення будуть заблоковані.", + "AllowFfmpegThrottling": "Примусово обмежити перекодування", + "AllowMediaConversionHelp": "Надайте або забороніть доступ для можливості перетворення медіа.", + "AllowMediaConversion": "Дозволити перетворення медіа", + "Alerts": "Термінові сповіщення", + "AlbumArtist": "Виконавець альбому", + "Album": "Альбом", + "AdditionalNotificationServices": "Пошук у каталозі плагінів для встановлення додаткових сервісів сповіщень.", + "ShowYear": "Відобразити рік", + "ShowTitle": "Відобразити назву", + "Raised": "Піднятий", + "OptionResElement": "кожний елемент", + "DropShadow": "Тінь", + "Blacklist": "Чорний список", + "BirthLocation": "Місце народження", + "Banner": "Обкладинка", + "AutoBasedOnLanguageSetting": "Автоматично (на основі поточної мови)", + "Auto": "Автоматично", + "AuthProviderHelp": "Оберіть сервіс аутентифікації, який буде використаний з поточним паролем користувача.", + "Audio": "Аудіо", + "AttributeNew": "Новий", + "AspectRatio": "Відношення сторін", + "AskAdminToCreateLibrary": "Попросіть адміністратора створити бібліотеку.", + "Ascending": "У порядку зростання", + "AsManyAsPossible": "Настільки багато наскільки можливо", + "Artist": "Виконавець", + "Art": "Мистецтво", + "AllowOnTheFlySubtitleExtractionHelp": "Вбудовані субтитри можуть бути експортовані з відео і надіслані, по черзі, клієнтам у вигляді тексту. Це допоможе уникнути перекодування відео. На деяких системах, перекодування може зайняти тривалий час і зупинити відтворення відео, для того щоб забезпечити експортування. Вимкнення цієї функції дозволить вбудованим субтитрам бути інтегрованим у відео, під час перекодування, якщо вбудовані субтитри не підтримуються на стороні клієнта.", + "AllowOnTheFlySubtitleExtraction": "Дозволити експортування субтитрів «на льоту»", + "AllowHWTranscodingHelp": "Дозволити клієнту перекодування на «на льоту». Це дозволить відмикати перекодування, якщо вона вимагається сервером.", + "AllComplexFormats": "Усі складні формати (ASS, SSA, VOBSUB, PGS, SUB, IDX, …)" } diff --git a/src/strings/ur_PK.json b/src/strings/ur_PK.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/src/strings/ur_PK.json @@ -0,0 +1 @@ +{} diff --git a/src/strings/vi.json b/src/strings/vi.json index e32272bdaf..c7e5c61d84 100644 --- a/src/strings/vi.json +++ b/src/strings/vi.json @@ -30,7 +30,7 @@ "HeaderLatestEpisodes": "Các tập phim mới nhất", "HeaderLatestMovies": "Phim mới nhất", "HeaderRecentlyPlayed": "Phát gần đây", - "HeaderSeries": "Series:", + "HeaderSeries": "Series", "HeaderStatus": "Trạng thái", "HeaderSystemDlnaProfiles": "Hồ sơ hệ thống", "HeaderUsers": "dùng", @@ -42,7 +42,7 @@ "LabelEnableDlnaPlayTo": "Cho phép DLNA chạy để", "LabelEvent": "Sự kiện:", "LabelFinish": "Kết thúc", - "LabelLanguage": "Ngôn ngữ", + "LabelLanguage": "Ngôn ngữ:", "LabelName": "Tên:", "LabelNewPassword": "Mật khẩu mới:", "LabelNewPasswordConfirm": "Xác nhận mật khẩu mới:", @@ -51,7 +51,7 @@ "LabelSaveLocalMetadata": "Lưu các ảnh nghệ thuật và metadata vào trong các thư mục media", "LabelSaveLocalMetadataHelp": "Lưu các ảnh nghệ thuật và metadata vào trong các thư mục media, sẽ đưa chúng vào một nơi bạn có thể chỉnh sửa dễ dàng hơn.", "LabelTime": "Thời gian:", - "LabelYourFirstName": "Tên của Bạn", + "LabelYourFirstName": "Tên của bạn:", "LabelYoureDone": "Bạn đã hoàn thành!", "MaxParentalRatingHelp": "Nội dung với đánh giá cao hơn sẽ được ẩn đi từ người dùng này.", "MessageNothingHere": "Không có gì ở đây.", @@ -87,7 +87,7 @@ "OptionTrackName": "Tên bài", "ParentalRating": "Parental Rating", "PasswordMatchError": "Mật khẩu và mật khẩu xác nhận cần phải khớp nhau .", - "PasswordResetComplete": "Mật khẩu đã được reset", + "PasswordResetComplete": "Mật khẩu đã được cài đặt lại.", "PasswordResetConfirmation": "Bạn có chắc muốn reset mật khẩu?", "PasswordSaved": "Mật khẩu đã được lưu.", "Saturday": "Thứ Bảy", @@ -116,7 +116,7 @@ "ThisWizardWillGuideYou": "Thủ thuật này sẽ hướng dẫn quá trình cài đặt cho bạn. Để bắt đầu, vui lòng lựa chọn ngôn ngữ bạn ưa thích.", "UninstallPluginConfirmation": "Bạn có chắc muốn gỡ bỏ{0}?", "UninstallPluginHeader": "Gỡ bỏ Plugin", - "AccessRestrictedTryAgainLater": "Truy cập hiện đang hạn chế. Hãy thử lại sau.", + "AccessRestrictedTryAgainLater": "Truy cập hiện đang bị hạn chế. Hãy thử lại sau.", "AddToCollection": "Thêm vào bộ sưu tập", "Actor": "Diễn viên", "ButtonRevoke": "Thu hồi", @@ -149,7 +149,7 @@ "ButtonFullscreen": "Toàn màn hình", "ButtonForgotPassword": "Quên mật khẩu", "ButtonFilter": "Lọc", - "ButtonEditOtherUserPreferences": "Chỉnh sửa hồ sơ, hình ảnh và sở thích cá nhân.", + "ButtonEditOtherUserPreferences": "Chỉnh sửa thông tin, hình ảnh và sở thích cá nhân.", "ButtonEditImages": "Sửa hình ảnh", "ButtonEdit": "Sửa", "ButtonDownload": "Tải", @@ -167,11 +167,11 @@ "ButtonAddScheduledTaskTrigger": "Thêm kích hoạt", "ButtonAddMediaLibrary": "Thêm thư viện Media", "ButtonAddImage": "Thêm hình ảnh", - "BurnSubtitlesHelp": "Xác định xem máy chủ có ghi phụ đề khi chuyển đổi video hay không tùy thuộc vào định dạng phụ đề. Tránh ghi trong phụ đề sẽ cải thiện hiệu suất máy chủ. Chọn Tự động để ghi hình ảnh dựa trên các định dạng (VOBSUB, PGS, SUB / IDX, v.v.) và phụ đề ASS/SSA nhất định.", + "BurnSubtitlesHelp": "Xác định xem máy chủ có nên ghi phụ đề khi chuyển đổi video hay không. Tránh thực hiện việc này sẽ cải thiện hiệu suất máy chủ đáng kể. Chọn Tự động để ghi các phụ đề có định dạng dựa trên hình ảnh (VOBSUB, PGS, SUB, IDX, ...) và một vài phụ đề ASS/SSA nhất định.", "Browse": "Duyệt", "BoxRear": "Hộp (mặt sau)", "Books": "Sách", - "BookLibraryHelp": "Âm thanh và sách văn bản được hỗ trợ. Xem lại {0}hướng dẫn đặt tên sách{1}.", + "BookLibraryHelp": "Âm thanh và sách văn bản được hỗ trợ. Xem lại {0} hướng dẫn đặt tên sách {1}.", "Blacklist": "Danh sách đen", "BirthPlaceValue": "Nơi sinh: {0}", "BirthLocation": "Nơi sinh", @@ -188,15 +188,15 @@ "Ascending": "Tăng dần", "AsManyAsPossible": "Càng nhiều càng tốt", "Artists": "Nghệ Sĩ", - "AroundTime": "Xunh quanh {0}", + "AroundTime": "Khoảng", "Anytime": "Bất cứ lúc nào", - "AnyLanguage": "Bất kỳ ngôn ngữ", + "AnyLanguage": "Ngôn Ngữ Bất Kỳ", "AlwaysPlaySubtitlesHelp": "Phụ đề phù hợp với sở thích ngôn ngữ sẽ được tải bất kể ngôn ngữ âm thanh.", "AlwaysPlaySubtitles": "Luôn hiển thị phụ đề", "AllowedRemoteAddressesHelp": "Danh sách địa chỉ IP được phân tách bằng dấu phẩy hoặc các mục IP/netmask cho các mạng sẽ được phép kết nối từ xa. Nếu để trống, tất cả các địa chỉ sẽ được cho phép.", "AllowRemoteAccessHelp": "Nếu không được chọn, tất cả các kết nối từ xa sẽ bị chặn.", "AllowRemoteAccess": "Cho phép kết nối từ xa đến Máy chủ Jellyfin.", - "AllowOnTheFlySubtitleExtractionHelp": "Phụ đề nhúng có thể được trích xuất từ video và dùng như văn bản thuần túy để giúp ngăn chặn chuyển mã video. Trên một số hệ thống, việc này có thể mất nhiều thời gian và khiến quá trình phát video bị đình trệ trong quá trình trích xuất. Vô hiệu hóa điều này để có phụ đề nhúng được ghi trong chuyển mã video khi chúng không được thiết bị khách hỗ trợ.", + "AllowOnTheFlySubtitleExtractionHelp": "Phụ đề nhúng có thể được trích xuất từ video và dùng như văn bản thuần túy để giúp ngăn chặn chuyển mã video. Trên một số hệ thống, việc này có thể mất nhiều thời gian và khiến quá trình phát video bị đình trệ trong quá trình trích xuất. Vô hiệu hóa điều này để có phụ đề nhúng được ghi trong video khi chuyển mã trong trường hợp chúng không được hỗ trợ trên thiết bị phát.", "AllowOnTheFlySubtitleExtraction": "Cho phép trích xuất phụ đề trực tiếp", "AllowMediaConversionHelp": "Cấp hoặc từ chối truy cập vào tính năng chuyển đổi media.", "AllowMediaConversion": "Cho phép chuyển đổi media", @@ -204,7 +204,7 @@ "AllLibraries": "Tất cả các thư viện", "AllLanguages": "Tất cả các ngôn ngữ", "AllEpisodes": "Tất cả các tập phim", - "AllComplexFormats": "Tất cả các định dạng phức tạp (ASS, SSA, VOBSUB, PGS, SUB / IDX, v.v.)", + "AllComplexFormats": "Tất cả các định dạng phức tạp (ASS, SSA, VOBSUB, PGS, SUB, IDX, ...)", "AllChannels": "Tất cả các kênh", "Alerts": "Cảnh Báo", "Albums": "Albums", @@ -215,7 +215,7 @@ "AddToPlaylist": "Thêm vào danh sách phát", "AddToPlayQueue": "Thêm vào hàng đợi", "AddItemToCollectionHelp": "Thêm các mục vào bộ sưu tập bằng cách tìm kiếm và nhấp chuột phải hoặc nhấn vào menu để thêm chúng vào bộ sưu tập.", - "Absolute": "Tuyệt Đối", + "Absolute": "Tuyệt đối", "ButtonSend": "Gửi", "ButtonSelectView": "Chọn chế độ xem", "ButtonSelectServer": "Chọn máy chủ", @@ -236,5 +236,553 @@ "ButtonStart": "Bắt đầu", "ButtonSignIn": "Đăng nhập", "ButtonShutdown": "Tắt", - "ButtonSettings": "Cài đặt" + "ButtonSettings": "Cài đặt", + "DefaultErrorMessage": "Có lỗi xảy ra trong lúc xử lý yêu cầu của bạn. Xin hãy thử lại sau.", + "Default": "Mặc định", + "DeathDateValue": "Không hoạt động: {0}", + "DatePlayed": "Ngày phát", + "DateAdded": "Ngày thêm vào", + "CustomDlnaProfilesHelp": "Tạo một bộ thiết lập tuỳ chọn dành cho một thiết bị mới hoặc thay thế một thiết lập hệ thống.", + "CriticRating": "Đánh giá phê bình", + "CopyStreamURLError": "Có lỗi xảy ra lúc sao chép URL.", + "CopyStreamURLSuccess": "URL đã được sao chép.", + "CopyStreamURL": "Sao Chép URL Phát Sóng", + "Continuing": "Tiếp tục", + "ContinueWatching": "Tiếp tục xem", + "Connect": "Kết nối", + "ConfirmEndPlayerSession": "Bạn có muốn tắt máy chủ Jellyfin trên {0}?", + "ConfirmDeletion": "Xác Nhận Xoá", + "ConfirmDeleteItems": "Xoá những mục này sẽ xoá chúng khỏi ổ cứng và thư viện của bạn. Bạn có chắc chắn muốn tiếp tục?", + "ConfirmDeleteItem": "Xoá mục này sẽ xoá nó khỏi ổ cứng và thư viện của bạn. Bạn có chắc chắn muốn tiếp tục?", + "ConfirmDeleteImage": "Bạn có chắc chắn xoá ảnh này?", + "ConfigureDateAdded": "Thiết lập cách xác định \"ngày thêm vào\" trong mục cài đặt Thư Viện của phần quản lý máy chủ Jellyfin", + "Composer": "Tác giả", + "CommunityRating": "Đánh giá của cộng đồng", + "ColorTransfer": "Chuyển đổi màu", + "ColorSpace": "Bộ mã màu", + "ColorPrimaries": "Những màu chủ đạo", + "Collections": "Bộ sưu tập", + "ClientSettings": "Cài đặt thiết bị phát", + "CinemaModeConfigurationHelp": "Chế độ Cinema giúp bạn mang lại trải nghiệm rạp chiếu phim ngay tại phòng khách với khả năng phát trailers và những đoạn mở đầu tuỳ chọn trước phần chính của bộ phim.", + "ChannelNumber": "Kênh số", + "Channels": "Kênh", + "ChannelNameOnly": "Chỉ kênh {0}", + "ChannelAccessHelp": "Chọn những kênh để chia vẻ với người dùng này. Người quản lý sẽ có thể thay đổi toàn bộ kênh bằng cách sử dụng bộ quản lý thông tin.", + "ChangingMetadataImageSettingsNewContent": "Thay đổi về thiết lập của việc tải thông tin hoặc hình ảnh sẽ chỉ có tác dụng với những nội dung mới được thêm vào thư viện. Để những thiết lập mới có tác dụng với nội dung đã có sẵn, bạn sẽ phải cập nhật lại thông tin của chúng.", + "CancelSeries": "Ngưng series", + "ButtonTogglePlaylist": "Danh sách phát", + "ButtonToggleContextMenu": "Thêm", + "BoxSet": "Tuyển tập", + "Box": "Hộp", + "Banner": "Ảnh bìa", + "Art": "Nghệ thuật", + "Artist": "Nghệ Sĩ", + "AllowFfmpegThrottlingHelp": "Tạm dừng quá trình chuyển mã hoặc chuyển đổi định dạng để tiết kiệm tài nguyên máy chủ khi việc này đã đủ để phát so với vị trí hiện tại. Điều này hữu hiệu khi không tua nhanh thường xuyên lúc nghe nhạc hoặc xem phim. Hãy tắt tính năng này nếu có hiện tượng giật lag khi nghe nhạc hoặc xem phim.", + "AllowFfmpegThrottling": "Điều tiết sự chuyển mã", + "AlbumArtist": "Nghệ sĩ Album", + "Album": "Album", + "DisplayMissingEpisodesWithinSeasonsHelp": "Thiết lập này cũng phải được kích hoạt trong thiết lập máy chủ dành cho thư viện phim bộ.", + "DisplayMissingEpisodesWithinSeasons": "Hiển thị những tập phim bị thiếu trong mỗi phần", + "DisplayInOtherHomeScreenSections": "Những phần hiển thị trên trang chính như là nội dung mới nhất và nội dung tiếp theo", + "DisplayInMyMedia": "Hiển thị trên trang chính", + "Display": "Hiển thị", + "Dislike": "Không thích", + "Disconnect": "Ngắt kết nối", + "Disc": "Đĩa", + "Disabled": "Đã vô hiệu hoá", + "Directors": "Đạo Diễn", + "Director": "Đạo Diễn", + "DirectStreaming": "Phát trực tuyến", + "DirectStreamHelp2": "Phát trực tuyến sử dụng rất ít tài nguyên máy chủ mà không giảm chất lượng video.", + "DirectStreamHelp1": "Nội dung này tương thích với thiết bị về độ phân giải và dạng mã hoá (H.264, AC3, v.v.), nhưng lại không tương tích định dạng (mkv, avi, wmv, v.v.). Video sẽ được chuyển đổi định dạng trực tiếp ngay trước khi phát trên thiết bị.", + "DirectPlaying": "Phát trực tiếp", + "DeviceAccessHelp": "Thiết lập này chỉ áp dụng cho những thiết bị có thể định danh và sẽ không chặn được truy cập từ trình duyệt. Chọn lọc thiết bị người dùng sẽ chặn người dùng này truy cập từ những thiết bị mới cho đến khi được duyệt.", + "DetectingDevices": "Đang tìm kiếm thiết bị", + "Desktop": "Máy tính", + "Descending": "Giảm dần", + "Depressed": "Hạ xuống", + "DeleteUserConfirmation": "Bạn có chắc chắn muốn xoá người dùng này?", + "DeleteMedia": "Xoá nội dung", + "DeleteDeviceConfirmation": "Bạn có chắc chắn muốn xoá thiết bị này? Nó sẽ xuất hiện lại khi người dùng đăng nhập bằng thiết bị đó.", + "DeinterlaceMethodHelp": "Chọn phương pháp khử xen kẽ khi chuyển mã những nội dung sử dụng phương pháp quét xen kẽ.", + "DefaultSubtitlesHelp": "Phụ đề được sử dụng dựa vào thiết lập mặc định (default) và bắt buộc (forced) trong phần thông tin bổ trợ. Tuỳ chọn ưu tiên ngôn ngữ sẽ có tác dụng khi có nhiều phụ đề khác nhau.", + "DefaultMetadataLangaugeDescription": "Đây là thiết lập mặc định chung, bạn có thể tuỳ chỉnh thiết lập riêng cho từng thư viện.", + "DisplayModeHelp": "Chọn kiểu bố trí giao diện mà bạn muốn.", + "Download": "Tải xuống", + "Down": "Xuống", + "DoNotRecord": "Không ghi lại", + "EnableCinemaMode": "Chế độ rạp phim", + "EnableBackdropsHelp": "Hiển thị phông nền phía sau một số trang khi xem thư viện.", + "EnableBackdrops": "Phông nền", + "EditSubtitles": "Chỉnh sửa phụ đề", + "EditMetadata": "Chỉnh sửa thông tin", + "EditImages": "Chỉnh sửa hình ảnh", + "Edit": "Chỉnh sửa", + "EasyPasswordHelp": "Mã PIN tiện lợi được sử dụng cho việc truy cập offline trên những thiết bị được hỗ trợ và cũng có thể sử dụng dành cho truy cập dễ dàng trong nội mạng.", + "DropShadow": "Đổ Bóng", + "DrmChannelsNotImported": "Những kênh được bảo vệ bản quyền sẽ không được nhập vào.", + "DownloadsValue": "{0} đã tải xuống", + "EnableColorCodedBackgrounds": "Màu nền theo loại kênh", + "HeaderDateIssued": "Ngày Phát Hành", + "HeaderContinueWatching": "Tiếp Tục Xem", + "HeaderContinueListening": "Tiếp Tục Nghe", + "HeaderCodecProfileHelp": "Bộ giải mã chỉ ra những kiểu mã hoá nhất định mà một thiết bị có thể phát. Nếu một nội dung không thể phát, nó sẽ được chuyển mã, thậm chí nếu kiểu mã hoá đó được thiết lập phát trực tiếp.", + "HeaderContainerProfileHelp": "Bộ định dạng chỉ ra những định dạng nhất định mà một thiết bị có thể phát. Nếu nội dung có định dạng không thể phát, nội dung sẽ được chuyển đổi định dạng, kể cả khi định dạng đó được thiết lập phát trực tiếp.", + "HeaderContainerProfile": "Bộ Định Dạng", + "HeaderConnectionFailure": "Kế Nối Thất Bại", + "HeaderConnectToServer": "Kết Nối Đến Máy Chủ", + "HeaderConfirmRevokeApiKey": "Thu Hồi Quyền Truy Cập API Key", + "HeaderConfirmProfileDeletion": "Xác Nhận Xoá Hồ Sơ", + "HeaderConfirmPluginInstallation": "Xác Nhận Cài Đặt Plugin", + "HeaderConfigureRemoteAccess": "Thiết Lập Truy Cập Từ Xa", + "HeaderCodecProfile": "Bộ Giải Mã", + "HeaderChapterImages": "Hình Ảnh Phân Đoạn", + "HeaderChannels": "Kênh", + "HeaderChannelAccess": "Quyền Truy Cập Kênh", + "HeaderCastCrew": "Diễn Viên & Ê-kíp", + "HeaderCastAndCrew": "Diễn Viên & Ê-kíp", + "HeaderCancelSeries": "Huỷ Series", + "HeaderCancelRecording": "Dừng Ghi Âm/Ghi Hình", + "HeaderBranding": "Tạo Thương Hiệu", + "HeaderBooks": "Sách", + "HeaderBlockItemsWithNoRating": "Khoá những mục không có thông tin đánh giá hoặc thông tin không đáng tin cậy:", + "HeaderAudioSettings": "Cài Đặt Âm Thanh", + "HeaderAudioBooks": "Sách Nói", + "HeaderAppearsOn": "Xuất Bản Vào", + "HeaderApp": "Ứng dụng", + "HeaderApiKeysHelp": "Những ứng dụng khác cần có \"API key\" để tương tác với máy chủ Jellyfin. Những mã này được tạo ra bằng cách đăng nhập vào máy chủ Jellyfin, hoặc bạn có thể cung cấp cho ứng dụng một mã truy cập.", + "HeaderApiKeys": "API Keys", + "HeaderApiKey": "API Key", + "HeaderAllowMediaDeletionFrom": "Cho Phép Xoá Nội Dung", + "HeaderAlert": "Thông Báo", + "HeaderAlbums": "Albums", + "HeaderAlbumArtists": "Nghệ Sĩ Album", + "HeaderAdmin": "Quản Trị", + "HeaderAdditionalParts": "Phần Bổ Sung", + "HeaderAddUpdateImage": "Thêm/Thay Đổi Hình Ảnh", + "HeaderAddToPlaylist": "Thêm vào Danh Sách Phát", + "HeaderAddToCollection": "Thêm vào Bộ Sưu Tập", + "HeaderAddScheduledTaskTrigger": "Thêm Kích Hoạt Tác Vụ", + "HeaderActivity": "Hoạt Động", + "HeaderActiveRecordings": "Bản Ghi Hình/Ghi Âm Đang Hoạt Động", + "HardwareAccelerationWarning": "Bật tính năng hỗ trợ từ phần cứng có thể gây ra sự bất ổn trong một vài môi trường. Hãy chắc chắn rằng điều hành và driver thiết bị đồ hoạ đã được cập nhật mới nhất. Nếu gặp vấn đề khi phát video sau khi bật tính năng này, bạn nên tắt tính năng này.", + "HeaderActiveDevices": "Thiết Bị Đang Hoạt Động", + "HeaderAccessScheduleHelp": "Tạo một thời gian biểu để giới hạn quyền truy cập vào một số khung giờ nhất định.", + "HeaderAccessSchedule": "Thời Gian Truy Cập", + "HandledByProxy": "Được xử lý bằng phương thức đảo ngược proxy", + "HDPrograms": "Chương trình chất lượng cao (HD)", + "EncoderPresetHelp": "Chọn một giá trị nhanh hơn để cải thiện hiệu suất máy chủ, hoặc một giá trị chậm hơn để tăng chất lượng video.", + "H264CrfHelp": "Hệ Số Tỉ Lệ Cố Định (Constant Rate Factor (CRF)) là thiết lập chất lượng mặc định dành cho bộ mã hoá x264. Bạn có thể điều chỉnh giá trị trong khoảng 0 đến 51, trong đó giá trị càng nhỏ thì chất lượng càng tốt (đồng nghĩa với việc dung lượng tập tin lớn hơn). Giá trị vừa phải nằm trong khoảng từ 18 đến 28. Giá trị mặc định dành cho x264 là 23, vì thế bạn có thể sử dụng nó để bắt đầu điều chỉnh cho phù hợp.", + "GuideProviderSelectListings": "Chọn Danh Sách", + "GuideProviderLogin": "Đăng nhập", + "Guide": "Lịch phát sóng", + "GuestStar": "Ngôi sao khách mời", + "GroupVersions": "Chia nhóm theo phiên bản", + "GroupBySeries": "Chia nhóm theo bộ", + "Genres": "Thể loại", + "Genre": "Thể loại", + "General": "Tổng Hợp", + "Fullscreen": "Toàn màn hình", + "Friday": "Thứ Sáu", + "FormatValue": "Định dạng: {0}", + "Folders": "Thư mục", + "FolderTypeUnset": "Nội Dung Tổng Hợp", + "FolderTypeMusicVideos": "Video Âm Nhạc", + "FolderTypeMusic": "Nhạc", + "FolderTypeMovies": "Phim lẻ", + "FolderTypeBooks": "Sách", + "Filters": "Bộ lọc", + "File": "Tập tin", + "FetchingData": "Đang tải thêm thông tin", + "Features": "Mục đặc trưng", + "Favorites": "Mục yêu thích", + "Favorite": "Mục yêu thích", + "FastForward": "Tua nhanh", + "FFmpegSavePathNotFound": "Máy chủ không thể tìm thấy chương trình FFmpeg trong đường dẫn bạn đã nhập. FFprobe thì cũng cần thiết và phải nằm trong cùng thư mục bên trên. Những phần mềm này thường được tổng hợp cùng nhau trong một lượt tải. Hãy thử lại sau khi kiểm tra đường dẫn.", + "Extras": "Đặc biệt", + "ExtractChapterImagesHelp": "Trích xuất hình ảnh từ những phân đoạn chính sẽ cho phép thiết bị phát hiển thị bảng lựa chọn cảnh thông qua những hình ảnh đó. Quá trình đó có thể chậm, cần nhiều tài nguyên máy chủ, và có thể cần một vài gigabytes trống. Điều này sẽ được thực hiện khi video được tìm thấy, và cũng như trong tác vụ hàng đêm. Thời gian thực hiện tác vụ này có thể điều chỉnh trong phần cài đặt tác vụ thường xuyên. Không nên thực hiện quá trình này trong giờ sử dụng cao điểm.", + "ExtraLarge": "Rất Lớn", + "ExitFullscreen": "Thoát khỏi chế độ toàn màn hình", + "EveryNDays": "Mỗi {0} ngày", + "ErrorSavingTvProvider": "Có lỗi xảy ra khi lưu thông tin của nhà cung cấp TV này. Hãy thử lại khi chắc chắn rằng nó có thể truy cập.", + "ErrorPleaseSelectLineup": "Hãy chọn một danh sách và thử lại. Nếu không có danh sách nào sẵn sàng, hãy chắc chắn rằng thông tin tài khoản, mật khẩu, và mã bưu điện đều chính xác.", + "ErrorMessageStartHourGreaterThanEnd": "Thời gian kết thúc phải lớn hơn thời gian bắt đầu.", + "ErrorGettingTvLineups": "Có lỗi xảy ra khi tải danh sách TV này. Hãy thử lại khi chắc chắn rằng thông tin của bạn chính xác.", + "ErrorDeletingItem": "Có lỗi xảy ra khi xoá mục này khỏi máy chủ Jellyfin. Hãy thử lại sau khi kiểm tra chắc chắn rằng máy chủ Jellyfin có quyền ghi/xoá vào thư mục nội dung.", + "ErrorAddingXmlTvFile": "Có lỗi xảy ra khi truy cập tài liệu XMLTV. Hãy thử lại khi chắc chắn rằng tài liệu này tồn tại.", + "ErrorAddingTunerDevice": "Có lỗi xảy ra khi thêm vào thiết bị bắt sóng này. Hãy thử lại khi chắc chắn rằng nó có thể truy cập.", + "ErrorAddingMediaPathToVirtualFolder": "Có lỗi xảy ra khi thêm đường dẫn này. Hãy chắc chắn rằng đường dẫn này đúng và máy chủ Jellyfin có quyền truy cập dữ liệu ở đường dẫn đó.", + "ErrorAddingListingsToSchedulesDirect": "Có lỗi xảy ra khi thêm danh sách này vào tài khoản Schedules Direct của bạn. Schedules Direct chỉ cho phép một số lượng danh sách nhất định mỗi tài khoản. Bạn có thể cần phải đăng nhập vào trang web của Schedules Direct và xoá những danh sách khác trước khi có thể thêm danh sách mới.", + "Episodes": "Tập", + "Episode": "Tập", + "EndsAtValue": "Kết thúc tại {0}", + "Ended": "Đã kết thúc", + "EnableThemeVideosHelp": "Phát nền những video chủ đề khi lướt qua thư viện nội dung.", + "EnableThemeVideos": "Video chủ đề", + "EnableThemeSongsHelp": "Phát những bài hát chủ đề khi lướt qua thư viện nội dung.", + "EnableThemeSongs": "Nhạc chủ đề", + "EnableStreamLoopingHelp": "Cho phép tính năng này nếu live streams chỉ dài một vài giây và cần được lặp lại liên tục. Bật tính năng này khi không cần thiết có thể gây ra nhiều vấn đề khác.", + "EnableStreamLooping": "Tự động phát lại live streams", + "EnablePhotosHelp": "Hình ảnh sẽ được nhận diện và hiển thị bên cạnh những nội dung media.", + "EnablePhotos": "Hiển thị hình ảnh", + "EnableNextVideoInfoOverlayHelp": "Khi ở phần cuối video, hiển thị thông tin về video tiếp theo trong danh sách phát hiện tại.", + "EnableNextVideoInfoOverlay": "Hiển thị thông tin video tiếp theo ở phần cuối video", + "EnableHardwareEncoding": "Sử dụng phần cứng để hỗ trợ chuyển mã", + "EnableExternalVideoPlayersHelp": "Phần mềm phát video từ thiết bị sẽ được hiển thị khi bắt đầu phát video.", + "EnableExternalVideoPlayers": "Sử dụng phần mềm phát video từ thiết bị", + "EnableDisplayMirroring": "Cho phép trình chiếu trên thiết bị khác", + "HeaderProfileInformation": "Thông Tin Hồ Sơ", + "HeaderProfile": "Hồ Sơ", + "HeaderPreferredMetadataLanguage": "Ngôn Ngữ Thông Tin Bổ Trợ Yêu Thích", + "HeaderPluginInstallation": "Cài Đặt Plugin", + "HeaderPleaseSignIn": "Hãy đăng nhập", + "HeaderPlaybackError": "Lỗi Phát Sóng", + "HeaderPlayback": "Phát Nội Dung", + "HeaderPlayOn": "Phát Trên", + "HeaderPlayAll": "Phát Tất Cả", + "HeaderPinCodeReset": "Đặt Lại Mã PIN", + "HeaderPhotoAlbums": "Album Ảnh", + "HeaderPeople": "Nhân Vật", + "HeaderPendingInvitations": "Những Lời Mời Đang Chờ", + "HeaderPaths": "Đường Dẫn", + "HeaderPasswordReset": "Đặt Lại Mật Khẩu", + "HeaderPassword": "Mật Khẩu", + "HeaderParentalRatings": "Đánh Giá Của Phụ Huynh", + "HeaderOtherItems": "Những Mục Khác", + "HeaderOnNow": "Phát Sóng Hiện Tại", + "HeaderNextVideoPlayingInValue": "Video Tiếp Theo Sẽ Phát trong {0}", + "HeaderNextUp": "Tiếp Theo", + "HeaderNextEpisodePlayingInValue": "Tập Tiếp Theo Sẽ Được Chạy trong {0}", + "HeaderNewDevices": "Thiết Bị Mới", + "HeaderNewApiKey": "Mã API Mới", + "HeaderNavigation": "Điều Hướng", + "HeaderMyMediaSmall": "Nội Dung Của Tôi (cỡ nhỏ)", + "HeaderMyMedia": "Nội Dung Của Tôi", + "HeaderMyDevice": "Thiết Bị Của Tôi", + "HeaderMusicVideos": "Video Âm Nhạc", + "HeaderMusicQuality": "Chất Lượng Âm Nhạc", + "HeaderMovies": "Phim", + "HeaderMoreLikeThis": "Nội Dung Tương Tự", + "HeaderMetadataSettings": "Cài Đặt Dữ Liệu Bổ Trợ", + "HeaderMediaInfo": "Thông Tin Nội Dung", + "HeaderMediaFolders": "Thư Mục Chứa Nội Dung", + "HeaderMedia": "Nội Dung", + "HeaderLoginFailure": "Đăng Nhập Không Thành Công", + "HeaderLiveTvTunerSetup": "Cài Đặt Bộ Bắt Sóng TV", + "HeaderLiveTv": "Truyền Hình Trực Tiếp", + "HeaderLiveTV": "Truyền Hình Trực Tiếp", + "HeaderLibrarySettings": "Cài Đặt Thư Viện", + "HeaderLibraryOrder": "Thứ Tự Thư Viện", + "HeaderLibraryFolders": "Thư Mục Của Thư Viện", + "HeaderLibraryAccess": "Truy Cập Thư Viện", + "HeaderLibraries": "Thư Viện", + "HeaderLatestRecordings": "Bản Ghi Âm/Ghi Hình Mới Nhất", + "HeaderLatestMusic": "Âm Nhạc Mới Nhất", + "HeaderLatestMedia": "Nội Dung Mới Nhất", + "HeaderKodiMetadataHelp": "Để bật hoặc tắt dữ liệu từ NFO, hãy chỉnh sửa thư viện trong phần cài đặt thư viện của Jellyfin và điều chỉnh phần lưu trữ dữ liệu bổ trợ.", + "HeaderKeepSeries": "Lưu Series", + "HeaderKeepRecording": "Tiếp Tục Ghi Âm/Ghi Hình", + "HeaderItems": "Mục", + "HeaderInstantMix": "Trộn Lẫn Nhanh", + "HeaderInstall": "Cài Đặt", + "HeaderImageSettings": "Thiết Lập Hình Ảnh", + "HeaderImageOptions": "Tuỳ Chọn Hình Ảnh", + "HeaderIdentifyItemHelp": "Hãy nhập một hoặc nhiều điều kiện tìm kiếm. Xoá điều kiện để tăng số lượng kết quả tìm kiếm.", + "HeaderIdentificationHeader": "Thông Tin Nhận Biết Trong Phần Đầu", + "HeaderIdentificationCriteriaHelp": "Nhập vào ít nhật một thông tin nhận biết.", + "HeaderIdentification": "Thông Tin Nhận Biết", + "HeaderHttpHeaders": "HTTP Headers", + "HeaderHome": "Trang Chủ", + "HeaderGuideProviders": "Nhà Cung Cấp Lịch Phát Sóng TV", + "HeaderGenres": "Thể Loại", + "HeaderForgotPassword": "Quên Mật Khẩu", + "HeaderForKids": "Dành Cho Trẻ Em", + "HeaderFilters": "Bộ Lọc", + "HeaderFetcherSettings": "Cài Đặt Chương Trình Tải Xuống", + "HeaderFetchImages": "Tải Hình Ảnh:", + "HeaderFeatures": "Nổi Bật", + "HeaderFavoritePeople": "Nhân Vật Yêu Thích", + "HeaderFavoriteAlbums": "Album Yêu Thích", + "HeaderFavoriteBooks": "Sách Yêu Thích", + "HeaderFavoriteVideos": "Video Yêu Thích", + "HeaderFavoriteSongs": "Bài Hát Yêu Thích", + "HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích", + "HeaderFavoriteEpisodes": "Tập Phim Yêu Thích", + "HeaderFavoriteShows": "Chương Trình Yêu Thích", + "HeaderFavoriteMovies": "Phim Yêu Thích", + "HeaderExternalIds": "Mã Định Danh Từ Bên Ngoài:", + "HeaderError": "Lỗi", + "HeaderEpisodes": "Tập", + "HeaderEnabledFieldsHelp": "Bỏ chọn một mục để khoá lựa chọn đó và không để nó thay đổi.", + "HeaderEnabledFields": "Những Mục Được Kích Hoạt", + "HeaderEditImages": "Chỉnh Sửa Hình Ảnh", + "HeaderEasyPinCode": "Mã PIN Đơn Giản", + "HeaderDownloadSync": "Tải Xuống Và Đồng Bộ", + "HeaderDisplay": "Hiển Thị", + "HeaderDirectPlayProfileHelp": "Thêm thiết lập phát trực tiếp để chỉ ra những định dạng mà thiết bị có thể phát trực tiếp mà không cần chuyển mã.", + "HeaderDirectPlayProfile": "Thiết Lập Phát Trực Tiếp", + "HeaderDevices": "Thiết Bị", + "HeaderDeviceAccess": "Truy Cập Thiết Bị", + "HeaderDeveloperInfo": "Thông Tin Nhà Phát Triển", + "HeaderDetectMyDevices": "Tìm Kiếm Những Thiết Bị Của Tôi", + "HeaderDeleteTaskTrigger": "Xoá Kích Hoạt Tác Vụ", + "HeaderDeleteProvider": "Xoá Nhà Cung Cấp", + "HeaderDeleteItems": "Xoá Những Mục Này", + "HeaderDeleteItem": "Xoá Mục Này", + "HeaderDeleteDevice": "Xoá Thiết Bị", + "HeaderDefaultRecordingSettings": "Thiết Lập Ghi Âm/Ghi Hình Mặc Định", + "HeaderRecordingOptions": "Tuỳ Chọn Ghi Âm/Ghi Hình", + "HeaderProfileServerSettingsHelp": "Những thông tin này về máy chủ Jellyfin sẽ hiển thị trên các thiết bị phát được kết nối.", + "HeaderSettings": "Cài Đặt", + "HeaderServerSettings": "Cài Đặt Máy Chủ", + "HeaderSeriesStatus": "Trạng Thái Series", + "HeaderSeriesOptions": "Tuỳ Chọn Series", + "HeaderSendMessage": "Gửi Tin Nhắn", + "HeaderSelectTranscodingPathHelp": "Hãy chọn đường dẫn để lưu tạm những tập tin chuyển mã. Máy chủ phải có quyền ghi trên thư mục đó.", + "HeaderSelectTranscodingPath": "Chọn Đường Dẫn Chuyển Mã", + "HeaderSelectServerCachePathHelp": "Hãy chọn đường dẫn để lưu tập tin ghi tạm cho máy chủ. Máy chủ phải có quyền ghi trên thư mục đó.", + "HeaderSelectServerCachePath": "Chọn Đường Dẫn Ghi Tạm Cho Máy Chủ", + "HeaderSelectServer": "Chọn Máy Chủ", + "HeaderSelectPath": "Chọn Đường Dẫn", + "HeaderSelectMetadataPathHelp": "Hãy chọn đường dẫn mà bạn muốn lưu thông tin bổ trợ. Máy chủ phải có quyền ghi trên thư mục đó.", + "HeaderSelectMetadataPath": "Chọn Đường Dẫn Thông Tin Bổ Trợ", + "HeaderSelectCertificatePath": "Chọn Đường Dẫn Đến Chứng Chỉ", + "HeaderSecondsValue": "{0} Giây", + "HeaderSeasons": "Phần", + "HeaderSchedule": "Lịch Phát Sóng", + "HeaderScenes": "Phân Cảnh", + "HeaderRunningTasks": "Những Tác Vụ Hoạt Động", + "HeaderRevisionHistory": "Lịch Sử Chỉnh Sửa", + "HeaderRestartingServer": "Đang Khởi Động Lại Máy Chủ", + "HeaderRestart": "Khởi Động Lại", + "HeaderResponseProfileHelp": "Hồ sơ phản hồi là phương thức tuỳ chỉnh thông tin gửi về thiết bị phát khi phát một số nội dung nhất định.", + "HeaderResponseProfile": "Hồ Sơ Phản Hồi", + "HeaderRemoveMediaLocation": "Xoá Đường Dẫn Nội Dung", + "HeaderRemoveMediaFolder": "Xoá Thư Mục Nội Dung", + "HeaderRemoteControl": "Điều Khiển Từ Xa", + "HeaderRecordingPostProcessing": "Xử Lý Sau Khi Ghi Âm/Ghi Hình", + "HeaderSortOrder": "Thứ tự Sắp xếp", + "HeaderSortBy": "Sắp xếp theo", + "HeaderStartNow": "Bắt đầu", + "HeaderSetupLibrary": "Thiết lập thư viện nội dung của bạn", + "HeaderTracks": "Bài Hát", + "HeaderThisUserIsCurrentlyDisabled": "Người dùng này hiện tại đang bị khoá", + "HeaderTaskTriggers": "Kích Hoạt Tác Vụ", + "HeaderTags": "Nhãn", + "HeaderSubtitleProfilesHelp": "Hồ sơ phụ đề chỉ ra những định dạng phụ đề được hỗ trợ bởi thiết bị phát.", + "HeaderSubtitleProfiles": "Hồ Sơ Phụ Đề", + "HeaderSubtitleProfile": "Hồ Sơ Phụ Đề", + "HeaderSubtitleDownloads": "Tải Phụ Đề", + "HeaderSubtitleAppearance": "Giao Diện Phụ Đề", + "HeaderStopRecording": "Ngừng Ghi Hình/Ghi Âm", + "HeaderSpecialFeatures": "Những Phần Đặc Biệt Nổi Bật", + "HeaderSpecialEpisodeInfo": "Thông Tin Tập Đặc Biệt", + "HeaderShutdown": "Tắt Máy Chủ", + "LabelCustomDeviceDisplayNameHelp": "Cung cấp một tên hiển thị riêng hoặc bỏ trống để sử dụng tên có sẵn của thiết bị.", + "LabelCustomDeviceDisplayName": "Tên hiển thị:", + "LabelCustomCssHelp": "Áp dụng tuỳ chỉnh riêng của bạn vào giao diện trang web.", + "LabelCustomCss": "CSS tuỳ chọn:", + "LabelCustomCertificatePathHelp": "Đường dẫn đến tập tin PKCS #12 chứa chứng chỉ (certificate) và khoá riêng (private key) để bật tính năng TLS trên một tên miền tuỳ chọn.", + "LabelCustomCertificatePath": "Đường dẫn đến chứng chỉ SSL:", + "LabelCriticRating": "Đánh giá phê bình:", + "LabelCorruptedFrames": "Những khung hình bị lỗi:", + "LabelContentType": "Loại nội dung:", + "LabelCommunityRating": "Đánh giá của cộng đồng:", + "LabelCollection": "Bộ Sưu Tập:", + "LabelChannels": "Kênh:", + "LabelCertificatePasswordHelp": "Nếu chứng chỉ của bạn cần mật khẩu, hãy nhập nó ở đây.", + "LabelCertificatePassword": "Mật khẩu chứng chỉ:", + "LabelCancelled": "Đã Huỷ", + "LabelCachePathHelp": "Chọn một đường dẫn cho những tập tin lưu tạm như là hình ảnh. Bỏ trống để sử dụng cài đặt mặc định của máy chủ.", + "LabelCachePath": "Đường dẫn cache:", + "LabelCache": "Cache:", + "LabelBurnSubtitles": "Nhúng phụ đề:", + "LabelBlockContentWithTags": "Chặn những mục có nhãn:", + "LabelBlastMessageIntervalHelp": "Xác định thời gian tồn tại giữa các tin nhắn (tính bằng giây).", + "LabelBlastMessageInterval": "Thời gian tồn tại của tin nhắn (giây)", + "LabelBitrate": "Bitrate:", + "LabelBirthYear": "Năm sinh:", + "LabelBirthDate": "Ngày sinh:", + "LabelBindToLocalNetworkAddressHelp": "Không bắt buộc. Cài đặt đè địa chỉ IP nội bộ để kết nối đến máy chủ HTTP. Nếu bỏ trống, máy chủ sẽ cài đặt vào toàn bộ những địa chỉ nội bộ có sẵn. Nếu thay đổi tuỳ chọn này, cần phải khởi động lại máy chủ Jellyfin để có tác dụng.", + "LabelBindToLocalNetworkAddress": "Cài đặt vào địa chỉ nội bộ:", + "LabelAutomaticallyRefreshInternetMetadataEvery": "Tự động cập nhật dữ liệu bổ trợ từ Internet:", + "LabelAuthProvider": "Nhà Cung Cấp Xác Thực:", + "LabelAudioSampleRate": "Sample rate âm thanh:", + "LabelAudioCodec": "Bộ giải mã âm thanh:", + "LabelAudioChannels": "Các kênh âm thanh:", + "LabelAudioBitrate": "Bitrate của âm thanh:", + "LabelAudioBitDepth": "Chiều sâu của âm thanh:", + "LabelAudio": "Âm Thanh", + "LabelArtistsHelp": "Sử dụng dấu ; để tách rời nhiều nghệ", + "LabelArtists": "Nghệ sĩ:", + "LabelAppNameExample": "Ví dụ: Sickbeard, Sonarr", + "LabelAppName": "Tên ứng dụng", + "LabelAllowedRemoteAddressesMode": "Chế độ bộ lọc địa chỉ IP từ xa:", + "LabelAllowedRemoteAddresses": "Bộ lọc địa chỉ IP từ xa:", + "LabelAllowServerAutoRestartHelp": "Máy chủ chỉ khởi động lại trong thời gian rỗi khi không có người dùng đang sử dụng.", + "LabelAllowHWTranscoding": "Cho phép chuyển mã bằng phần cứng", + "LabelAll": "Tất Cả", + "LabelAlbumArtists": "Nghệ sĩ album:", + "LabelAlbumArtPN": "Bìa album PN:", + "LabelAlbumArtMaxWidthHelp": "Độ phân giải cao nhất của bìa album thông qua upnp:albumArtURI.", + "LabelAlbumArtMaxWidth": "Chiều ngang lớn nhất của bìa album:", + "LabelAlbumArtMaxHeightHelp": "Độ phân giải cao nhất của bìa album thông qua upnp:albumArtURI.", + "LabelAlbumArtMaxHeight": "Chiều cao lớn nhất của bìa album:", + "LabelAlbumArtHelp": "PN được sử dụng cho bìa album, trong dlna:profileID thuộc tính upnp:albumArtURI. Một vài thiết bị phát cần một giá trị đặc biệt, không ảnh hưởng đến kích thước của hình ảnh.", + "LabelAlbum": "Album:", + "LabelAirsBeforeSeason": "Phát sóng trước mùa:", + "LabelAirsBeforeEpisode": "Phát sóng trước tập:", + "LabelAirsAfterSeason": "Phát sóng sau mùa:", + "LabelAirTime": "Thời gian phát sóng:", + "LabelAirDays": "Ngày phát sóng:", + "LabelAccessStart": "Thời gian bắt đầu:", + "LabelAccessEnd": "Thời gian kết thúc:", + "LabelAccessDay": "Ngày trong tuần:", + "LabelAbortedByServerShutdown": "(Đã huỷ bởi máy chủ ngừng hoạt động)", + "Label3DFormat": "Định dạng 3D:", + "Kids": "Trẻ Em", + "Items": "Mục", + "ItemCount": "{0} mục", + "InstantMix": "Trộn Lẫn Nhanh", + "InstallingPackage": "Đang cài đặt {0} (phiên bản {1})", + "ImportMissingEpisodesHelp": "Nếu bật tuỳ chọn này, thông tin bị thiếu trong các tập phim sẽ được nhập vào cơ sở dữ liệu của máy chủ Jellyfin và hiển thị trong các phần và series. Điều này có thể làm việc quét thư viện lâu hơn rất nhiều.", + "ImportFavoriteChannelsHelp": "Nếu bật tuỳ chọn này, chỉ những kênh yêu thích trong thiết bị bắt sóng sẽ được nhập vào.", + "Images": "Hình Ảnh", + "Identify": "Nhận Dạng", + "HttpsRequiresCert": "Để bật kết nối bảo mật, bạn cần phải cung cấp một Chứng Chỉ SSL đáng tin cậy, ví dụ như \"Let's Encrypt\". Hãy cung cấp Chứng Chỉ SSL hoặc là tắt tính năng kết nối bảo mật.", + "Horizontal": "Nằm Ngang", + "Home": "Trang Chủ", + "HideWatchedContentFromLatestMedia": "Ẩn những nội dung đã xem khỏi phần nội dung mới nhất", + "Hide": "Ẩn", + "Help": "Trợ Giúp", + "HeadersFolders": "Thư Mục", + "HeaderYears": "Năm", + "HeaderXmlSettings": "Cài Đặt XML", + "HeaderXmlDocumentAttributes": "Những Thuộc Tính Tài Liệu XML", + "HeaderXmlDocumentAttribute": "Thuộc Tính Tài Liệu XML", + "HeaderVideos": "Videos", + "HeaderVideoTypes": "Kiểu Video", + "HeaderVideoType": "Kiểu Video", + "HeaderVideoQuality": "Chất Lượng Video", + "HeaderUser": "Người Dùng", + "HeaderUploadImage": "Tải Lên Hình Ảnh", + "HeaderUpcomingOnTV": "Sắp Phát Sóng Trên TV", + "HeaderTypeText": "Nhập nội dung", + "HeaderTypeImageFetchers": "{0} Trình Tải Hình Ảnh", + "HeaderTuners": "Bộ Điều Khiển Thu Phát Sóng", + "HeaderTunerDevices": "Thiết Bị Bắt Sóng", + "HeaderTranscodingProfileHelp": "Thêm hồ sơ chuyển mã để chỉ ra những định dạng nên dùng khi cần chuyển mã.", + "HeaderTranscodingProfile": "Hồ Sơ Chuyển Mã", + "LabelEnableAutomaticPortMap": "Kích hoạt tính năng tự động kết nối các port", + "LabelEmbedAlbumArtDidlHelp": "Một vài thiết bị ưu tiên phương pháp này để tải bìa album. Một số thiết bị khác có thể không phát được nếu tuỳ chọn này được kích hoạt.", + "LabelEmbedAlbumArtDidl": "Bìa album trong Didl", + "LabelEasyPinCode": "Mã PIN đơn giản:", + "LabelDynamicExternalId": "{0} Id:", + "LabelDropShadow": "Đổ bóng:", + "LabelDroppedFrames": "Những khung hình bị loại bỏ:", + "LabelDropImageHere": "Kéo thả hình ảnh vào đây, hoặc click để lựa chọn hình ảnh.", + "LabelDownloadLanguages": "Ngôn ngữ tải xuống:", + "LabelDownMixAudioScaleHelp": "Tăng cường âm lượng khi trộn âm thanh. Giá trị bằng 1 sẽ giữ nguyên âm lượng gốc.", + "LabelDownMixAudioScale": "Tăng cường âm lượng khi trộn âm thanh:", + "LabelDisplaySpecialsWithinSeasons": "Hiển thị những tập đặc biệt trong phần mà nó được phát sóng", + "LabelDisplayOrder": "Thứ tự hiển thị:", + "LabelDisplayName": "Tên hiển thị:", + "LabelDisplayMode": "Chế độ hiển thị:", + "LabelDisplayMissingEpisodesWithinSeasons": "Hiển thị những tập bị thiếu", + "LabelDisplayLanguageHelp": "Dự án chuyển ngữ Jellyfin là một dự án diễn ra ra liên tục.", + "LabelDisplayLanguage": "Ngôn ngữ hiển thị:", + "LabelDiscNumber": "Đĩa số:", + "LabelDidlMode": "Chế độ DIDL:", + "LabelDeviceDescription": "Mô tả thiết bị", + "LabelDeinterlaceMethod": "Phương pháp khử xen kẽ:", + "LabelDefaultUserHelp": "Xác định thư viện được hiển thị trên những thiết bị đã kết nối. Tuỳ chọn này có thể được ghi đè trên hồ sơ của từng thiết bị.", + "LabelDefaultUser": "Người dùng mặc định:", + "LabelDefaultScreen": "Màn hình mặc định:", + "LabelDeathDate": "Ngày mất:", + "LabelDateTimeLocale": "Ngày giờ địa phương:", + "LabelDateAddedBehaviorHelp": "Nếu có giá trị dữ liệu bổ trợ, nó sẽ luôn được sử dụng trước một trong các tùy chọn này.", + "LabelDateAddedBehavior": "Ngày thêm hành vi cho nội dung mới:", + "LabelDateAdded": "Ngày thêm vào:", + "LabelDashboardTheme": "Chủ đề bảng điều khiển máy chủ:", + "LabelCustomRating": "Đánh giá tuỳ chọn:", + "HeaderFavoritePlaylists": "Danh Sách Phát Yêu Thích", + "ApiKeysCaption": "Danh sách các mã API đang hoạt động", + "LabelBaseUrl": "URL cơ bản:", + "LabelEveryXMinutes": "Mỗi:", + "LabelEpisodeNumber": "Tập số:", + "LabelEndDate": "Ngày kết thúc:", + "LabelEnableSingleImageInDidlLimitHelp": "Một số thiết bị không hiển thị rõ ràng nếu có nhiều hình ảnh được nhúng trong Didl.", + "LabelEnableSingleImageInDidlLimit": "Giới hạn chỉ một hình ảnh nhúng", + "LabelEnableRealtimeMonitorHelp": "Thay đổi để nội dung sẽ được xử lý ngay lập tức trên các hệ thống được hỗ trợ.", + "LabelEnableRealtimeMonitor": "Bật tính năng theo dõi thời gian thực", + "LabelEnableHttpsHelp": "Cho phép máy chủ theo dõi port HTTPS đã được thiết lập. Cần phải có chứng chỉ hợp lệ để tính năng này có hiệu quả.", + "LabelEnableHttps": "Bật HTTPS", + "LabelEnableHardwareDecodingFor": "Bật tính năng giãi mã phần cứng cho:", + "LabelEnableDlnaServerHelp": "Cho phép các thiết bị UPnP trong mạng của bản để duyệt và phát nội dung.", + "LabelEnableDlnaServer": "Bật tính năng máy chủ DLNA", + "LabelEnableDlnaPlayToHelp": "Tìm kiếm thiết bị trong mạng của bạn và đưa ra khả năng điều khiển từ xa những thiết bị đó.", + "LabelEnableDlnaDebugLoggingHelp": "Tạo những tập tin gỡ lỗi lớn và chỉ nên được sử dụng khi cần thiết để xử lý sự cố.", + "LabelEnableDlnaDebugLogging": "Bật tính năng gỡ lỗi DLNA", + "LabelEnableDlnaClientDiscoveryIntervalHelp": "Xác định thời gian tính bằng giây giữa các tìm kiếm SSDP thực hiện bởi Jellyfin.", + "LabelEnableDlnaClientDiscoveryInterval": "Thời gian tìm kiếm thiết bị phát (giây)", + "LabelEnableBlastAliveMessagesHelp": "Bật tính năng này nếu máy chủ không thể kết nối chắc chắn với những thiết bị UPnP trong mạng của bạn.", + "LabelEnableBlastAliveMessages": "Phát tin nhắn trực tiếp", + "LabelEnableAutomaticPortMapHelp": "Tự động chuyển tiếp những port công khai trên bộ định tuyến đến port trên máy chủ thông qua UPnP. Cài đặt này có thể không hoạt động trên một số loại bộ định tuyến hoặc thiết lập mạng. Thay đổi sẽ được áp dụng sau khi khởi động lại máy chủ.", + "HeaderServerAddressSettings": "Cài Đặt Địa Chỉ Máy Chủ", + "HeaderRemoteAccessSettings": "Cài Đặt Truy Cập Từ Xa", + "HeaderHttpsSettings": "Cài Đặt HTTPS", + "HeaderDVR": "DVR", + "LabelExtractChaptersDuringLibraryScanHelp": "Trích xuất hình ảnh của video được nhập vào trong lúc quét thư viện. Nếu không thì hình này này sẽ được trích xuất thông qua những tác vụ định kì, giúp cho quá trình quét thư viện diễn ra nhanh hơn.", + "LabelExtractChaptersDuringLibraryScan": "Trích xuất hình ảnh từng chương khi quét thư viện", + "LabelBaseUrlHelp": "Thêm một thư mục tuỳ chọn vào đường dẫn máy chủ. Ví dụ: http://example.com/<baseurl>", + "LabelLoginDisclaimerHelp": "Một tin nhắn sẽ hiển thị ở phía cuối của trang đăng nhập.", + "LabelLoginDisclaimer": "Hiển thị khi đăng nhập:", + "LabelLockItemToPreventChanges": "Khoá mục này để ngăn những thay đổi trong tương lai", + "LabelLocalHttpServerPortNumberHelp": "TCP port mà máy chủ Jellyfin HTTP nên kết nối.", + "LabelLocalHttpServerPortNumber": "HTTP port nội bộ:", + "LabelLineup": "Danh sách diễn viên:", + "LabelLibraryPageSizeHelp": "Cài đặt số lượng mục hiển thị trong một trang thư viện. Cài đặt 0 để vô hiệu hoá việc phân trang.", + "LabelLibraryPageSize": "Kích thước trang của thư viện:", + "LabelLanNetworks": "Mạng nội bộ:", + "LabelKodiMetadataUserHelp": "Lưu dữ liệu xem vào tập tin NFO dành cho những ứng dụng khác sử dụng.", + "LabelKodiMetadataUser": "Lưu thông tin người xem vào tập tin NFO dành cho:", + "LabelKodiMetadataSaveImagePathsHelp": "Cài đặt này được khuyến cáo nếu bạn có những hình ảnh đặt tên không đúng với hướng dẫn của Kodi.", + "LabelKodiMetadataSaveImagePaths": "Lưu đường dẫn hình ảnh trong tập tin NFO", + "LabelKodiMetadataEnablePathSubstitutionHelp": "Kích hoạt thay thế đường dẫn hình ảnh sử dụng cài đặt của máy chủ.", + "LabelKodiMetadataEnablePathSubstitution": "Kích hoạt thay thế đường dẫn", + "LabelKodiMetadataEnableExtraThumbsHelp": "Khi tải hình ảnh, chúng có thể được lưu vào cả extrafanart và extrathumbs để tối ưu hoá khả năng tương thích với giao diện Kodi.", + "LabelKodiMetadataEnableExtraThumbs": "Sao chép từ mục extrafanart đến mục extrathumbs", + "LabelKodiMetadataDateFormatHelp": "Toàn bộ ngày trong tập tin NFO sẽ được đọc sử dụng định dạng này.", + "LabelKodiMetadataDateFormat": "Định dạng của ngày phát hành:", + "LabelKidsCategories": "Những thể loại của trẻ em:", + "LabelKeepUpTo": "Theo kịp:", + "LabelInternetQuality": "Chất lượng Internet:", + "LabelInNetworkSignInWithEasyPasswordHelp": "Sử dụng mã PIN đơn giản để đăng nhập thiết bị phát trong mạng nội bộ. Mật khẩu thông thường sẽ chỉ cần khi không truy cập nội mạng. Nếu mã PIN để trống, bạn sex không cần mật khẩu trong mạng nội bộ.", + "LabelInNetworkSignInWithEasyPassword": "Kích hoạt đăng nhập nội mạng bằng mã PIN đơn giản", + "LabelImportOnlyFavoriteChannels": "Giới hạn để chỉ nhập vào những kênh yêu thích", + "LabelImageType": "Loại hình ảnh:", + "LabelImageFetchersHelp": "Kích hoạt và xếp hạng chương trình tải hình ảnh theo thứ tự ưu tiên.", + "LabelIdentificationFieldHelp": "Một phần chuỗi không phân biệt viết hoa/thường hoặc regex expression.", + "LabelIconMaxWidthHelp": "Độ phân giải tối đa của biểu tượng hiển thị thông qua upnp:icon.", + "LabelIconMaxWidth": "Chiều ngang tối đa của biểu tượng:", + "LabelIconMaxHeightHelp": "Độ phân giải tối đa của biểu tượng hiển thị thông qua upnp:icon.", + "LabelIconMaxHeight": "Chiều cao tối đa của biểu tượng:", + "LabelHttpsPortHelp": "TCP port mà máy chủ Jellyfin HTTPS nên kết nối vào.", + "LabelHttpsPort": "HTTPS port trên máy chủ:", + "LabelHomeScreenSectionValue": "Mục trên trang chủ {0}:", + "LabelHomeNetworkQuality": "Chất lượng mạng trong nhà:", + "LabelHardwareAccelerationTypeHelp": "Hỗ trợ phần cần những thiết lập bổ sung.", + "LabelHardwareAccelerationType": "Hỗ trợ phần cứng:", + "LabelEncoderPreset": "Thiết lập cài sẵn của mã H264 và H265:", + "LabelH264Crf": "CRF của mã H264:", + "LabelGroupMoviesIntoCollectionsHelp": "Khi hiển thị danh sách phim, các bộ phim thuộc về một bộ sưu tập sẽ hiển thị trong một nhóm.", + "LabelGroupMoviesIntoCollections": "Nhóm phim vào bộ sưu tập", + "LabelServerNameHelp": "Tên này sẽ được sử dụng để phân biệt máy chủ và giá trị mặc định là tên của máy tính chủ.", + "LabelFriendlyName": "Tên thân thiện:", + "LabelFormat": "Định dạng:", + "LabelForgotPasswordUsernameHelp": "Nhập vào tên tài khoản nếu bạn nhớ nó.", + "LabelFont": "Kiểu chữ:", + "LabelFolder": "Thư mục:", + "LabelFileOrUrl": "Tệp hoặc URL:", + "LabelFailed": "Thất bại" } diff --git a/src/strings/zh-cn.json b/src/strings/zh-cn.json index 80ab759081..606f9fea0c 100644 --- a/src/strings/zh-cn.json +++ b/src/strings/zh-cn.json @@ -1,5 +1,5 @@ { - "AccessRestrictedTryAgainLater": "目前访问受限。请稍后再试。", + "AccessRestrictedTryAgainLater": "目前访问受限,请稍后再试。", "Actor": "演员", "Add": "添加", "AddItemToCollectionHelp": "通过搜索项目并右键或轻触得到的弹出菜单来将项目添加到集合中。", @@ -29,7 +29,7 @@ "AlwaysPlaySubtitles": "总是显示", "AlwaysPlaySubtitlesHelp": "无论音频为何种语言,都将加载与语言偏好匹配的字幕。", "Anytime": "任何时间", - "AroundTime": "{0} 左右", + "AroundTime": "大约", "Artists": "艺术家", "AsManyAsPossible": "尽可能多", "Ascending": "升序", @@ -449,7 +449,7 @@ "Hide": "隐藏", "HideWatchedContentFromLatestMedia": "从最新媒体中隐藏已观看的内容", "Home": "首页", - "HttpsRequiresCert": "要启用安全连接, 您需要提供一个受信任的 SSL 证书, 如 \"允许加密\"。请提供证书或禁用安全连接。", + "HttpsRequiresCert": "要启用安全连接, 您需要提供一个受信任的 SSL 证书, 例如 Let's Encrypt 。请提供证书或禁用安全连接。", "Identify": "识别", "Images": "图片", "ImportFavoriteChannelsHelp": "如果启用,只有在协调器设备中被标记为我的最爱的频道才会被导入。", @@ -548,7 +548,7 @@ "LabelEmbedAlbumArtDidl": "在DIDL中嵌入专辑封面", "LabelEmbedAlbumArtDidlHelp": "有些设备首选这种方式获取专辑封面。启用该选项可能导致其他设备播放失败。", "LabelEnableAutomaticPortMap": "开启自动端口映射", - "LabelEnableAutomaticPortMapHelp": "尝试通过UPnP将公共端口自动映射到本地端口。这可能不适用于某些型号的路由器。需要服务器重新启动后才会应用更改。", + "LabelEnableAutomaticPortMapHelp": "通过UPnP将路由器端口自动转发到服务器端口。这可能不适用于某些型号的路由器和网络配置。需要服务器重新启动后才会应用更改。", "LabelEnableBlastAliveMessages": "爆发活动信号", "LabelEnableBlastAliveMessagesHelp": "如果该服务器不能被网络中的其他UPnP设备检测到,请启用此选项。", "LabelEnableDlnaClientDiscoveryInterval": "客户端搜寻时间间隔(秒)", @@ -670,7 +670,7 @@ "LabelNumberOfGuideDays": "下载几天的节目指南:", "LabelNumberOfGuideDaysHelp": "下载更多天的节目指南可以帮你进一步查看节目列表并做出提前安排,但下载过程也将耗时更久。它将基于频道数量自动选择。", "LabelOptionalNetworkPath": "(可选的)共享的网络文件夹:", - "LabelOptionalNetworkPathHelp": "如果这个文件夹在你的网络上是共享的,提供这个网络共享地址能够允许其他设备上的 Jellyfin 应用程序直接访问媒体文件。", + "LabelOptionalNetworkPathHelp": "如果这个文件夹在你的网络上是共享的,提供这个网络共享地址能够允许其他设备上的 Jellyfin 应用程序直接访问媒体文件,例如 {0} 或者 {1}。", "LabelOriginalAspectRatio": "原始长宽比:", "LabelOriginalTitle": "原标题:", "LabelOverview": "内容概述:", @@ -1020,7 +1020,7 @@ "OptionMissingEpisode": "缺少的剧集", "OptionMonday": "星期一", "OptionNameSort": "名字", - "OptionNew": "更新...", + "OptionNew": "新建…", "OptionNone": "没有", "OptionOnAppStartup": "在程序启动时", "OptionOnInterval": "在一个期间", @@ -1407,7 +1407,6 @@ "Shows": "节目", "SkipEpisodesAlreadyInMyLibraryHelp": "将使用季和剧集编号对剧集进行比较。", "Smaller": "更小", - "StatsForNerds": "统计数据", "SubtitleSettings": "字幕设置", "SubtitleSettingsIntro": "要配置默认字幕外观和语言设置,请停止视频播放,然后单击应用程序右上角的用户图标。", "TagsValue": "标签:{0}", @@ -1456,7 +1455,7 @@ "ButtonAddImage": "添加图片", "LabelPlayer": "播放器:", "LabelBaseUrl": "基础 URL:", - "LabelBaseUrlHelp": "您可以在此处添加自定义子目录,以便从更唯一的 URL 访问服务器。", + "LabelBaseUrlHelp": "为服务器 URL添加自定义子目录,例如:http://example.com/<baseurl>。", "MusicLibraryHelp": "重播 {0}音乐命名指南{1}。", "HeaderFavoritePeople": "最喜欢的人物", "OptionRandom": "随机", @@ -1465,8 +1464,6 @@ "HeaderNavigation": "导航", "CopyStreamURLError": "复制URL地址时发生错误。", "MessageConfirmAppExit": "你要退出吗?", - "EnableFastImageFadeIn": "快速图片淡入", - "EnableFastImageFadeInHelp": "为已加载的图片启用更快的图片淡入动画", "OptionForceRemoteSourceTranscoding": "强制远程转码(像电视直播一样)", "NoCreatedLibraries": "看上去您还未创建任何资料库。{0} 您想现在创建一个吗? {1}", "AskAdminToCreateLibrary": "请联系管理员以创建一个新的资料库。", @@ -1499,10 +1496,53 @@ "EveryXMinutes": "每 {0} 分钟", "WriteAccessRequired": "Jellyfin 服务端需要此文件夹的写入权限。请确认是否拥有写入权限并重试。", "PathNotFound": "无法找到此路径。请确认路径有效并重试。", - "YadifBob": "Yadif Bob", - "Yadif": "Yadif", + "YadifBob": "YADIF Bob", + "Yadif": "YADIF", "LabelDeinterlaceMethod": "反交错方法:", "DeinterlaceMethodHelp": "选择对隔行扫描内容进行转码时所用的反交错方法。", + "LabelLibraryPageSize": "媒体库分页阈值:", + "LabelLibraryPageSizeHelp": "设置媒体库页面每页要显示的最多媒体个数。设置为 0 以禁用分页。", + "UnsupportedPlayback": "Jellyfin无法解密被DRM保护的内容,但仍然会尝试播放包括受保护内容在内的所有内容。某些文件由于被加密或包含不受支持的特性(如互动标题),在播放时可能显示为黑屏。", + "MessageUnauthorizedUser": "您目前无权访问服务器。更多有关信息,请与服务器管理员联系。", + "Filter": "过滤", + "New": "新的", + "HeaderFavoritePlaylists": "收藏的播放列表", + "ButtonTogglePlaylist": "播放列表", + "ButtonToggleContextMenu": "更多", + "HeaderServerAddressSettings": "服务器地址设置", + "HeaderRemoteAccessSettings": "远程访问设置", + "HeaderHttpsSettings": "HTTPS 设置", + "ApiKeysCaption": "当前启用的 API 密钥", + "TabDVR": "DVR", + "SaveChanges": "保存更改", + "LabelRequireHttpsHelp": "开启后服务器将自动将所有 HTTP 请求重定向到 HTTPS。如果服务器没有启用 HTTPS 则不生效。", + "LabelRequireHttps": "强制 HTTPS", + "LabelStable": "稳定版", + "LabelEnableHttpsHelp": "开启服务器对所配置HTTPS 端口的监听。必须配置有效的证书才会生效。", + "LabelEnableHttps": "启用 HTTPS", + "LabelChromecastVersion": "Chromecast版本", + "HeaderDVR": "DVR", + "LabelNightly": "开发版", + "MessageSyncPlayErrorAccessingGroups": "访问群组列表时发生错误。", + "MessageSyncPlayLibraryAccessDenied": "搜限制的访问权限。", + "MessageSyncPlayCreateGroupDenied": "需要权限创建该组。", + "MessageSyncPlayGroupDoesNotExist": "无法加入群组,该群组不存在。", + "MessageSyncPlayPlaybackPermissionRequired": "需要播放权限。", + "MessageSyncPlayGroupWait": "{0} 正在缓冲...", + "MessageSyncPlayUserLeft": "{0} 已离开小组。", + "MessageSyncPlayUserJoined": "{0} 已加入该小组。", + "LabelSyncPlayAccessNone": "禁用此用户", + "LabelSyncPlayAccessJoinGroups": "允许用户加入群组", + "LabelSyncPlayAccessCreateAndJoinGroups": "允许用户创建和加入群组", + "LabelSyncPlayLeaveGroup": "离开组", + "LabelSyncPlayNewGroupDescription": "创建一个新组", + "LabelSyncPlayNewGroup": "新组", + "LabelSyncPlaySyncMethod": "同步方式:", + "MillisecondsUnit": "毫秒", + "LabelSyncPlayTimeOffset": "服务器时间偏移:", + "HeaderSyncPlayEnabled": "同步播放已启用", + "HeaderSyncPlaySelectGroup": "加入群组", + "EnableDetailsBannerHelp": "在项目详细信息页面的顶部显示横幅图片。", "EnableDecodingColorDepth10": "启用 10-Bit 硬件解码", "EnableDecodingColorDepth10Help" : "在支持的硬件上启用 10-Bit 硬件解码。仅对 HEVC 和 VP9 格式起作用。如果你遇到了播放问题,请关闭这个选项。" } diff --git a/src/strings/zh-hk.json b/src/strings/zh-hk.json index 2d9634d333..97b2d2d1f7 100644 --- a/src/strings/zh-hk.json +++ b/src/strings/zh-hk.json @@ -286,7 +286,7 @@ "TabAdvanced": "進階", "TabAlbumArtists": "唱片歌手", "TabAlbums": "專輯", - "TabArtists": "歌手", + "TabArtists": "藝人", "TabCatalog": "目錄", "TabChannels": "頻道", "TabCollections": "藏品", @@ -339,7 +339,7 @@ "Anytime": "任何時間", "AnyLanguage": "任何語言", "Artists": "藝人", - "AsManyAsPossible": "盡可能地越多越好", + "AsManyAsPossible": "越多越好", "Audio": "音頻", "Auto": "自動", "AutoBasedOnLanguageSetting": "自動 (基於語言設定)", @@ -359,15 +359,48 @@ "HeaderFavoriteSongs": "最愛的歌曲", "HeaderFavoriteShows": "最愛的節目", "HeaderFavoriteEpisodes": "最愛的劇集", - "HeaderFavoriteArtists": "最愛藝術家", + "HeaderFavoriteArtists": "最愛的藝人", "HeaderFavoriteAlbums": "最愛專輯", "HeaderContinueWatching": "繼續觀看", - "HeaderAlbumArtists": "專輯藝術家", + "HeaderAlbumArtists": "專輯藝人", "Genres": "風格", "Folders": "檔案夾", "Favorites": "我的最愛", "Collections": "合輯", "Channels": "頻道", "Books": "圖書", - "Albums": "專輯" + "Albums": "專輯", + "Absolute": "絕對", + "AuthProviderHelp": "選擇用於驗證該用戶密碼的身份驗證提供程序。", + "AttributeNew": "新", + "AspectRatio": "長寬比", + "AskAdminToCreateLibrary": "要求管理員創建一個庫。", + "Ascending": "上升", + "Artist": "藝人", + "Art": "藝術", + "AroundTime": "大約{0}", + "AlwaysPlaySubtitlesHelp": "無論語言是哪種音頻,都將加載與語言首選項匹配的字幕。", + "AllowedRemoteAddressesHelp": "IP地址或IP /網絡掩碼條目的逗號分隔列表,用於允許遠程連接的網絡。 如果保留為空白,將允許所有遠程地址。", + "AllowRemoteAccessHelp": "如果未選中,則將阻止所有遠程連接。", + "AllowRemoteAccess": "允許與此Jellyfin服務器的遠程連接。", + "AllowFfmpegThrottlingHelp": "當轉碼或remux距離當前播放位置足夠遠時,請暫停該過程,以減少資源消耗。 在不經常觀看的情況下,此功能最為有用。 如果遇到播放問題,請關閉此功能。", + "AllowOnTheFlySubtitleExtractionHelp": "可以從視頻中提取嵌入式字幕,然後以純文本格式將其交付給客戶端,以幫助防止視頻轉碼。 在某些系統上,這可能需要很長時間,並且會導致提取過程中視頻播放停止。 如果客戶端設備本身不支持嵌入的字幕,則可以禁用此選項以通過視頻轉碼刻錄字幕。", + "AllowOnTheFlySubtitleExtraction": "允許即時提取字幕", + "AllowMediaConversionHelp": "授予或拒絕訪問轉換媒體功能的權限。", + "AllowMediaConversion": "允許媒體轉換", + "AllowHWTranscodingHelp": "允許調諧器即時轉碼流。 這可以幫助減少服務器所需的代碼轉換。", + "AllLibraries": "所有媒體庫", + "AllEpisodes": "所有劇集", + "AllComplexFormats": "所有格式(ASS,SSA,VOBSUB,PGS,SUB,IDX等)", + "AllChannels": "所有頻道", + "Alerts": "警報", + "AlbumArtist": "專輯歌手", + "Album": "專輯", + "Aired": "已播出", + "AirDate": "播出日期", + "AdditionalNotificationServices": "瀏覽插件目錄以安裝其他通知服務。", + "AddToPlayQueue": "添加到播放列", + "AddToCollection": "添加到收藏", + "AddItemToCollectionHelp": "通過搜索項目並使用右鍵單擊或點擊菜單將其添加到集合中,從而將它們添加到集合中。", + "AccessRestrictedTryAgainLater": "目前限制訪問。 請稍後再試。" } diff --git a/src/strings/zh-tw.json b/src/strings/zh-tw.json index 718cb4d572..7023e564be 100644 --- a/src/strings/zh-tw.json +++ b/src/strings/zh-tw.json @@ -45,7 +45,7 @@ "Edit": "編輯", "EditImages": "編輯圖片", "Ended": "完結", - "EndsAtValue": "完結於{0}", + "EndsAtValue": "於 {0} 結束", "Favorite": "加到最愛", "File": "檔案", "FileNotFound": "未找到檔案。", @@ -67,13 +67,13 @@ "HeaderDeleteItem": "刪除項目", "HeaderEasyPinCode": "簡易 PIN 碼", "HeaderFeatureAccess": "可以使用的功能", - "HeaderFetchImages": "抓取圖像:", + "HeaderFetchImages": "擷取圖像:", "HeaderForgotPassword": "忘記密碼", "HeaderFrequentlyPlayed": "經常播放", "HeaderGuideProviders": "節目表提供者", "HeaderImageSettings": "圖像設置", "HeaderInstantMix": "瞬時混播", - "HeaderLatestEpisodes": "最新節目單元", + "HeaderLatestEpisodes": "最新劇集", "HeaderLatestMovies": "最新電影", "HeaderLatestRecordings": "最新錄影的節目", "HeaderLatestSongs": "最新歌曲", @@ -106,7 +106,7 @@ "LabelAllowServerAutoRestart": "允許伺服器自動重新啟動去安裝更新資料", "LabelAllowServerAutoRestartHelp": "伺服器只會在沒有使用者在使用時重新啟動。", "LabelAudioLanguagePreference": "音頻語言偏好選項:", - "LabelCachePath": "緩存路徑:", + "LabelCachePath": "快取路徑:", "LabelCollection": "收藏櫃:", "LabelContentType": "內容類型:", "LabelCountry": "國家:", @@ -116,7 +116,7 @@ "LabelEnableDlnaClientDiscoveryInterval": "尋找客戶端時間間隔(秒)", "LabelEnableDlnaDebugLogging": "記錄 DLNA 除錯資料到日誌", "LabelEnableDlnaDebugLoggingHelp": "將會建立非常大的日誌檔案,建議在進行故障排除時啟用。", - "LabelEnableDlnaPlayTo": "播放到DLNA設備", + "LabelEnableDlnaPlayTo": "播放到 DLNA 設備", "LabelEnableRealtimeMonitor": "啟用實時監控", "LabelEnableRealtimeMonitorHelp": "支持的檔案系統上的更改,將會立即處理。", "LabelEvent": "事件:", @@ -149,15 +149,15 @@ "LabelTime": "時間:", "LabelTriggerType": "觸發類型:", "LabelUser": "使用者:", - "LabelYourFirstName": "你的名字:", - "LabelYoureDone": "完成!", + "LabelYourFirstName": "您的名字:", + "LabelYoureDone": "完成,耶!", "LibraryAccessHelp": "選擇媒體資料夾與此使用者共享。管理員將可以使用中繼資料管理器編輯所有的媒體資料夾。", "Like": "喜歡", "MaxParentalRatingHelp": "具有較高的家長評級內容將從這使用者被隱藏。", "MessageAreYouSureDeleteSubtitles": "您真的要刪除這個字幕檔嗎?", "MessageDownloadQueued": "下載已排程。", "MessageItemsAdded": "已新增項目。", - "MessageNoMovieSuggestionsAvailable": "目前並沒有推薦的電影。開始觀看並對您的電影評分後,我們就會為您推薦您可能會喜歡的內容。", + "MessageNoMovieSuggestionsAvailable": "目前並沒有推薦的電影,開始觀看並對您的電影評分後,我們就會為您推薦您可能會喜歡的內容。", "MessageNothingHere": "這裡沒有什麼。", "MessagePasswordResetForUsers": "下列使用者的密碼已被重新設置。該使用者現在可以使用在密碼重置時所使用之 PIN 代碼進行登入。", "MessagePleaseEnsureInternetMetadata": "請確保已啟用從互聯網下載媒體資料。", @@ -181,7 +181,7 @@ "OptionBluray": "藍光", "OptionCommunityRating": "社區評分", "OptionContinuing": "持續", - "OptionCriticRating": "評論家評價", + "OptionCriticRating": "影評人評價", "OptionDaily": "每日", "OptionDateAdded": "新增日期", "OptionDatePlayed": "播放日期", @@ -212,7 +212,7 @@ "OptionMissingEpisode": "缺少了的單元", "OptionMonday": "星期一", "OptionNameSort": "名字", - "OptionNew": "新增...", + "OptionNew": "新增…", "OptionOnAppStartup": "在伺服器啟動", "OptionOnInterval": "每時段", "OptionParentalRating": "家長評級", @@ -235,7 +235,7 @@ "OptionWeekly": "每週", "OriginalAirDateValue": "原始播出日期:{0}", "ParentalRating": "Parental Rating", - "PasswordMatchError": "密碼和密碼確認必須一致。", + "PasswordMatchError": "兩個密碼必須一致。", "PasswordResetComplete": "密碼已重設。", "PasswordResetConfirmation": "你確定要重設密碼?", "PasswordSaved": "密碼已保存。", @@ -245,8 +245,8 @@ "RecordingCancelled": "已取消排程錄製。", "RecordingScheduled": "已排程錄製。", "Refresh": "重新整理", - "RefreshDialogHelp": "詳細資料的更新方式會依據Jellyfin的設定及已經啟用的網路服務來進行。", - "ReplaceAllMetadata": "取代所有詳細資料", + "RefreshDialogHelp": "詳細資料的更新方式會依據 Jellyfin 的設定及已經啟用的網路服務來進行。", + "ReplaceAllMetadata": "取代所有中繼資料", "ReplaceExistingImages": "取代現有圖片", "Saturday": "星期六", "Save": "保存", @@ -291,19 +291,19 @@ "TabTrailers": "預告", "TabTranscoding": "轉碼中", "TabUpcoming": "接下來", - "TellUsAboutYourself": "請自我介紹一下你自己", - "ThisWizardWillGuideYou": "此精靈將帶你完成安裝過程。開始之前,請選擇您慣用的語言。", + "TellUsAboutYourself": "介紹一下自己", + "ThisWizardWillGuideYou": "此精靈將帶你完成安裝過程,開始之前,請選擇您慣用的語言。", "Thursday": "星期四", - "TrackCount": "{0}個曲目", + "TrackCount": "{0} 個曲目", "Tuesday": "星期二", - "UninstallPluginConfirmation": "你確定要卸載{0}?", - "UninstallPluginHeader": "卸載插件", + "UninstallPluginConfirmation": "你確定要解除安裝 {0}?", + "UninstallPluginHeader": "解除安裝插件", "UserProfilesIntro": "Jellyfin 可單獨對使用者進行設定,所有使用者擁有自己的顯示設置,播放狀態和家長控制。", "Users": "使用者", "VersionNumber": "版本 {0}", "Wednesday": "星期三", "WelcomeToProject": "歡迎使用 Jellyfin!", - "WizardCompleted": "這就是我們所需的全部資訊,Jellyfin 現在正在收集你的媒體櫃的資料,在這段時間內,不妨參考我們推出的應用程式。按一下完成進入伺服器總覽頁。", + "WizardCompleted": "這就是我們所需的全部資訊,Jellyfin 現在正在收集你的媒體櫃的資料,在這段時間內,不妨參考我們推出的應用程式。按一下完成進入控制台。", "Actor": "演員", "AddToPlayQueue": "加入播放清單", "AddToPlaylist": "加入播放列表", @@ -367,7 +367,7 @@ "ButtonConnect": "連結", "ButtonDown": "下", "ButtonDownload": "下載", - "ButtonEditOtherUserPreferences": "編輯此使用者的內容,大頭貼和個人設定。", + "ButtonEditOtherUserPreferences": "編輯此使用者的內容、大頭貼和個人設定。", "ButtonFullscreen": "全螢幕", "ButtonHelp": "幫助", "ButtonInfo": "詳細資料", @@ -413,12 +413,11 @@ "ChannelNumber": "頻道號碼", "Channels": "頻道", "CinemaModeConfigurationHelp": "劇影院模式直接為您的客廳帶來劇場級體驗,同時還可以播放預告片和自定開場白。", - "CinemaModeConfigurationHelp2": "Jellyfin 應用程式將有一個用於啟動或關閉劇院模式的設定。 電視的應用程式預設開啟劇院模式。", "CinemaModeFeatureDescription": "劇院模式用預告片和自定開場白帶給您最真實的劇院體驗。", "CloudSyncFeatureDescription": "將您的媒體備份到雲端當作簡單的備份,收藏和轉檔。", "Collections": "合輯", "Composer": "作曲家", - "ConfigureDateAdded": "調整伺服器怎麼判定媒體褲的「新增日期」", + "ConfigureDateAdded": "調整伺服器怎麼判定媒體庫的「新增日期」", "ConfirmDeleteImage": "刪除圖片?", "ConfirmDeleteItems": "刪除這些項目會將檔案從系統和媒體庫中刪除。你真的要繼續嗎?", "ConfirmEndPlayerSession": "您要在 {0} 秒後將 Jellyfin關機嗎?", @@ -462,7 +461,7 @@ "DisplayInOtherHomeScreenSections": "在“最新媒體”和“繼續觀看“等主畫面區塊中顯示", "DisplayMissingEpisodesWithinSeasons": "顯示每季缺少的劇集", "DisplayMissingEpisodesWithinSeasonsHelp": "必須在 Jellyfin 伺服器的電視媒體庫設定中啟用該功能。", - "DisplayModeHelp": "選擇您正在運行Jellyfin的螢幕類型。", + "DisplayModeHelp": "選擇您想要的界面樣式。", "DoNotRecord": "不要錄", "Down": "下", "DownloadItemLimitHelp": "非必要。 設置要下載的項目數限制。", @@ -498,25 +497,20 @@ "EnterFFmpegLocation": "輸入 FFmpeg 路徑", "Episodes": "劇集", "Error": "錯誤", - "ErrorAddingListingsToSchedulesDirect": "在將電視節目時間表添加到您的Schedules Direct賬號時出現錯誤。每個Schedules Direct賬號只允許有限的時間表。您在繼續前可能需要登入Schedules Direct網站并刪除賬號中的其它列表。", + "ErrorAddingListingsToSchedulesDirect": "在將電視節目時間表新增到您的 Schedules Direct 帳號時出現錯誤。每個 Schedules Direct 帳號只允許有限的時間表。您在繼續前可能需要登入 Schedules Direct 網站並刪除帳號中的其它列表。", "ErrorAddingGuestAccount1": "新增Jellyfin Connect時發生錯誤。你的賓客有建立Jellyfin帳號嗎?他們可以在 {0} 創建帳號。", "ErrorAddingGuestAccount2": "若你還是遇到問題,請發送email至 {0} 並附上你和他們的email帳號。", - "ErrorAddingJellyfinConnectAccount1": "新增Jellyfin Connect時發生錯誤。您有建立Jellyfin帳號嗎?您可以在 {0} 創建帳號。", - "ErrorAddingJellyfinConnectAccount2": "若你還是遇到問題,請用發生問題的email帳號發送email至 {0}。", - "ErrorAddingJellyfinConnectAccount3": "這個 Jellyfin 帳號已經被連接至一個本地帳號。一個 Jellyfin帳號 只能同時被連接到一個本機帳號。", "ErrorAddingMediaPathToVirtualFolder": "新增媒體路徑時發生錯誤,請確認路徑是否有效,且你的 Jellyfin 伺服器有對該位置的存取權。", "ErrorAddingTunerDevice": "新增調諧器設備時發生錯誤,請確認它是否可被存取後再試一次。", - "ErrorAddingXmlTvFile": "存取 XMLTV 文件時發生錯誤。請確認該檔案是否存在後再試一次。", + "ErrorAddingXmlTvFile": "存取 XMLTV 檔案時發生錯誤,請確認該檔案是否存在後再試一次。", "ErrorConnectServerUnreachable": "處理請求時發生錯誤。您的伺服器無法與我們位於 {0} 的 Jellyfin Connect伺服器溝通。請確認你的伺服器有網路連結且防火牆或其他安全性程式允許這個程式對外溝通。", - "ErrorDeletingItem": "從Jellyfin伺服器刪除項目時發生錯誤。請確認伺服器有那個磁碟的寫入權限並再試一次。", - "ErrorGettingTvLineups": "下載電視節目表時發生錯誤,請確認你的資訊是正確的然後再試一次。", + "ErrorDeletingItem": "從 Jellyfin 伺服器刪除項目時發生錯誤,請確認伺服器對該磁碟有寫入權限並再試一次。", + "ErrorGettingTvLineups": "下載電視節目表時發生錯誤,請確認你的資訊是否正確並重試。", "ErrorMessagePasswordNotMatchConfirm": "密碼和密碼確認必須吻合。", "ErrorMessageStartHourGreaterThanEnd": "結束時間必須在開始時間後。", "ErrorMessageUsernameInUse": "用戶名已存在。請重新選個名稱再試。", "ErrorPleaseSelectLineup": "請選擇節目表,然後再試一次。如果沒有可用的節目表,請檢查您的使用者名稱、密碼和郵遞區號是否正確。", - "ErrorReachingJellyfinConnect": "連接 Jellyfin Connect 伺服器時發生錯誤。請確認你的網絡狀態是否穩定後再試一次。", - "ErrorRemovingJellyfinConnectAccount": "移除 Jellyfin Connect 帳號時發生錯誤。請確認你的網絡狀態是否穩定後再試一次。", - "ErrorSavingTvProvider": "儲存電視供應商時發生錯誤。請確認它是可存取後再試一次。", + "ErrorSavingTvProvider": "儲存電視供應商時發生錯誤,請確認是否可存取並重試。", "EveryNDays": "每 {0} 天", "ExitFullscreen": "結束全螢幕", "ExtraLarge": "特大", @@ -535,22 +529,21 @@ "FolderTypeUnset": "混合內容", "Folders": "資料夾", "FormatValue": "格式:{0}", - "FreeAppsFeatureDescription": "享受免費的Jellyfin應用程式。", "Fullscreen": "全螢幕", "General": "一般", "Genre": "類型", "Genres": "風格", "GroupBySeries": "按系列分組", "GroupVersions": "按版本分組", - "GuestStar": "特邀明星", + "GuestStar": "客串", "GuestUserNotFound": "未找到用戶。請確保用戶名稱正確後重試,或者嘗試輸入他們的電子郵件地址。", "Guide": "指南", "GuideProviderSelectListings": "選擇列表", - "H264CrfHelp": "The Constant Rate Factor (CRF) 是 x264 編碼器的默認畫質設置。此方法允許編碼器自動分配位元速率來試著達到一定輸出品質。讓每個畫格得到它需要的位元數來保持所需的品質等級。CRF 會得到最佳的位元速率分配結果。", + "H264CrfHelp": "CRF 是 x264 編碼器的預設畫質設置。此方法允許編碼器自動分配位元速率來試著達到一定輸出品質。讓每個畫格得到它需要的位元數來保持所需的品質等級。CRF 會得到最佳的位元速率分配結果。", "EncoderPresetHelp": "速度越慢則會得到更好的壓縮編碼效率。", - "HDPrograms": "HD節目", + "HDPrograms": "HD 節目", "HandledByProxy": "由反向代理處理", - "HardwareAccelerationWarning": "啟動硬體加速可能在某些環境下導致系統不穩定。請確認你的作業系統和影片驅動程式是最新的。如果你在開啟此項後播放影片產生困難,那麼你需要將此選項設回”無“。", + "HardwareAccelerationWarning": "啟動硬體加速可能在某些環境下導致系統不穩定。請確認你的作業系統和影片驅動程式是最新的。如果你在開啟此項後難以播放影片,那麼你需要將此選項設回「無」。", "HeaderAccessSchedule": "存取時程", "HeaderAccessScheduleHelp": "建立一個存取時程以限制可存取的時段。", "HeaderActiveDevices": "運行中裝置", @@ -570,7 +563,7 @@ "HeaderAllowMediaDeletionFrom": "允許從中刪除媒體", "HeaderApiKey": "API 金鑰", "HeaderApiKeys": "API 金鑰", - "HeaderApiKeysHelp": "外部應用程式需要有一個 API 金鑰以用於和 Jellyfin 伺服器溝通。金鑰將在通過 Jellyfin 帳戶登入時自動發行,或者你可以手動為應用程式產生一個金鑰。", + "HeaderApiKeysHelp": "外部應用程式需要有一個 API 金鑰以用於和 Jellyfin 伺服器溝通。金鑰會在 Jellyfin 使用者登入時自動發行,或者你可以手動為應用程式產生一個金鑰。", "HeaderApp": "應用程式", "HeaderAppearsOn": "同時出現於", "HeaderAudio": "音訊", @@ -606,16 +599,15 @@ "HeaderConfirmRecordingCancellation": "確認取消錄製", "HeaderConfirmRecordingDeletion": "確認刪除錄影", "HeaderConfirmRemoveUser": "移除用戶", - "HeaderConfirmRevokeApiKey": "撤銷 API 金鑰", + "HeaderConfirmRevokeApiKey": "重置 API 金鑰", "HeaderConfirmSeriesCancellation": "確認系列取消", "HeaderConfirmation": "確認", "HeaderConnectToServer": "連結至伺服器", "HeaderConnectionFailure": "連結失敗", - "HeaderContainerProfile": "影片容器設定檔", + "HeaderContainerProfile": "影片載體設定", "HeaderContainerProfileHelp": "影片容器的設定檔標明了設備播放特定媒體格式時的限制。如果在限制之內則媒體將被轉碼,否則媒體格式將被設定為直接播放。", "HeaderContinueListening": "繼續聆聽", "HeaderContinueWatching": "繼續觀賞", - "HeaderConvertYourRecordings": "為你的錄制轉檔", "HeaderCredits": "製作人員名單", "HeaderDashboardUserPassword": "用戶的密碼被管理在每個用戶的私人配置設置中。", "HeaderDate": "日期", @@ -628,7 +620,7 @@ "HeaderDeleteTaskTrigger": "刪除任務觸發條件", "HeaderDestination": "目的地", "HeaderDetectMyDevices": "偵測我的設備", - "HeaderDeveloperInfo": "開發者訊息", + "HeaderDeveloperInfo": "開發者資訊", "HeaderDevice": "裝置", "HeaderDeviceAccess": "允許裝置存取", "HeaderDevices": "裝置", @@ -651,7 +643,7 @@ "HeaderFavoriteAlbums": "最愛專輯", "HeaderFavoriteArtists": "最愛演出者", "HeaderFavoriteCollections": "最愛收藏", - "HeaderFavoriteEpisodes": "最愛級數", + "HeaderFavoriteEpisodes": "最愛影集", "HeaderFavoriteMovies": "最愛電影", "HeaderFavoritePlaylists": "最愛播放列表", "HeaderFavoriteShows": "最愛節目", @@ -668,7 +660,7 @@ "HeaderHomePage": "主畫面", "HeaderHomeScreen": "主畫面", "HeaderHomeScreenSettings": "主畫面設定", - "HeaderHttpHeaders": "Http 標頭", + "HeaderHttpHeaders": "HTTP 標頭", "HeaderIdentification": "身份識別", "HeaderIdentificationCriteriaHelp": "至少輸入一個識別標準。", "HeaderIdentificationHeader": "身份識別標題", @@ -679,14 +671,7 @@ "HeaderImagePrimary": "主要", "HeaderImages": "圖片", "HeaderInstall": "安裝", - "HeaderInvitationSent": "邀請已傳送", - "HeaderInvitations": "邀請", - "HeaderInviteUser": "邀請使用者", - "HeaderInviteUserHelp": "通過 Jellyfin Connect 與朋友分享你的媒體比以往任何時候都更為簡單。", - "HeaderInviteWithJellyfinConnect": "通過 Jellyfin Connect 邀請", "HeaderItems": "項目", - "HeaderJellyfinAccountAdded": "Jellyfin 帳戶已添加", - "HeaderJellyfinAccountRemoved": "已移除 Jellyfin 帳戶", "HeaderKeepRecording": "繼續錄製", "HeaderKeepSeries": "保存系列", "HeaderKodiMetadataHelp": "要啟用或禁用 NFO 元數據, 請在 Jellyfin “建立媒體庫”頁面中編輯該媒體庫, 然後找到“元數據儲存”部分。", @@ -734,10 +719,6 @@ "HeaderNextVideoPlayingInValue": "下一部影片在 {0} 後播放", "HeaderNotifications": "通知", "HeaderNumberOfPlayers": "播放器", - "HeaderOffline": "離線", - "HeaderOfflineDownloads": "離線媒體", - "HeaderOfflineDownloadsDescription": "下載媒體以離線使用。", - "HeaderOfflineSync": "離線同步", "HeaderOnNow": "現正播放", "HeaderOptions": "選項", "HeaderOtherDisplaySettings": "顯示設定", @@ -824,14 +805,9 @@ "HeaderTryPlayback": "嘗試播放", "HeaderTunerDevices": "調諧器裝置", "BobAndWeaveWithHelp": "Bob and weave (高品質,轉檔慢)", - "ButtonPurchase": "購買", - "ButtonRestorePreviousPurchase": "恢復購買", - "ButtonUnlockWithPurchase": "購買已解鎖", - "LabelIpAddressValue": "IP 位置: {0}", "LabelRunningTimeValue": "運行時間: {0}", "MessageApplicationUpdated": "Jellyfin Server 已經更新", "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 部分已經更新", - "MessageServerConfigurationUpdated": "伺服器設定已經更新", "Movies": "電影", "Photos": "相片", "Playlists": "播放清單", @@ -871,9 +847,9 @@ "HeaderVideoType": "影片類型", "HeaderVideoTypes": "影片類型", "HeaderVideos": "影片", - "HeaderXmlDocumentAttribute": "XML檔案屬性", - "HeaderXmlDocumentAttributes": "XML檔案屬性", - "HeaderXmlSettings": "XML設置", + "HeaderXmlDocumentAttribute": "XML 檔案屬性", + "HeaderXmlDocumentAttributes": "XML 檔案屬性", + "HeaderXmlSettings": "XML 設置", "HeaderYears": "年份", "HeadersFolders": "資料夾", "Hide": "隱藏", @@ -883,14 +859,14 @@ "HttpsRequiresCert": "要啟用安全連線,您需要提供受信任的SSL證書,如 Let's Encrypt。 請提供證書,或停用安全連線。", "Identify": "識別", "Images": "圖片", - "ImportFavoriteChannelsHelp": "如果啟用,只有在調諧器設備中被標記為我的最愛的頻道才會被導入。", - "ImportMissingEpisodesHelp": "如果啟用,有關缺失劇集的數據導入您的Jellyfin媒體庫,並在季節和系列中顯示。 這可能會導致媒體庫掃描延長。", - "InstallingPackage": "正在安装 {0}", + "ImportFavoriteChannelsHelp": "若啟用,只有在調諧器設備中被標記為我的最愛的頻道才會被導入。", + "ImportMissingEpisodesHelp": "若啟用,有關缺失劇集的數據導入您的 Jellyfin 媒體庫,並在季節和系列中顯示。 這可能會導致媒體庫掃描延長。", + "InstallingPackage": "正在安装 {0}(版本 {1})", "InstantMix": "即時混音", "Items": "項目", "Kids": "兒童", - "Label3DFormat": "3D格式:", - "LabelAbortedByServerShutdown": "(因為伺服器關閉被中止)", + "Label3DFormat": "3D 格式:", + "LabelAbortedByServerShutdown": "(因為伺服器關閉被中止)", "LabelAccessDay": "一周中的何時:", "LabelAccessEnd": "結束時間:", "LabelAccessStart": "開始時間:", @@ -904,24 +880,24 @@ "LabelAlbumArtMaxHeight": "專輯封面最大高度:", "LabelAlbumArtMaxHeightHelp": "通過 upnp:albumArtURI 顯示的專輯封面超鏈接的最大分辨率。", "LabelAlbumArtMaxWidth": "專輯封面最大寬度:", - "LabelAlbumArtMaxWidthHelp": "通過upnp:albumArtURI顯示的專輯封面超鏈接的最大分辨率。", - "LabelAlbumArtPN": "專輯封面PN :", + "LabelAlbumArtMaxWidthHelp": "通過 upnp:albumArtURI 顯示的專輯封面超鏈接的最大解析度。", + "LabelAlbumArtPN": "專輯封面 PN :", "LabelAlbumArtists": "專輯作家:", "LabelAll": "所有", "LabelAllowHWTranscoding": "允許硬體轉碼", - "LabelAllowedRemoteAddresses": "遠端IP地址過濾:", - "LabelAllowedRemoteAddressesMode": "遠端IP地址過濾模式:", - "LabelAppName": "APP名稱", - "LabelAppNameExample": "例如: Sickbeard, Sonarr", + "LabelAllowedRemoteAddresses": "遠端 IP 過濾:", + "LabelAllowedRemoteAddressesMode": "遠端 IP 過濾模式:", + "LabelAppName": "APP 名稱", + "LabelAppNameExample": "例如:可愛蹦蹦主機、小安的 Jellyfin", "LabelArtists": "藝人:", "LabelArtistsHelp": "分開多重使用 ;", "LabelAudio": "音頻", "LabelAuthProvider": "認證提供者:", "LabelAutomaticallyRefreshInternetMetadataEvery": "從網路自動刷新數據:", "LabelBindToLocalNetworkAddress": "聯結本地網絡地址:", - "LabelBindToLocalNetworkAddressHelp": "(可選的)覆蓋 HTTP 服務器綁定的本地 IP 地址。如果留空,服務器將會監聽所有可用的地址。改變這個值需要重啟 Jellyfin 伺服器\n。", + "LabelBindToLocalNetworkAddressHelp": "(選用)覆蓋 HTTP 服務器綁定的本地 IP 地址。如果留空,服務器將會監聽所有可用的地址。改變這個值需要重啟 Jellyfin 伺服器。", "LabelBirthDate": "出生日期:", - "LabelBirthYear": "出生年份:", + "LabelBirthYear": "出生年:", "LabelBlastMessageInterval": "活動信號的時間間隔(秒)", "LabelBlastMessageIntervalHelp": "確定伺服器活動消息之間的持續時間(秒)。", "LabelBlockContentWithTags": "通過標籤鎖定內容:", @@ -940,17 +916,17 @@ "LabelCustomCssHelp": "於 Web 介面套用您的自訂樣式。", "LabelCustomDeviceDisplayName": "顯示名稱:", "Depressed": "凹陷", - "HeaderHome": "主頁", + "HeaderHome": "首頁", "HeaderSelectMetadataPathHelp": "瀏覽或者輸入路徑以用於保存中繼資料,請確保資料夾可以寫入。", "HeaderSelectServerCachePathHelp": "瀏覽或者輸入路徑以用於伺服器快取檔案。請確保該資料夾可以被寫入。", "LabelCustomDeviceDisplayNameHelp": "指定自訂的顯示名稱,或者留空以使用設備自己報告的名稱。", "LabelCustomRating": "自訂分級:", - "LabelDashboardTheme": "儀表板佈景主題:", + "LabelDashboardTheme": "控制台佈景主題:", "LabelDateAdded": "新增日期:", "LabelDateAddedBehavior": "新内容加入的日期應使用:", "LabelDateTimeLocale": "設定時區:", "LabelDeathDate": "死亡時間:", - "LabelDefaultScreen": "預設螢幕:", + "LabelDefaultScreen": "預設分頁:", "LabelDefaultUser": "預設使用者:", "LabelDeviceDescription": "裝置說明", "LabelDidlMode": "DIDL 模式:", @@ -966,13 +942,13 @@ "OptionMax": "最大", "LabelAudioBitDepth": "音訊位元深度:", "LabelBaseUrl": "根路徑:", - "LabelIconMaxHeight": "Icon 最高高度:", + "LabelIconMaxHeight": "圖示最高高度:", "LabelHttpsPortHelp": "Jellyfin 的 HTTPS 伺服器應綁定的 TCP 端口。", "LabelIconMaxHeightHelp": "通過 upnp:icon 的圖標最大解析度。", "CopyStreamURL": "複製串流連結", "MediaInfoDefault": "預設", "MediaInfoStreamTypeAudio": "音訊", - "LabelDateAddedBehaviorHelp": "如果原本就有中繼資料,則將始終在這些選項之一之前使用它。", + "LabelDateAddedBehaviorHelp": "如果原本就有中繼資料,將會優先使用其數據。", "LabelScreensaver": "螢幕保護程式:", "LabelSeasonNumber": "季:", "LabelDropImageHere": "拖移圖片到這裡,或是點擊來選取。", @@ -988,7 +964,7 @@ "LabelVersionInstalled": "{0} 已安裝", "DashboardVersionNumber": "版本:{0}", "DashboardServerName": "伺服器:{0}", - "NoSubtitles": "沒有字幕", + "NoSubtitles": "無", "List": "清單", "OptionAllowMediaPlayback": "允許播放媒體", "OneChannel": "單聲道", @@ -1010,7 +986,7 @@ "ValueCodec": "編碼:{0}", "ValueSongCount": "{0} 首歌", "LabelFileOrUrl": "檔案或路徑:", - "LabelKodiMetadataSaveImagePaths": "在 nfo 檔案中儲存圖片路徑", + "LabelKodiMetadataSaveImagePaths": "在 NFO 檔案中儲存圖片路徑", "LabelLanNetworks": "區域網路:", "LabelMetadataPathHelp": "指定自訂路徑來儲存下載的圖片與中繼資料。", "LabelZipCode": "郵遞區號:", @@ -1024,7 +1000,7 @@ "ShowAdvancedSettings": "顯示進階選項", "ShowTitle": "顯示標題", "ShowYear": "顯示年份", - "Shuffle": "隨ㄔㄧ", + "Shuffle": "隨機播放", "Smart": "智慧", "HeaderFavoriteBooks": "最愛的書籍", "LabelAudioBitrate": "音訊比特率:", @@ -1130,7 +1106,7 @@ "ManageRecording": "管理錄影", "MessageAlreadyInstalled": "已安裝此版本。", "MessageConfirmRestart": "您確定要重新啟動嗎?", - "Metadata": "ˊ中繼資料", + "Metadata": "中繼資料", "OptionAllUsers": "所有使用者", "OptionHomeVideos": "圖片", "OptionPoster": "海報", @@ -1144,7 +1120,7 @@ "TV": "電視", "TabUsers": "使用者", "Trailers": "預告", - "LabelImageFetchersHelp": "按優先級啟用並排列您喜歡的圖片抓取器。", + "LabelImageFetchersHelp": "啟用並按優先順序排序您的首選圖片擷取器。", "LabelDownMixAudioScale": "縮混時的音訊增強:", "LabelDownMixAudioScaleHelp": "縮混時增強音訊。其中一個音軌將保持原始音量。", "LabelDownloadLanguages": "下載語言:", @@ -1162,7 +1138,7 @@ "LabelSelectUsers": "選擇使用者:", "LabelSelectVersionToInstall": "選擇要安裝的版本:", "LabelSendNotificationToUsers": "傳送通知給:", - "LabelSortBy": "排序依:", + "LabelSortBy": "排序按照:", "LabelVideoBitrate": "影片比特率:", "MediaInfoSize": "大小", "MediaInfoTimestamp": "時間戳", @@ -1295,7 +1271,7 @@ "Up": "上", "ValueOneSeries": "1 劇集", "Writer": "編劇", - "XmlTvMovieCategoriesHelp": "有這些類別的節目會被當作電影。用「|」分隔多個。", + "XmlTvMovieCategoriesHelp": "有這些類別的節目會被當作電影,以「|」來分隔多個項目。", "ValueSeriesCount": "{0} 劇集", "LabelHardwareAccelerationTypeHelp": "硬件加速需要額外的配置。", "LabelHomeNetworkQuality": "區域網路畫質:", @@ -1317,7 +1293,7 @@ "OptionEstimateContentLength": "轉檔時,估計內容長度", "OptionExternallyDownloaded": "外部下載", "OptionHlsSegmentedSubtitles": "HLS 分段字幕", - "OptionLoginAttemptsBeforeLockout": "確定在被封鎖之前可以登入失敗幾次。", + "OptionLoginAttemptsBeforeLockout": "在被封鎖之前可以登入失敗幾次。", "OptionRequirePerfectSubtitleMatch": "只下載與我的影片檔案完美匹配的字幕", "PlayCount": "播放次數", "Series": "電視劇", @@ -1342,7 +1318,7 @@ "LabelMoviePrefixHelp": "若前綴套用到電影標題,請在此處輸入它來方便伺服器能夠正確處理。", "LabelMovieRecordingPath": "電影錄製路徑(選用):", "LabelNotificationEnabled": "啟用這個通知", - "LabelProfileContainersHelp": "以逗號分隔。留空則適用於所有影片容器。", + "LabelProfileContainersHelp": "以逗號分隔,留空則適用於所有影片容器。", "LabelSelectFolderGroupsHelp": "未選中的資料夾將在其自己的檢視中顯示。", "LabelSerialNumber": "序號", "LabelSeriesRecordingPath": "電視劇錄影路徑(選用):", @@ -1398,7 +1374,7 @@ "MessageDirectoryPickerLinuxInstruction": "使用 Linux on Arch Linux、CentOS、Debian、Fedora、openSUSE 或 Ubuntu 作業系統,您必須授權使用者至少讀取權限來存取您的儲存路徑。", "MessageEnablingOptionLongerScans": "啟用這個選項可能會延長媒體庫的掃描時間。", "MessageFileReadError": "讀取檔案時發生錯誤。", - "MessageForgotPasswordInNetworkRequired": "請檢查您的區域網路後再試一次來開始密碼重置流程。", + "MessageForgotPasswordInNetworkRequired": "請檢查您的區域網路後再開始密碼重置流程。", "MessageForgotPasswordFileCreated": "已在伺服器上建立了以下檔案,並包含有關後續步驟說明:", "MessageNoAvailablePlugins": "沒有可用的模組。", "MessageNoServersAvailable": "無法自動偵測伺服器。", @@ -1414,7 +1390,7 @@ "News": "新聞", "NoNewDevicesFound": "找不到裝置,要添加新調諧器,請關閉此對話框並手動輸入裝置訊息。", "OnlyForcedSubtitles": "僅顯示強制字幕", - "OnlyImageFormats": "僅圖片格式(VOBSUB、PGS、SUB 等)", + "OnlyImageFormats": "圖片格式(VOBSUB、PGS、SUB)", "OptionAllowLinkSharingHelp": "只有網頁包含的媒體訊息會被共享,媒體檔案本身不會被公開共享,共享的內容會在 {0} 天後到期。", "OptionAllowRemoteSharedDevices": "允許遠端控制共享裝置", "OptionAllowSyncTranscoding": "允許需要轉檔的媒體下載和同步", @@ -1441,7 +1417,7 @@ "OptionSubstring": "子串", "OptionWeekdays": "工作日", "Overview": "概述", - "PackageInstallCancelled": "{0} 安裝被取消。", + "PackageInstallCancelled": "{0} (版本 {1})安裝被取消。", "PlayAllFromHere": "從這裡開始全部播放", "PleaseAddAtLeastOneFolder": "請點擊新增按鈕,新增至少一個資料夾到這個媒體庫。", "PleaseConfirmPluginInstallation": "點擊「OK」以確認您已經閱讀了上述內容並希望繼續安裝模組。", @@ -1459,7 +1435,7 @@ "ProductionLocations": "產地", "Programs": "節目", "Quality": "品質", - "PackageInstallFailed": "{0} 安裝失敗。", + "PackageInstallFailed": "{0} (版本 {1}) 安裝失敗。", "QueueAllFromHere": "將這裡的全部內容加入佇列", "Raised": "提高", "Rate": "評等", @@ -1474,7 +1450,7 @@ "Uniform": "輪廓", "Unmute": "取消靜音", "Unplayed": "尚未播放", - "TvLibraryHelp": "查看{0}電視命名指南{1}。", + "TvLibraryHelp": "查看 {0} 電視命名指南 {1} 。", "LabelMonitorUsers": "監控活動:", "LabelPleaseRestart": "改動將在手動重啟用戶端後生效。", "LabelProfileCodecsHelp": "以逗號分隔。留空則適用於所有編解碼器。", @@ -1482,7 +1458,7 @@ "LabelInNetworkSignInWithEasyPasswordHelp": "你可以在你的家庭網路中使用你的簡易 PIN 碼登錄 Jellyfin 應用程式,僅在你使用外部網路時才需要輸入密碼,如果 PIN 碼留空,那麼在你的區域網路中便不需輸入密碼。", "LabelReleaseDate": "釋出日期:", "LabelRemoteClientBitrateLimit": "網際網路串流傳輸位元率限制(Mbps):", - "LanNetworksHelp": "在强制頻寬限制時,認作本地網路上的 IP 地址或 IP/網路掩碼條目的逗號分隔列表。如果設置此項,所有其它 IP 地址將被視作在外部網路上,并且將受到外部頻寬限制。如果保留爲空,則只將服務器的子網視作本地網路。", + "LanNetworksHelp": "在強制頻寬限制時,認作本地網路上的 IP 地址或 IP/子網域遮罩項目的逗號分隔列表。若設置此項,所有其它 IP 地址將被視作在外部網路上,并且將受到外部頻寬限制。如果保留爲空,則只將服務器的子網域遮罩作本地網路。", "OptionIgnoreTranscodeByteRangeRequests": "忽略轉檔位元組範圍請求", "OptionIgnoreTranscodeByteRangeRequestsHelp": "如果啟用,這些請求會被兌現,但會忽略的位元組範圍標頭。", "OptionLoginAttemptsBeforeLockoutHelp": "若值為 0,則表示將允許普通使用者嘗試三次、管理員嘗試五次的預設值,設定為 -1 來停用此功能。", @@ -1507,8 +1483,8 @@ "SystemDlnaProfilesHelp": "系統設定檔案是唯讀的,更改系統設定檔案將被儲存到自訂的新設定檔案。", "LabelNumber": "編號:", "LabelNumberOfGuideDays": "下載電視指南日數:", - "OnlyForcedSubtitlesHelp": "只有標記為「強制」的字幕會被載入。", - "PackageInstallCompleted": "{0} 安裝完成。", + "OnlyForcedSubtitlesHelp": "僅被標記為「強制」的字幕會被載入。", + "PackageInstallCompleted": "{0} (版本 {1}) 安裝完成。", "OptionDisplayFolderViewHelp": "在其他媒體庫旁邊顯示資料夾,對想要一個普通的資料夾檢視很有用。", "LabelReasonForTranscoding": "轉檔原因:", "LabelRecord": "錄影:", @@ -1534,7 +1510,7 @@ "LabelSportsCategories": "體育分類:", "LabelStartWhenPossible": "當可能時自動開始:", "LabelVaapiDevice": "VA API 裝置:", - "DashboardArchitecture": "結構:{0}", + "DashboardArchitecture": "架構:{0}", "MediaInfoSampleRate": "採樣率", "MessageContactAdminToResetPassword": "請聯繫您的管理員來重置密碼。", "MessageUnsetContentHelp": "內容將顯示為純資料夾,建議使用中繼資料管理器設置子資料夾的內容類型。", @@ -1542,7 +1518,7 @@ "OptionCustomUsers": "自訂", "OptionDateAddedFileTime": "使用檔案建立日期", "OptionReportByteRangeSeekingWhenTranscodingHelp": "這對一些時間跳轉緩慢的裝置是必要的。", - "XmlTvNewsCategoriesHelp": "有這些類別的節目會被當作新聞節目。用「|」分隔多個。", + "XmlTvNewsCategoriesHelp": "有這些類別的節目會被當作新聞節目,以「|」來分隔多個項目。", "LabelKodiMetadataEnableExtraThumbsHelp": "為了相容 Kodi 主題,下載的圖片將被同時儲存在 extrafanart 和 extrathumbs 資料夾中。", "LabelInternetQuality": "網路畫質:", "LabelKodiMetadataEnablePathSubstitutionHelp": "允許將圖片的路徑以伺服器路徑來替換。", @@ -1575,16 +1551,16 @@ "LabelPostProcessorArguments": "處理器後命令行參數:", "LabelPostProcessorArgumentsHelp": "使用 {path} 作為錄製檔案的路徑。", "LabelPreferredDisplayLanguage": "首選語言:", - "LabelPreferredDisplayLanguageHelp": "翻譯 Jellyfin 是一個進行中的項目。", + "LabelPreferredDisplayLanguageHelp": "歡迎一起翻譯 Jellyfin!", "LabelPreferredSubtitleLanguage": "字幕語言偏好:", - "LabelProtocol": "協議:", + "LabelProtocol": "協議:", "LabelProtocolInfo": "協議資訊:", "LabelPublicHttpPort": "公開 HTTP 端口:", "LabelPublicHttpsPort": "公開 HTTPS 端口:", "LabelProtocolInfoHelp": "當響應來自裝置的 GetProtocolInfo(獲取協議訊息)請求時,該值將被使用。", "LabelPublicHttpPortHelp": "公開連接埠應映射到本地 HTTP 連接埠。", "LabelPublicHttpsPortHelp": "公開連接埠應映射到本地 HTTPS 連接埠。", - "LabelReadHowYouCanContribute": "了解如何作出貢獻。", + "LabelReadHowYouCanContribute": "瞭解如何一同貢獻。", "LabelSelectFolderGroups": "自動將以下資料夾中的內容分組到視圖中,例如電影、音樂和電視:", "LabelStatus": "狀態:", "LiveBroadcasts": "直播", @@ -1603,13 +1579,13 @@ "Vertical": "垂直", "VideoRange": "影片範圍", "ViewPlaybackInfo": "查看播放訊息", - "XmlTvSportsCategoriesHelp": "有這些類別的節目會被當作體育節目。用「|」分隔多個。", + "XmlTvSportsCategoriesHelp": "有這些類別的節目會被當作體育節目,以「|」來分隔多個項目。", "XmlTvPathHelp": "XML 電視檔案的路徑,Jellyfin 將讀取該檔案並定期檢查其更新,您負責建立和更新檔案。", "MessageInvalidForgotPasswordPin": "簡易代碼錯誤或已過期,請再試一次。", "OptionAllowVideoPlaybackTranscoding": "允許播放需要轉檔的影片", "Small": "小", "Smaller": "更小", - "XmlTvKidsCategoriesHelp": "有這些類別的節目會被當作兒童節目。用「|」分隔多個。", + "XmlTvKidsCategoriesHelp": "有這些類別的節目會被當作兒童節目,以「|」來分隔多個項目。", "TabResponses": "響應", "LabelDisplaySpecialsWithinSeasons": "顯示劇集季度中的特集", "LabelNumberOfGuideDaysHelp": "下載更多電視指南資料會提供更好時間表查看能力,但將需要更長的下載時間。自動基於頻道數目來選擇。", @@ -1628,14 +1604,12 @@ "SelectAdminUsername": "請為管理員賬戶選擇一個用戶名。", "CopyStreamURLError": "複製網址的時候發生錯誤.", "OptionSaveMetadataAsHiddenHelp": "更改此項將應用於以後保存的元數據。現有元數據文件將在下一次 Jellyfin 伺服器保存它們時被更新。", - "OptionAllowRemoteSharedDevicesHelp": "DLNA 設備在用戶對它們進行控制前都被視作共享的。", + "OptionAllowRemoteSharedDevicesHelp": "DLNA裝置將被視為共享中,直至有使用者控制。", "OptionForceRemoteSourceTranscoding": "强制遠端轉碼(像電視直播一樣)", "MessageConfirmAppExit": "您要退出嗎?", "LaunchWebAppOnStartupHelp": "伺服器啓動時在默認游覽器中打開網頁端。使用重啓伺服器功能時此項不生效。", "LabelVideoResolution": "視頻解析度:", "LabelStreamType": "串流類型:", - "EnableFastImageFadeInHelp": "對已載入的圖片啟用更快的淡入動畫", - "EnableFastImageFadeIn": "圖片快速淡入效果", "LabelPlayerDimensions": "播放器尺寸:", "LabelDroppedFrames": "丟棄的幀:", "LabelCorruptedFrames": "損壞的幀:", @@ -1646,6 +1620,42 @@ "AllowFfmpegThrottlingHelp": "當轉檔或重組進度大量超前目前播放進度時,將暫停轉檔節省消耗的資源。在不常跳播的時候最有效。如果遇到播放問題,請關閉此功能。", "AllowFfmpegThrottling": "限制轉檔", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "這將會使用內建劇集資料。", - "PlaybackErrorNoCompatibleStream": "用戶端偵測出了問題,伺服器也未傳送相容的媒體格式。", - "PreferEmbeddedEpisodeInfosOverFileNames": "優先使用內建劇集資訊而不是檔案名稱" + "PlaybackErrorNoCompatibleStream": "用戶端與該媒體不相容,伺服器也未傳送相容的媒體格式。", + "PreferEmbeddedEpisodeInfosOverFileNames": "優先使用內建劇集資訊而不是檔案名稱", + "OtherArtist": "其他歌手", + "Artist": "歌手", + "AlbumArtist": "專輯歌手", + "Album": "專輯", + "YadifBob": "YADIF Bob", + "WriteAccessRequired": "Jellyfin 伺服器需要此資料夾的寫入權限,請確認是否擁有寫入權限並重試。", + "PathNotFound": "無法找到此路徑,請確認路徑可用並重試。", + "Track": "音軌", + "Yadif": "YADIF", + "ListPaging": "{2} 的 {0}-{1}", + "PersonRole": "作為 {0}", + "LastSeen": "上次觀看 {0}", + "DailyAt": "每日的 {0}", + "WeeklyAt": "每週第 {0} 天的 {1}", + "OnWakeFromSleep": "當從睡眠中喚醒時", + "EveryXMinutes": "每 {0} 分鐘", + "EveryHour": "每小時", + "EveryXHours": "每 {0} 小時", + "OnApplicationStartup": "當應用程式啟動時", + "Season": "季", + "ReleaseGroup": "發行組織", + "Person": "人物", + "Movie": "電影", + "LabelLibraryPageSizeHelp": "設置媒體庫頁面每頁要顯示的最多媒體個數。設置為 0 來停用分頁。", + "LabelLibraryPageSize": "媒體庫分頁大小:", + "LabelDeinterlaceMethod": "反交錯方法:", + "Episode": "劇集", + "DeinterlaceMethodHelp": "選擇對隔行掃描內容進行轉碼時所用的反交錯方法。", + "BoxSet": "套裝", + "UnsupportedPlayback": "Jellyfin 無法解密受 DRM 保護的內容,但仍然會嘗試播放所有內容。某些檔案由於被加密或包含如互動標題等不受支援的內容,在播放時可能會沒有畫面。", + "MessageUnauthorizedUser": "您目前無權存取伺服器,請與您的伺服器管理員聯繫以獲取更多訊息。", + "Filter": "篩選器", + "New": "新增", + "ApiKeysCaption": "目前已啟用的API金鑰列表", + "ButtonTogglePlaylist": "播放清單", + "ButtonToggleContextMenu": "更多" } diff --git a/src/themes/appletv/theme.css b/src/themes/appletv/theme.css index 5ca517bea5..b3ce2c7e92 100644 --- a/src/themes/appletv/theme.css +++ b/src/themes/appletv/theme.css @@ -43,7 +43,9 @@ html { } .backgroundContainer, -.dialog { +.dialog, +.nowPlayingPlaylist, +.nowPlayingContextMenu { background: #d5e9f2; -webkit-background-size: 100% 100%; background-size: 100% 100%; @@ -186,7 +188,8 @@ html { } .appfooter, -.formDialogFooter:not(.formDialogFooter-clear) { +.formDialogFooter:not(.formDialogFooter-clear), +.playlistSectionButton { color: rgba(0, 0, 0, 0.7); background: #303030; background: -webkit-gradient(linear, left top, right top, from(#bcbcbc), color-stop(#a7b4b7), color-stop(#beb5a5), color-stop(#adbec2), to(#b9c7cb)); @@ -290,7 +293,17 @@ html { .emby-checkbox:checked + span + .checkboxOutline, .itemProgressBarForeground { - background-color: #00a4dc; + background: linear-gradient(90deg, rgba(0, 210, 201, 1) 0%, rgba(13, 194, 98, 1) 28%, rgba(0, 75, 185, 1) 100%); +} + +.itemProgressBar { + background: rgba(230, 230, 230, 0.8); + height: 0.18rem; +} + +.innerCardFooter { + border-radius: 0.14rem; + margin: 0.4rem 0.5rem 0.4rem 0.5rem; } .emby-checkbox:focus:not(:checked) + span + .checkboxOutline { @@ -442,6 +455,12 @@ html { border-color: #00a4dc !important; } +.cardContent-button, +.itemDetailImage, +.cardOverlayContainer { + border-radius: 0.5rem; +} + .metadataSidebarIcon { color: #00a4dc; } diff --git a/src/themes/blueradiance/theme.css b/src/themes/blueradiance/theme.css index b929c2a296..74a60c91c0 100644 --- a/src/themes/blueradiance/theme.css +++ b/src/themes/blueradiance/theme.css @@ -42,6 +42,8 @@ html { } .dialog, +.nowPlayingPlaylist, +.nowPlayingContextMenu, html { background-color: #033361; } @@ -129,23 +131,23 @@ html { } .defaultCardBackground1 { - background-color: #d2b019; + background-color: #213440; } .defaultCardBackground2 { - background-color: #338abb; + background-color: #8c6565; } .defaultCardBackground3 { - background-color: #6b689d; + background-color: #57778c; } .defaultCardBackground4 { - background-color: #dd452b; + background-color: #8c8c49; } .defaultCardBackground5 { - background-color: #5ccea9; + background-color: #404024; } .cardText-secondary, @@ -179,7 +181,8 @@ html { color: rgba(255, 255, 255, 0.87); } -.appfooter { +.appfooter, +.playlistSectionButton { background: #033664; color: #ccc; color: rgba(255, 255, 255, 0.78); diff --git a/src/themes/dark/theme.css b/src/themes/dark/theme.css index 4363be991b..a32e606386 100644 --- a/src/themes/dark/theme.css +++ b/src/themes/dark/theme.css @@ -36,6 +36,8 @@ html { .backgroundContainer, .dialog, +.nowPlayingPlaylist, +.nowPlayingContextMenu, html { background-color: #101010; } @@ -111,23 +113,23 @@ html { } .defaultCardBackground1 { - background-color: #d2b019; + background-color: #00455c; } .defaultCardBackground2 { - background-color: #338abb; + background-color: #44bae1; } .defaultCardBackground3 { - background-color: #6b689d; + background-color: #00a4db; } .defaultCardBackground4 { - background-color: #dd452b; + background-color: #1c4c5c; } .defaultCardBackground5 { - background-color: #5ccea9; + background-color: #007ea8; } .cardText-secondary, @@ -161,7 +163,8 @@ html { color: rgba(255, 255, 255, 0.87); } -.appfooter { +.appfooter, +.playlistSectionButton { background: #202020; color: #ccc; color: rgba(255, 255, 255, 0.78); diff --git a/src/themes/light/theme.css b/src/themes/light/theme.css index d84a1c3b67..114ef7c3b1 100644 --- a/src/themes/light/theme.css +++ b/src/themes/light/theme.css @@ -51,7 +51,9 @@ html { background-color: rgba(255, 255, 255, 0.8); } -.dialog { +.dialog, +.nowPlayingPlaylist, +.nowPlayingContextMenu { background-color: #f0f0f0; } @@ -183,7 +185,8 @@ html { color: rgba(255, 255, 255, 0.87); } -.appfooter { +.appfooter, +.playlistSectionButton { background: #282828; color: #ccc; color: rgba(255, 255, 255, 0.78); diff --git a/src/themes/purplehaze/theme.css b/src/themes/purplehaze/theme.css index 45f43abc2f..de69a5542a 100644 --- a/src/themes/purplehaze/theme.css +++ b/src/themes/purplehaze/theme.css @@ -37,6 +37,8 @@ html { } .dialog, +.nowPlayingPlaylist, +.nowPlayingContextMenu, html { background-color: #230c33; } @@ -219,23 +221,23 @@ a[data-role=button] { } .defaultCardBackground1 { - background-color: #d2b019; + background-color: #9c20ab; } .defaultCardBackground2 { - background-color: #338abb; + background-color: #d799de; } .defaultCardBackground3 { - background-color: #6b689d; + background-color: #412378; } .defaultCardBackground4 { - background-color: #dd452b; + background-color: #857b48; } .defaultCardBackground5 { - background-color: #5ccea9; + background-color: #ab8820; } .cardText-secondary, @@ -269,7 +271,8 @@ a[data-role=button] { color: rgba(255, 255, 255, 0.87); } -.appfooter { +.appfooter, +.playlistSectionButton { background: #06256f; color: #ccc; color: rgba(255, 255, 255, 0.78); diff --git a/src/themes/wmc/theme.css b/src/themes/wmc/theme.css index 6143b7fe2c..e7d4c0371b 100644 --- a/src/themes/wmc/theme.css +++ b/src/themes/wmc/theme.css @@ -48,7 +48,9 @@ html { } .backgroundContainer, -.dialog { +.dialog, +.nowPlayingPlaylist, +.nowPlayingContextMenu { background: -webkit-gradient(linear, left top, left bottom, from(#0f3562), color-stop(#1162a4), to(#03215f)); background: -webkit-linear-gradient(top, #0f3562, #1162a4, #03215f); background: -o-linear-gradient(top, #0f3562, #1162a4, #03215f); @@ -172,7 +174,8 @@ html { } .appfooter, -.formDialogFooter:not(.formDialogFooter-clear) { +.formDialogFooter:not(.formDialogFooter-clear), +.playlistSectionButton { background: #0c2450; background: -webkit-gradient(linear, left bottom, left top, from(#0c2450), to(#081b3b)); background: -webkit-linear-gradient(bottom, #0c2450, #081b3b); diff --git a/src/tv.html b/src/tv.html index 9a47c6fa8b..ceb5c51b44 100644 --- a/src/tv.html +++ b/src/tv.html @@ -3,9 +3,9 @@
- - - + + +
@@ -59,9 +59,9 @@
- - - + + +
diff --git a/src/useredit.html b/src/useredit.html index 0de3069dc7..c3a613bed4 100644 --- a/src/useredit.html +++ b/src/useredit.html @@ -6,7 +6,7 @@ @@ -104,6 +104,16 @@
${LabelUserRemoteClientBitrateLimitHelp}
+
+
+ +
${SyncPlayAccessHelp}
+
+

${HeaderAllowMediaDeletionFrom}

diff --git a/src/userlibraryaccess.html b/src/userlibraryaccess.html index 4653fd07eb..8a6177585a 100644 --- a/src/userlibraryaccess.html +++ b/src/userlibraryaccess.html @@ -6,7 +6,7 @@ diff --git a/src/usernew.html b/src/usernew.html index bca1e72fea..fac036aa8d 100644 --- a/src/usernew.html +++ b/src/usernew.html @@ -5,7 +5,7 @@

${HeaderAddUser}

- ${Help} + ${Help}
diff --git a/src/userparentalcontrol.html b/src/userparentalcontrol.html index 4cb5708db7..2c13ec8012 100644 --- a/src/userparentalcontrol.html +++ b/src/userparentalcontrol.html @@ -4,7 +4,7 @@ @@ -31,7 +31,7 @@

${LabelBlockContentWithTags}

@@ -41,7 +41,7 @@

${HeaderAccessSchedule}

diff --git a/src/userpassword.html b/src/userpassword.html index f38e395999..119a0212de 100644 --- a/src/userpassword.html +++ b/src/userpassword.html @@ -4,7 +4,7 @@ diff --git a/src/userprofiles.html b/src/userprofiles.html index 022e06b95f..98237645dd 100644 --- a/src/userprofiles.html +++ b/src/userprofiles.html @@ -6,9 +6,9 @@

${HeaderUsers}

- ${Help} + ${Help}
diff --git a/src/videoosd.html b/src/videoosd.html index c200360c05..452c8a9af8 100644 --- a/src/videoosd.html +++ b/src/videoosd.html @@ -8,7 +8,7 @@

- autorenew + ${FetchingData}
@@ -25,47 +25,47 @@
@@ -76,7 +76,7 @@
diff --git a/src/wizardfinish.html b/src/wizardfinish.html index d3a0c4a831..4d54a10cd4 100644 --- a/src/wizardfinish.html +++ b/src/wizardfinish.html @@ -5,11 +5,11 @@

${WizardCompleted}

diff --git a/src/wizardlibrary.html b/src/wizardlibrary.html index 4fad656581..3a5ca5069b 100644 --- a/src/wizardlibrary.html +++ b/src/wizardlibrary.html @@ -3,7 +3,7 @@

${HeaderSetupLibrary}

- +

@@ -11,12 +11,12 @@
diff --git a/src/wizardremoteaccess.html b/src/wizardremoteaccess.html index c7ddfb7e84..0718c2dc37 100644 --- a/src/wizardremoteaccess.html +++ b/src/wizardremoteaccess.html @@ -13,7 +13,7 @@
${LabelEnableAutomaticPortMapHelp}
@@ -21,12 +21,12 @@
diff --git a/src/wizardsettings.html b/src/wizardsettings.html index f753919478..d4f537cf98 100644 --- a/src/wizardsettings.html +++ b/src/wizardsettings.html @@ -16,12 +16,12 @@
diff --git a/src/wizardstart.html b/src/wizardstart.html index 615feb3b04..05e282bee3 100644 --- a/src/wizardstart.html +++ b/src/wizardstart.html @@ -4,7 +4,7 @@

${WelcomeToProject}

- + ${ButtonQuickStartGuide}
@@ -20,7 +20,7 @@
diff --git a/src/wizarduser.html b/src/wizarduser.html index 90f492b0fe..3ce0b3ba74 100644 --- a/src/wizarduser.html +++ b/src/wizarduser.html @@ -22,12 +22,12 @@
diff --git a/webpack.common.js b/webpack.common.js index c87ccdbc0d..d870b1046e 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -1,38 +1,38 @@ -const path = require("path"); +const path = require('path'); -const { CleanWebpackPlugin } = require("clean-webpack-plugin"); -const CopyPlugin = require("copy-webpack-plugin"); +const CopyPlugin = require('copy-webpack-plugin'); const Assets = [ - "alameda/alameda.js", - "native-promise-only/npo.js", - "libass-wasm/dist/js/subtitles-octopus-worker.js", - "libass-wasm/dist/js/subtitles-octopus-worker.data", - "libass-wasm/dist/js/subtitles-octopus-worker.wasm", - "libass-wasm/dist/js/subtitles-octopus-worker-legacy.js", - "libass-wasm/dist/js/subtitles-octopus-worker-legacy.data", - "libass-wasm/dist/js/subtitles-octopus-worker-legacy.js.mem" + 'alameda/alameda.js', + 'native-promise-only/npo.js', + 'libass-wasm/dist/js/subtitles-octopus-worker.js', + 'libass-wasm/dist/js/subtitles-octopus-worker.data', + 'libass-wasm/dist/js/subtitles-octopus-worker.wasm', + 'libass-wasm/dist/js/subtitles-octopus-worker-legacy.js', + 'libass-wasm/dist/js/subtitles-octopus-worker-legacy.data', + 'libass-wasm/dist/js/subtitles-octopus-worker-legacy.js.mem' ]; module.exports = { - context: path.resolve(__dirname, "src"), - entry: "./bundle.js", + context: path.resolve(__dirname, 'src'), + entry: './bundle.js', + stats: 'errors-only', resolve: { modules: [ - path.resolve(__dirname, "node_modules") + path.resolve(__dirname, 'node_modules') ] }, output: { - filename: "bundle.js", - path: path.resolve(__dirname, "dist"), - libraryTarget: "amd-require" + filename: 'bundle.js', + path: path.resolve(__dirname, 'dist'), + libraryTarget: 'amd-require' }, plugins: [ new CopyPlugin( Assets.map(asset => { return { from: path.resolve(__dirname, `./node_modules/${asset}`), - to: path.resolve(__dirname, "./dist/libraries") + to: path.resolve(__dirname, './dist/libraries') }; }) ) diff --git a/webpack.dev.js b/webpack.dev.js index f39e027a17..17377acf1c 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -1,34 +1,56 @@ -const path = require("path"); -const common = require("./webpack.common"); -const merge = require("webpack-merge"); +const path = require('path'); +const common = require('./webpack.common'); +const merge = require('webpack-merge'); +const packageConfig = require('./package.json'); module.exports = merge(common, { - mode: "development", + mode: 'development', output: { - filename: "bundle.js", - path: path.resolve(__dirname, "dist"), - libraryTarget: "amd-require" + filename: 'bundle.js', + path: path.resolve(__dirname, 'dist'), + libraryTarget: 'amd-require' }, + devtool: 'inline-source-map', module: { rules: [ { test: /\.js$/, - exclude: /node_modules[\\/](?!query-string)/, - loader: "babel-loader" + exclude: /node_modules[\\/](?!date-fns|epubjs|jellyfin-apiclient|query-string|split-on-first|strict-uri-encode)/, + use: { + loader: 'babel-loader', + options: { + presets: packageConfig.babel.presets + } + } }, { test: /\.css$/i, - use: ["style-loader", "css-loader", "postcss-loader"] + use: [ + 'style-loader', + 'css-loader', + { + loader: 'postcss-loader', + options: { + config: { + path: __dirname + } + } + } + ] }, { test: /\.(png|jpg|gif)$/i, - use: ["file-loader"] + use: ['file-loader'] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: [ - 'file-loader', + 'file-loader' ] + }, + { + test: /\.(mp3)$/i, + use: ['file-loader'] } ] } diff --git a/webpack.prod.js b/webpack.prod.js index a6fb07091d..1b7f4d029e 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -1,29 +1,49 @@ -const path = require("path"); -const common = require("./webpack.common"); -const merge = require("webpack-merge"); +const common = require('./webpack.common'); +const merge = require('webpack-merge'); +const packageConfig = require('./package.json'); module.exports = merge(common, { - mode: "production", + mode: 'production', module: { rules: [ { test: /\.js$/, - exclude: /node_modules[\\/](?!query-string)/, - loader: "babel-loader" + exclude: /node_modules[\\/](?!date-fns|epubjs|jellyfin-apiclient|query-string|split-on-first|strict-uri-encode)/, + use: { + loader: 'babel-loader', + options: { + presets: packageConfig.babel.presets + } + } }, { test: /\.css$/i, - use: ["style-loader", "css-loader", "postcss-loader"] + use: [ + 'style-loader', + 'css-loader', + { + loader: 'postcss-loader', + options: { + config: { + path: __dirname + } + } + } + ] }, { test: /\.(png|jpg|gif)$/i, - use: ["file-loader"] + use: ['file-loader'] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: [ - 'file-loader', + 'file-loader' ] + }, + { + test: /\.(mp3)$/i, + use: ['file-loader'] } ] } diff --git a/yarn.lock b/yarn.lock index 3c286a7d7e..0e2c7a0c06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,35 +2,35 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" + integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== dependencies: - "@babel/highlight" "^7.8.3" + "@babel/highlight" "^7.10.1" -"@babel/compat-data@^7.8.6", "@babel/compat-data@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.0.tgz#04815556fc90b0c174abd2c0c1bb966faa036a6c" - integrity sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g== +"@babel/compat-data@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db" + integrity sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw== dependencies: - browserslist "^4.9.1" + browserslist "^4.12.0" invariant "^2.2.4" semver "^5.5.0" -"@babel/core@>=7.2.2", "@babel/core@^7.8.6": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" - integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== +"@babel/core@>=7.2.2", "@babel/core@>=7.9.0", "@babel/core@^7.10.2": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a" + integrity sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.0" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helpers" "^7.9.0" - "@babel/parser" "^7.9.0" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" + "@babel/code-frame" "^7.10.1" + "@babel/generator" "^7.10.2" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helpers" "^7.10.1" + "@babel/parser" "^7.10.2" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.2" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" @@ -40,283 +40,312 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.9.0": - version "7.9.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.3.tgz#7c8b2956c6f68b3ab732bd16305916fbba521d94" - integrity sha512-RpxM252EYsz9qLUIq6F7YJyK1sv0wWDBFuztfDGWaQKzHjqDHysxSiRUpA/X9jmfqo+WzkAVKFaUily5h+gDCQ== +"@babel/generator@^7.10.1", "@babel/generator@^7.10.2": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9" + integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA== dependencies: - "@babel/types" "^7.9.0" + "@babel/types" "^7.10.2" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" - integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== +"@babel/helper-annotate-as-pure@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz#f6d08acc6f70bbd59b436262553fb2e259a1a268" + integrity sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" - integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz#0ec7d9be8174934532661f87783eb18d72290059" + integrity sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw== dependencies: - "@babel/helper-explode-assignable-expression" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-explode-assignable-expression" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-compilation-targets@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz#dac1eea159c0e4bd46e309b5a1b04a66b53c1dde" - integrity sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw== +"@babel/helper-compilation-targets@^7.10.2": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz#a17d9723b6e2c750299d2a14d4637c76936d8285" + integrity sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA== dependencies: - "@babel/compat-data" "^7.8.6" - browserslist "^4.9.1" + "@babel/compat-data" "^7.10.1" + browserslist "^4.12.0" invariant "^2.2.4" levenary "^1.1.1" semver "^5.5.0" -"@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087" - integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg== +"@babel/helper-create-class-features-plugin@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.1.tgz#6d8a45aafe492378d0e6fc0b33e5dea132eae21c" + integrity sha512-bwhdehBJZt84HuPUcP1HaTLuc/EywVS8rc3FgsEPDcivg+DCW+SHuLHVkYOmcBA1ZfI+Z/oZjQc/+bPmIO7uAA== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-regex" "^7.8.3" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-member-expression-to-functions" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + +"@babel/helper-create-regexp-features-plugin@^7.10.1", "@babel/helper-create-regexp-features-plugin@^7.8.3": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz#1b8feeab1594cbcfbf3ab5a3bbcabac0468efdbd" + integrity sha512-Rx4rHS0pVuJn5pJOqaqcZR4XSgeF9G/pO/79t+4r7380tXFJdzImFnxMU19f83wjSrmKHq6myrM10pFHTGzkUA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-regex" "^7.10.1" regexpu-core "^4.7.0" -"@babel/helper-define-map@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" - integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== +"@babel/helper-define-map@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz#5e69ee8308648470dd7900d159c044c10285221d" + integrity sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-function-name" "^7.10.1" + "@babel/types" "^7.10.1" lodash "^4.17.13" -"@babel/helper-explode-assignable-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" - integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== +"@babel/helper-explode-assignable-expression@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz#e9d76305ee1162ca467357ae25df94f179af2b7e" + integrity sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg== dependencies: - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" - integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA== +"@babel/helper-function-name@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4" + integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-get-function-arity" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-get-function-arity@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" - integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== +"@babel/helper-get-function-arity@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d" + integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-hoist-variables@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" - integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== +"@babel/helper-hoist-variables@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz#7e77c82e5dcae1ebf123174c385aaadbf787d077" + integrity sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-member-expression-to-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" - integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== +"@babel/helper-member-expression-to-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15" + integrity sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-module-imports@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" - integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== +"@babel/helper-module-imports@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876" + integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-module-transforms@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" - integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== +"@babel/helper-module-transforms@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622" + integrity sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-simple-access" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/template" "^7.8.6" - "@babel/types" "^7.9.0" + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-simple-access" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" lodash "^4.17.13" -"@babel/helper-optimise-call-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" - integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== +"@babel/helper-optimise-call-expression@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543" + integrity sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" - integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.1", "@babel/helper-plugin-utils@^7.8.0": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127" + integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA== -"@babel/helper-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" - integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== +"@babel/helper-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.1.tgz#021cf1a7ba99822f993222a001cc3fec83255b96" + integrity sha512-7isHr19RsIJWWLLFn21ubFt223PjQyg1HY7CZEMRr820HttHPpVvrsIN3bUOo44DEfFV4kBXO7Abbn9KTUZV7g== dependencies: lodash "^4.17.13" -"@babel/helper-remap-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" - integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== +"@babel/helper-remap-async-to-generator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz#bad6aaa4ff39ce8d4b82ccaae0bfe0f7dbb5f432" + integrity sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-wrap-function" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-wrap-function" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" - integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== +"@babel/helper-replace-supers@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" + integrity sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A== dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/helper-member-expression-to-functions" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-simple-access@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" - integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== +"@babel/helper-simple-access@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" + integrity sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw== dependencies: - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/template" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helper-split-export-declaration@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" - integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== +"@babel/helper-split-export-declaration@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f" + integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.1" -"@babel/helper-validator-identifier@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" - integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw== +"@babel/helper-validator-identifier@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" + integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== -"@babel/helper-wrap-function@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" - integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== +"@babel/helper-wrap-function@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz#956d1310d6696257a7afd47e4c42dfda5dfcedc9" + integrity sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-function-name" "^7.10.1" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/helpers@^7.9.0": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f" - integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== +"@babel/helpers@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973" + integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw== dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" + "@babel/template" "^7.10.1" + "@babel/traverse" "^7.10.1" + "@babel/types" "^7.10.1" -"@babel/highlight@^7.8.3": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" - integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== +"@babel/highlight@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" + integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== dependencies: - "@babel/helper-validator-identifier" "^7.9.0" + "@babel/helper-validator-identifier" "^7.10.1" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.8.6", "@babel/parser@^7.9.0": - version "7.9.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.3.tgz#043a5fc2ad8b7ea9facddc4e802a1f0f25da7255" - integrity sha512-E6SpIDJZ0cZAKoCNk+qSDd0ChfTnpiJN9FfNf3RZ20dzwA2vL2oq5IX1XTVT+4vDmRlta2nGk5HGMMskJAR+4A== +"@babel/parser@^7.10.1", "@babel/parser@^7.10.2": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" + integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== -"@babel/plugin-proposal-async-generator-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" - integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== +"@babel/plugin-proposal-async-generator-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz#6911af5ba2e615c4ff3c497fe2f47b35bf6d7e55" + integrity sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-remap-async-to-generator" "^7.10.1" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" - integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== +"@babel/plugin-proposal-class-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.1.tgz#046bc7f6550bb08d9bd1d4f060f5f5a4f1087e01" + integrity sha512-sqdGWgoXlnOdgMXU+9MbhzwFRgxVLeiGBqTrnuS7LC2IBU31wSsESbTUreT2O418obpfPdGUR2GbEufZF1bpqw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-proposal-dynamic-import@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz#e36979dc1dc3b73f6d6816fc4951da2363488ef0" + integrity sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-dynamic-import" "^7.8.0" -"@babel/plugin-proposal-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" - integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== +"@babel/plugin-proposal-json-strings@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz#b1e691ee24c651b5a5e32213222b2379734aff09" + integrity sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-json-strings" "^7.8.0" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" - integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz#02dca21673842ff2fe763ac253777f235e9bbf78" + integrity sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" - integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== +"@babel/plugin-proposal-numeric-separator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz#a9a38bc34f78bdfd981e791c27c6fdcec478c123" + integrity sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-numeric-separator" "^7.10.1" -"@babel/plugin-proposal-object-rest-spread@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz#a28993699fc13df165995362693962ba6b061d6f" - integrity sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow== +"@babel/plugin-proposal-object-rest-spread@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz#cba44908ac9f142650b4a65b8aa06bf3478d5fb6" + integrity sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.10.1" -"@babel/plugin-proposal-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" - integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== +"@babel/plugin-proposal-optional-catch-binding@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz#c9f86d99305f9fa531b568ff5ab8c964b8b223d2" + integrity sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" - integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== +"@babel/plugin-proposal-optional-chaining@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz#15f5d6d22708629451a91be28f8facc55b0e818c" + integrity sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d" - integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A== +"@babel/plugin-proposal-private-methods@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz#ed85e8058ab0fe309c3f448e5e1b73ca89cdb598" + integrity sha512-RZecFFJjDiQ2z6maFprLgrdnm0OzoC23Mx89xf1CcEsxmHuzuXOdniEuI+S3v7vjQG4F5sa6YtUp+19sZuSxHg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.8" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-create-class-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-proposal-unicode-property-regex@^7.10.1", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f" + integrity sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-async-generators@^7.8.0": version "7.8.4" @@ -325,6 +354,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-class-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz#d5bc0645913df5b17ad7eda0fa2308330bde34c5" + integrity sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-dynamic-import@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" @@ -346,12 +382,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" - integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== +"@babel/plugin-syntax-numeric-separator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz#25761ee7410bc8cf97327ba741ee94e4a61b7d99" + integrity sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-object-rest-spread@^7.8.0": version "7.8.3" @@ -374,160 +410,160 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" - integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== +"@babel/plugin-syntax-top-level-await@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz#8b8733f8c57397b3eaa47ddba8841586dcaef362" + integrity sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-arrow-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" - integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== +"@babel/plugin-transform-arrow-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz#cb5ee3a36f0863c06ead0b409b4cc43a889b295b" + integrity sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" - integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== +"@babel/plugin-transform-async-to-generator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz#e5153eb1a3e028f79194ed8a7a4bf55f862b2062" + integrity sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg== dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-remap-async-to-generator" "^7.10.1" -"@babel/plugin-transform-block-scoped-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" - integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== +"@babel/plugin-transform-block-scoped-functions@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz#146856e756d54b20fff14b819456b3e01820b85d" + integrity sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-block-scoping@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" - integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== +"@babel/plugin-transform-block-scoping@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz#47092d89ca345811451cd0dc5d91605982705d5e" + integrity sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" lodash "^4.17.13" -"@babel/plugin-transform-classes@^7.9.0": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz#8603fc3cc449e31fdbdbc257f67717536a11af8d" - integrity sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ== +"@babel/plugin-transform-classes@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz#6e11dd6c4dfae70f540480a4702477ed766d733f" + integrity sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-define-map" "^7.8.3" - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-define-map" "^7.10.1" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-optimise-call-expression" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" - integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== +"@babel/plugin-transform-computed-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz#59aa399064429d64dce5cf76ef9b90b7245ebd07" + integrity sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-destructuring@^7.8.3": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz#fadb2bc8e90ccaf5658de6f8d4d22ff6272a2f4b" - integrity sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ== +"@babel/plugin-transform-destructuring@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz#abd58e51337815ca3a22a336b85f62b998e71907" + integrity sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" - integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== +"@babel/plugin-transform-dotall-regex@^7.10.1", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz#920b9fec2d78bb57ebb64a644d5c2ba67cc104ee" + integrity sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-duplicate-keys@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" - integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== +"@babel/plugin-transform-duplicate-keys@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz#c900a793beb096bc9d4d0a9d0cde19518ffc83b9" + integrity sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-exponentiation-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" - integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== +"@babel/plugin-transform-exponentiation-operator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz#279c3116756a60dd6e6f5e488ba7957db9c59eb3" + integrity sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-for-of@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" - integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== +"@babel/plugin-transform-for-of@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz#ff01119784eb0ee32258e8646157ba2501fcfda5" + integrity sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" - integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== +"@babel/plugin-transform-function-name@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz#4ed46fd6e1d8fde2a2ec7b03c66d853d2c92427d" + integrity sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" - integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== +"@babel/plugin-transform-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz#5794f8da82846b22e4e6631ea1658bce708eb46a" + integrity sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-member-expression-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" - integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== +"@babel/plugin-transform-member-expression-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz#90347cba31bca6f394b3f7bd95d2bbfd9fce2f39" + integrity sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-modules-amd@^7.8.3", "@babel/plugin-transform-modules-amd@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz#19755ee721912cf5bb04c07d50280af3484efef4" - integrity sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q== +"@babel/plugin-transform-modules-amd@^7.10.1", "@babel/plugin-transform-modules-amd@^7.9.6": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz#65950e8e05797ebd2fe532b96e19fc5482a1d52a" + integrity sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz#e3e72f4cbc9b4a260e30be0ea59bdf5a39748940" - integrity sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g== +"@babel/plugin-transform-modules-commonjs@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz#d5ff4b4413ed97ffded99961056e1fb980fb9301" + integrity sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-simple-access" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-simple-access" "^7.10.1" + babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz#e9fd46a296fc91e009b64e07ddaa86d6f0edeb90" - integrity sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ== +"@babel/plugin-transform-modules-systemjs@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz#9962e4b0ac6aaf2e20431ada3d8ec72082cbffb6" + integrity sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA== dependencies: - "@babel/helper-hoist-variables" "^7.8.3" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-hoist-variables" "^7.10.1" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697" - integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== +"@babel/plugin-transform-modules-umd@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz#ea080911ffc6eb21840a5197a39ede4ee67b1595" + integrity sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA== dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-module-transforms" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": version "7.8.3" @@ -536,164 +572,175 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.8.3" -"@babel/plugin-transform-new-target@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" - integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== +"@babel/plugin-transform-new-target@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz#6ee41a5e648da7632e22b6fb54012e87f612f324" + integrity sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-object-super@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" - integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== +"@babel/plugin-transform-object-super@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz#2e3016b0adbf262983bf0d5121d676a5ed9c4fde" + integrity sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-replace-supers" "^7.10.1" -"@babel/plugin-transform-parameters@^7.8.7": - version "7.9.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.3.tgz#3028d0cc20ddc733166c6e9c8534559cee09f54a" - integrity sha512-fzrQFQhp7mIhOzmOtPiKffvCYQSK10NR8t6BBz2yPbeUHb9OLW8RZGtgDRBn8z2hGcwvKDL3vC7ojPTLNxmqEg== +"@babel/plugin-transform-parameters@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz#b25938a3c5fae0354144a720b07b32766f683ddd" + integrity sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-get-function-arity" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-property-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" - integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== +"@babel/plugin-transform-property-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz#cffc7315219230ed81dc53e4625bf86815b6050d" + integrity sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-regenerator@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8" - integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== +"@babel/plugin-transform-regenerator@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz#10e175cbe7bdb63cc9b39f9b3f823c5c7c5c5490" + integrity sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw== dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" - integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== +"@babel/plugin-transform-reserved-words@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz#0fc1027312b4d1c3276a57890c8ae3bcc0b64a86" + integrity sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-shorthand-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" - integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== +"@babel/plugin-transform-shorthand-properties@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz#e8b54f238a1ccbae482c4dce946180ae7b3143f3" + integrity sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" - integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== +"@babel/plugin-transform-spread@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz#0c6d618a0c4461a274418460a28c9ccf5239a7c8" + integrity sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-sticky-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" - integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== +"@babel/plugin-transform-sticky-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz#90fc89b7526228bed9842cff3588270a7a393b00" + integrity sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-regex" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-regex" "^7.10.1" -"@babel/plugin-transform-template-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" - integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== +"@babel/plugin-transform-template-literals@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz#914c7b7f4752c570ea00553b4284dad8070e8628" + integrity sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-annotate-as-pure" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-typeof-symbol@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" - integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== +"@babel/plugin-transform-typeof-symbol@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz#60c0239b69965d166b80a84de7315c1bc7e0bb0e" + integrity sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-unicode-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" - integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== +"@babel/plugin-transform-unicode-escapes@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.1.tgz#add0f8483dab60570d9e03cecef6c023aa8c9940" + integrity sha512-zZ0Poh/yy1d4jeDWpx/mNwbKJVwUYJX73q+gyh4bwtG0/iUlzdEu0sLMda8yuDFS6LBQlT/ST1SJAR6zYwXWgw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.1" + +"@babel/plugin-transform-unicode-regex@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz#6b58f2aea7b68df37ac5025d9c88752443a6b43f" + integrity sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" "@babel/polyfill@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.8.7.tgz#151ec24c7135481336168c3bd8b8bf0cf91c032f" - integrity sha512-LeSfP9bNZH2UOZgcGcZ0PIHUt1ZuHub1L3CVmEyqLxCeDLm4C5Gi8jRH8ZX2PNpDhQCo0z6y/+DIs2JlliXW8w== + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.10.1.tgz#d56d4c8be8dd6ec4dce2649474e9b707089f739f" + integrity sha512-TviueJ4PBW5p48ra8IMtLXVkDucrlOZAIZ+EXqS3Ot4eukHbWiqcn7DcqpA1k5PcKtmJ4Xl9xwdv6yQvvcA+3g== dependencies: core-js "^2.6.5" regenerator-runtime "^0.13.4" -"@babel/preset-env@^7.8.6": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.0.tgz#a5fc42480e950ae8f5d9f8f2bbc03f52722df3a8" - integrity sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ== +"@babel/preset-env@^7.10.2": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.10.2.tgz#715930f2cf8573b0928005ee562bed52fb65fdfb" + integrity sha512-MjqhX0RZaEgK/KueRzh+3yPSk30oqDKJ5HP5tqTSB1e2gzGS3PLy7K0BIpnp78+0anFuSwOeuCf1zZO7RzRvEA== dependencies: - "@babel/compat-data" "^7.9.0" - "@babel/helper-compilation-targets" "^7.8.7" - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-proposal-async-generator-functions" "^7.8.3" - "@babel/plugin-proposal-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-json-strings" "^7.8.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-numeric-separator" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.9.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining" "^7.9.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" + "@babel/compat-data" "^7.10.1" + "@babel/helper-compilation-targets" "^7.10.2" + "@babel/helper-module-imports" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-proposal-async-generator-functions" "^7.10.1" + "@babel/plugin-proposal-class-properties" "^7.10.1" + "@babel/plugin-proposal-dynamic-import" "^7.10.1" + "@babel/plugin-proposal-json-strings" "^7.10.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.1" + "@babel/plugin-proposal-numeric-separator" "^7.10.1" + "@babel/plugin-proposal-object-rest-spread" "^7.10.1" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.1" + "@babel/plugin-proposal-optional-chaining" "^7.10.1" + "@babel/plugin-proposal-private-methods" "^7.10.1" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.1" "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.1" "@babel/plugin-syntax-dynamic-import" "^7.8.0" "@babel/plugin-syntax-json-strings" "^7.8.0" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.1" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.8.3" - "@babel/plugin-transform-async-to-generator" "^7.8.3" - "@babel/plugin-transform-block-scoped-functions" "^7.8.3" - "@babel/plugin-transform-block-scoping" "^7.8.3" - "@babel/plugin-transform-classes" "^7.9.0" - "@babel/plugin-transform-computed-properties" "^7.8.3" - "@babel/plugin-transform-destructuring" "^7.8.3" - "@babel/plugin-transform-dotall-regex" "^7.8.3" - "@babel/plugin-transform-duplicate-keys" "^7.8.3" - "@babel/plugin-transform-exponentiation-operator" "^7.8.3" - "@babel/plugin-transform-for-of" "^7.9.0" - "@babel/plugin-transform-function-name" "^7.8.3" - "@babel/plugin-transform-literals" "^7.8.3" - "@babel/plugin-transform-member-expression-literals" "^7.8.3" - "@babel/plugin-transform-modules-amd" "^7.9.0" - "@babel/plugin-transform-modules-commonjs" "^7.9.0" - "@babel/plugin-transform-modules-systemjs" "^7.9.0" - "@babel/plugin-transform-modules-umd" "^7.9.0" + "@babel/plugin-syntax-top-level-await" "^7.10.1" + "@babel/plugin-transform-arrow-functions" "^7.10.1" + "@babel/plugin-transform-async-to-generator" "^7.10.1" + "@babel/plugin-transform-block-scoped-functions" "^7.10.1" + "@babel/plugin-transform-block-scoping" "^7.10.1" + "@babel/plugin-transform-classes" "^7.10.1" + "@babel/plugin-transform-computed-properties" "^7.10.1" + "@babel/plugin-transform-destructuring" "^7.10.1" + "@babel/plugin-transform-dotall-regex" "^7.10.1" + "@babel/plugin-transform-duplicate-keys" "^7.10.1" + "@babel/plugin-transform-exponentiation-operator" "^7.10.1" + "@babel/plugin-transform-for-of" "^7.10.1" + "@babel/plugin-transform-function-name" "^7.10.1" + "@babel/plugin-transform-literals" "^7.10.1" + "@babel/plugin-transform-member-expression-literals" "^7.10.1" + "@babel/plugin-transform-modules-amd" "^7.10.1" + "@babel/plugin-transform-modules-commonjs" "^7.10.1" + "@babel/plugin-transform-modules-systemjs" "^7.10.1" + "@babel/plugin-transform-modules-umd" "^7.10.1" "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.8.3" - "@babel/plugin-transform-object-super" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.8.7" - "@babel/plugin-transform-property-literals" "^7.8.3" - "@babel/plugin-transform-regenerator" "^7.8.7" - "@babel/plugin-transform-reserved-words" "^7.8.3" - "@babel/plugin-transform-shorthand-properties" "^7.8.3" - "@babel/plugin-transform-spread" "^7.8.3" - "@babel/plugin-transform-sticky-regex" "^7.8.3" - "@babel/plugin-transform-template-literals" "^7.8.3" - "@babel/plugin-transform-typeof-symbol" "^7.8.4" - "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/plugin-transform-new-target" "^7.10.1" + "@babel/plugin-transform-object-super" "^7.10.1" + "@babel/plugin-transform-parameters" "^7.10.1" + "@babel/plugin-transform-property-literals" "^7.10.1" + "@babel/plugin-transform-regenerator" "^7.10.1" + "@babel/plugin-transform-reserved-words" "^7.10.1" + "@babel/plugin-transform-shorthand-properties" "^7.10.1" + "@babel/plugin-transform-spread" "^7.10.1" + "@babel/plugin-transform-sticky-regex" "^7.10.1" + "@babel/plugin-transform-template-literals" "^7.10.1" + "@babel/plugin-transform-typeof-symbol" "^7.10.1" + "@babel/plugin-transform-unicode-escapes" "^7.10.1" + "@babel/plugin-transform-unicode-regex" "^7.10.1" "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.9.0" - browserslist "^4.9.1" + "@babel/types" "^7.10.2" + browserslist "^4.12.0" core-js-compat "^3.6.2" invariant "^2.2.2" levenary "^1.1.1" @@ -710,43 +757,50 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" - integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== +"@babel/runtime@^7.8.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.1.tgz#b6eb75cac279588d3100baecd1b9894ea2840822" + integrity sha512-nQbbCbQc9u/rpg1XCxoMYQTbSMVZjCDxErQ1ClCn9Pvcmv1lGads19ep0a2VsEiIJeHqjZley6EQGEC3Yo1xMA== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.8.3", "@babel/template@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== +"@babel/runtime@^7.9.2": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" + integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" + regenerator-runtime "^0.13.4" -"@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892" - integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w== +"@babel/template@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" + integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.0" - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.0" - "@babel/types" "^7.9.0" + "@babel/code-frame" "^7.10.1" + "@babel/parser" "^7.10.1" + "@babel/types" "^7.10.1" + +"@babel/traverse@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27" + integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ== + dependencies: + "@babel/code-frame" "^7.10.1" + "@babel/generator" "^7.10.1" + "@babel/helper-function-name" "^7.10.1" + "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/parser" "^7.10.1" + "@babel/types" "^7.10.1" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5" - integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng== +"@babel/types@^7.10.1", "@babel/types@^7.10.2", "@babel/types@^7.4.4": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" + integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng== dependencies: - "@babel/helper-validator-identifier" "^7.9.0" + "@babel/helper-validator-identifier" "^7.10.1" lodash "^4.17.13" to-fast-properties "^2.0.0" @@ -813,6 +867,21 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@stylelint/postcss-css-in-js@^0.37.1": + version "0.37.1" + resolved "https://registry.yarnpkg.com/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.1.tgz#41e5e7660f73d88227610e18c6ebb262d56ac125" + integrity sha512-UMf2Rni3JGKi3ZwYRGMYJ5ipOA5ENJSKMtYA/pE1ZLURwdh7B5+z2r73RmWvub+N0UuH1Lo+TGfCgYwPvqpXNw== + dependencies: + "@babel/core" ">=7.9.0" + +"@stylelint/postcss-markdown@^0.36.1": + version "0.36.1" + resolved "https://registry.yarnpkg.com/@stylelint/postcss-markdown/-/postcss-markdown-0.36.1.tgz#829b87e6c0f108014533d9d7b987dc9efb6632e8" + integrity sha512-iDxMBWk9nB2BPi1VFQ+Dc5+XpvODBHw2n3tYpaBZuEAFQlbtF9If0Qh5LTTwSi/XwdbJ2jt+0dis3i8omyggpw== + dependencies: + remark "^12.0.0" + unist-util-find-all-after "^3.0.1" + "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" @@ -837,6 +906,25 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/html-minifier-terser@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880" + integrity sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA== + +"@types/jszip@^3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@types/jszip/-/jszip-3.4.1.tgz#e7a4059486e494c949ef750933d009684227846f" + integrity sha512-TezXjmf3lj+zQ651r6hPqvSScqBLvyPI9FxdXBqpEwBijNGQ2NXpaFW/7joGzveYkKQUil7iiDHLo6LV71Pc0A== + dependencies: + jszip "*" + +"@types/localforage@0.0.34": + version "0.0.34" + resolved "https://registry.yarnpkg.com/@types/localforage/-/localforage-0.0.34.tgz#5e31c32dd8791ec4b9ff3ef47c9cb55b2d0d9438" + integrity sha1-XjHDLdh5HsS5/z70fJy1Wy0NlDg= + dependencies: + localforage "*" + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -848,9 +936,9 @@ integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= "@types/node@*": - version "13.9.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.3.tgz#6356df2647de9eac569f9a52eda3480fa9e70b4d" - integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA== + version "13.13.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.4.tgz#1581d6c16e3d4803eb079c87d4ac893ee7501c2c" + integrity sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -872,15 +960,15 @@ resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== -"@types/tapable@*": +"@types/tapable@*", "@types/tapable@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== "@types/uglify-js@*": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082" - integrity sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ== + version "3.9.0" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.9.0.tgz#4490a140ca82aa855ad68093829e7fd6ae94ea87" + integrity sha512-3ZcoyPYHVOCcLpnfZwD47KFLr8W/mpUcgjpf1M4Q78TMJIw7KMAHSjiCLJp1z3ZrBR9pTLbe191O0TldFK5zcw== dependencies: source-map "^0.6.1" @@ -914,10 +1002,10 @@ "@types/source-list-map" "*" source-map "^0.6.1" -"@types/webpack@^4.4.31": - version "4.41.8" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.8.tgz#d2244f5f612ee30230a5c8c4ae678bce90d27277" - integrity sha512-mh4litLHTlDG84TGCFv1pZldndI34vkrW9Mks++Zx4KET7DRMoCXUvLbTISiuF4++fMgNnhV9cc1nCXJQyBYbQ== +"@types/webpack@^4.41.8": + version "4.41.12" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.12.tgz#0386ee2a2814368e2f2397abb036c0bf173ff6c3" + integrity sha512-BpCtM4NnBen6W+KEhrL9jKuZCXVtiH6+0b6cxdvNt2EwU949Al334PjQSl2BeAyvAX9mgoNNG21wvjP3xZJJ5w== dependencies: "@types/anymatch" "*" "@types/node" "*" @@ -1086,7 +1174,7 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: +accepts@~1.3.4: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== @@ -1104,7 +1192,7 @@ acorn@5.X, acorn@^5.0.3: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== -acorn@^6.2.1: +acorn@^6.4.1: version "6.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== @@ -1138,9 +1226,9 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.5.5: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" - integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -1193,11 +1281,6 @@ ansi-gray@^0.1.1: dependencies: ansi-wrap "0.1.0" -ansi-html@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" - integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -1344,16 +1427,6 @@ array-find-index@^1.0.1: resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -array-flatten@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - array-includes@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" @@ -1471,6 +1544,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-metadata-inferer@^0.2.0-0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ast-metadata-inferer/-/ast-metadata-inferer-0.2.0.tgz#a470e5d1d7402b18c6f7a1f3d6900723cfa07392" + integrity sha512-6yPph2NeCHNxoI/ZmjklYaLOSZDAx+0L0+wsXnF56FxmjxvUlYZSWcj1KXtXO8IufruQTzVFOjg1+IzdDazSPg== + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -1518,13 +1596,6 @@ async@1.5.2: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= -async@^2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1535,18 +1606,18 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@^9.0.0, autoprefixer@^9.6.1, autoprefixer@^9.7.4: - version "9.7.5" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.5.tgz#8df10b9ff9b5814a8d411a5cfbab9c793c392376" - integrity sha512-URo6Zvt7VYifomeAfJlMFnYDhow1rk2bufwkbamPEAtQFcL11moLk4PnR7n9vlu7M+BkXAZkHFA0mIcY7tjQFg== +autoprefixer@^9.0.0, autoprefixer@^9.6.1, autoprefixer@^9.7.6, autoprefixer@^9.8.0: + version "9.8.0" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.0.tgz#68e2d2bef7ba4c3a65436f662d0a56a741e56511" + integrity sha512-D96ZiIHXbDmU02dBaemyAg53ez+6F5yZmapmgKcjm35yEe1uVDYI8hGW3VYoGRaG290ZFf91YxHrR518vC0u/A== dependencies: - browserslist "^4.11.0" - caniuse-lite "^1.0.30001036" + browserslist "^4.12.0" + caniuse-lite "^1.0.30001061" chalk "^2.4.2" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^7.0.27" - postcss-value-parser "^4.0.3" + postcss "^7.0.30" + postcss-value-parser "^4.1.0" aws-sign2@~0.7.0: version "0.7.0" @@ -1566,6 +1637,15 @@ axios@0.19.0: follow-redirects "1.5.10" is-buffer "^2.0.2" +babel-eslint@^11.0.0-beta.2: + version "11.0.0-beta.2" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-11.0.0-beta.2.tgz#de1f06795aa0d8cedcf6ac943e63056f5b4a7048" + integrity sha512-D2tunrOu04XloEdU2XVUminUu25FILlGruZmffqH5OSnLDhCheKNvUoM1ihrexdUvhizlix8bjqRnsss4V/UIQ== + dependencies: + eslint-scope "5.0.0" + eslint-visitor-keys "^1.1.0" + semver "^6.3.0" + babel-loader@^8.0.6: version "8.1.0" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" @@ -1577,10 +1657,10 @@ babel-loader@^8.0.6: pify "^4.0.1" schema-utils "^2.6.5" -babel-plugin-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== dependencies: object.assign "^4.1.0" @@ -1756,39 +1836,16 @@ bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +blurhash@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-1.1.3.tgz#dc325af7da836d07a0861d830bdd63694382483e" + integrity sha512-yUhPJvXexbqbyijCIE/T2NCXcj9iNPhWmOKbPTuR/cm7Q5snXYIfnVnz6m7MWOXxODMz/Cr3UcVkRdHiuDVRDw== + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= - dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" - dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" - boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -1955,15 +2012,15 @@ browserslist@^1.1.3: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" -browserslist@^4.0.0, browserslist@^4.11.0, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.9.1: - version "4.11.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.11.0.tgz#aef4357b10a8abda00f97aac7cd587b2082ba1ad" - integrity sha512-WqEC7Yr5wUH5sg6ruR++v2SGOQYpyUdYYd4tZoAq1F7y+QXoLoYGXVbxhtaIqWmAJjtNTRjVD3HuJc1OXTel2A== +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.6.4, browserslist@^4.8.5: + version "4.12.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" + integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== dependencies: - caniuse-lite "^1.0.30001035" - electron-to-chromium "^1.3.380" - node-releases "^1.1.52" - pkg-up "^3.1.0" + caniuse-lite "^1.0.30001043" + electron-to-chromium "^1.3.413" + node-releases "^1.1.53" + pkg-up "^2.0.0" bs-recipes@1.3.4: version "1.3.4" @@ -2008,11 +2065,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== - buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -2028,9 +2080,9 @@ buffer@^4.3.0: isarray "^1.0.0" buffer@^5.2.1: - version "5.5.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.5.0.tgz#9c3caa3d623c33dd1c7ef584b89b88bf9c9bc1ce" - integrity sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww== + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" @@ -2040,11 +2092,6 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= - bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -2166,10 +2213,10 @@ camelcase-keys@^4.0.0: map-obj "^2.0.0" quick-lru "^1.0.0" -camelcase-keys@^6.1.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.1.tgz#cd3e2d2d7db767aa3f247e4c2df93b4661008945" - integrity sha512-BPCNVH56RVIxQQIXskp5tLQXUNGQ6sXr7iCv1FHDt81xBOQ/1r6H8SPxf19InVP6DexWar4s87q9thfuk8X9HA== +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== dependencies: camelcase "^5.3.1" map-obj "^4.0.0" @@ -2195,6 +2242,11 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" + integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== + caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -2210,10 +2262,20 @@ caniuse-db@^1.0.30000639: resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001036.tgz#8761fb6cd423ef2d3f8d96a21d898932252dc477" integrity sha512-plRkihXQyiDaFUXC7x/jAIXXTKiiaWvfAagsruh/vmstnRQ+a2a95HyENxiTr5WrkPSvmFUIvsRUalVFyeh2/w== -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001036: - version "1.0.30001036" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001036.tgz#930ea5272010d8bf190d859159d757c0b398caf0" - integrity sha512-jU8CIFIj2oR7r4W+5AKcsvWNVIb6Q6OZE3UsrXrZBHFtreT4YgTeOJtTucp+zSedEpTi3L5wASSP0LYIE3if6w== +caniuse-db@^1.0.30001059: + version "1.0.30001068" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001068.tgz#79fa671a063f03485c663f4165252f039c312c36" + integrity sha512-FF4o1nUDSnYY8rPCldTJ1486rqcgSZasQtWIMvSC3WOllFJNvmwhuBcApuWC1CD2TKTRnIBXqM4d4lJsCdRJzQ== + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001061: + version "1.0.30001061" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001061.tgz#80ca87ef14eb543a7458e7fd2b5e2face3458c9f" + integrity sha512-SMICCeiNvMZnyXpuoO+ot7FHpMVPlrsR+HmfByj6nY4xYDHXLqMTbgH7ecEkDNXWkH1vaip+ZS0D7VTXwM1KYQ== + +caniuse-lite@^1.0.30001043: + version "1.0.30001066" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz#0a8a58a10108f2b9bf38e7b65c237b12fd9c5f04" + integrity sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw== caseless@~0.12.0: version "0.12.0" @@ -2235,15 +2297,6 @@ ccount@^1.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17" integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw== -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2255,6 +2308,15 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" @@ -2263,6 +2325,14 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" + integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + character-entities-html4@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" @@ -2288,7 +2358,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.4, chokidar@^2.1.8: +chokidar@^2.0.0, chokidar@^2.0.4, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -2353,14 +2423,6 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -clean-webpack-plugin@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz#a99d8ec34c1c628a4541567aa7b457446460c62b" - integrity sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A== - dependencies: - "@types/webpack" "^4.4.31" - del "^4.1.1" - cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -2369,9 +2431,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== cliui@^3.2.0: version "3.2.0" @@ -2382,15 +2444,6 @@ cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -2559,11 +2612,6 @@ commander@^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" - integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== - commander@~2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" @@ -2601,26 +2649,6 @@ component-inherit@0.0.3: resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2636,7 +2664,7 @@ concat-stream@^1.5.0, concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-with-sourcemaps@^1.0.0, concat-with-sourcemaps@^1.0.5: +concat-with-sourcemaps@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== @@ -2651,7 +2679,7 @@ config-chain@^1.1.11: ini "^1.3.4" proto-list "~1.2.1" -connect-history-api-fallback@^1, connect-history-api-fallback@^1.6.0: +connect-history-api-fallback@^1: version "1.6.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== @@ -2691,18 +2719,13 @@ contains-path@^0.1.0: resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= -content-disposition@0.5.3, content-disposition@^0.5.2: +content-disposition@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== dependencies: safe-buffer "5.1.2" -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - convert-source-map@1.X, convert-source-map@^1.5.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -2710,21 +2733,11 @@ convert-source-map@1.X, convert-source-map@^1.5.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -2769,11 +2782,11 @@ copy-webpack-plugin@^5.1.1: webpack-log "^2.0.0" core-js-compat@^3.6.2: - version "3.6.4" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17" - integrity sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA== + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" + integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== dependencies: - browserslist "^4.8.3" + browserslist "^4.8.5" semver "7.0.0" core-js@^2.6.5: @@ -2781,10 +2794,10 @@ core-js@^2.6.5: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== -core-js@^3.6.4: - version "3.6.4" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" - integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== +core-js@^3.6.5: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" + integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -2843,17 +2856,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" @@ -2871,6 +2873,17 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -2917,22 +2930,23 @@ css-has-pseudo@^0.10.0: postcss-selector-parser "^5.0.0-rc.4" css-loader@^3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.4.2.tgz#d3fdb3358b43f233b78501c5ed7b1c6da6133202" - integrity sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA== + version "3.5.3" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.5.3.tgz#95ac16468e1adcd95c844729e0bb167639eb0bcf" + integrity sha512-UEr9NH5Lmi7+dguAm+/JSPovNjYbm2k3TK58EiwQHzOHH5Jfq1Y+XoP2bQO6TMn7PptMd0opxxedAWcaSTRKHw== dependencies: camelcase "^5.3.1" cssesc "^3.0.0" icss-utils "^4.1.1" loader-utils "^1.2.3" normalize-path "^3.0.0" - postcss "^7.0.23" + postcss "^7.0.27" postcss-modules-extract-imports "^2.0.0" postcss-modules-local-by-default "^3.0.2" - postcss-modules-scope "^2.1.1" + postcss-modules-scope "^2.2.0" postcss-modules-values "^3.0.0" - postcss-value-parser "^4.0.2" - schema-utils "^2.6.0" + postcss-value-parser "^4.0.3" + schema-utils "^2.6.6" + semver "^6.3.0" css-prefers-color-scheme@^3.1.1: version "3.1.1" @@ -2974,6 +2988,14 @@ css-tree@1.0.0-alpha.37: mdn-data "2.0.4" source-map "^0.6.1" +css-tree@1.0.0-alpha.39: + version "1.0.0-alpha.39" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" + integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== + dependencies: + mdn-data "2.0.6" + source-map "^0.6.1" + css-what@2.1: version "2.1.3" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" @@ -3078,11 +3100,11 @@ cssnano@^4.1.10: postcss "^7.0.0" csso@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d" - integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg== + version "4.0.3" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903" + integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ== dependencies: - css-tree "1.0.0-alpha.37" + css-tree "1.0.0-alpha.39" currently-unhandled@^0.4.1: version "0.4.1" @@ -3111,10 +3133,10 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -date-fns@^2.11.1: - version "2.11.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.11.1.tgz#197b8be1bbf5c5e6fe8bea817f0fe111820e7a12" - integrity sha512-3RdUoinZ43URd2MJcquzBbDQo+J87cSzB8NkXdZiN5ia1UNyep0oCyitfiL88+R7clGTeq/RniXAc16gWyAu1w== +date-fns@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.14.0.tgz#359a87a265bb34ef2e38f93ecf63ac453f9bc7ba" + integrity sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw== dateformat@^2.0.0: version "2.2.0" @@ -3137,7 +3159,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@3.X, debug@^3.0.0, debug@^3.1.1, debug@^3.2.5: +debug@3.X: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3223,9 +3245,9 @@ decompress-unzip@^4.0.1: yauzl "^2.4.2" decompress@^4.0.0, decompress@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d" - integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50= + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" + integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== dependencies: decompress-tar "^4.0.0" decompress-tarbz2 "^4.0.0" @@ -3236,18 +3258,6 @@ decompress@^4.0.0, decompress@^4.2.0: pify "^2.3.0" strip-dirs "^2.0.0" -deep-equal@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== - dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" - deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -3260,14 +3270,6 @@ default-compare@^1.0.0: dependencies: kind-of "^5.0.2" -default-gateway@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" - integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== - dependencies: - execa "^1.0.0" - ip-regex "^2.1.0" - default-resolution@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" @@ -3302,19 +3304,6 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -del@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" - integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== - dependencies: - "@types/glob" "^7.1.1" - globby "^6.1.0" - is-path-cwd "^2.0.0" - is-path-in-cwd "^2.0.0" - p-map "^2.0.0" - pify "^4.0.1" - rimraf "^2.6.3" - del@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" @@ -3367,11 +3356,6 @@ detect-newline@2.X: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= -detect-node@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" - integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== - dev-ip@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0" @@ -3386,14 +3370,6 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -dir-glob@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" - integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== - dependencies: - arrify "^1.0.1" - path-type "^3.0.0" - dir-glob@^2.0.0, dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -3408,26 +3384,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= - -dns-packet@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== - dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" - -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= - dependencies: - buffer-indexof "^1.0.0" - doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -3465,12 +3421,12 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -dom7@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/dom7/-/dom7-2.1.3.tgz#a736f9c3bfbc4ca039a81cd095f97d1d7f3de19c" - integrity sha512-QTxHHDox+M6ZFz1zHPAHZKI3JOHY5iY4i9BK2uctlggxKQwRhO3q3HHFq1BKsT25Bm/ySSj70K6Wk/G4bs9rMQ== +dom7@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/dom7/-/dom7-2.1.5.tgz#a79411017800b31d8400070cdaebbfc92c1f6377" + integrity sha512-xnhwVgyOh3eD++/XGtH+5qBwYTgCm0aW91GFgPJ3XG+jlsRLyJivnbP0QmUBFhI+Oaz9FV0s7cxgXHezwOEBYA== dependencies: - ssr-window "^1.0.1" + ssr-window "^2.0.0" domain-browser@^1.1.1: version "1.2.0" @@ -3632,10 +3588,15 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.380: - version "1.3.382" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.382.tgz#cad02da655c33f7a3d6ca7525bd35c17e90f3a8f" - integrity sha512-gJfxOcgnBlXhfnUUObsq3n3ReU8CT6S8je97HndYRkKsNZMJJ38zO/pI5aqO7L3Myfq+E3pqPyKK/ynyLEQfBA== +electron-to-chromium@^1.2.7: + version "1.3.427" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.427.tgz#ea43d02908a8c71f47ebb46e09de5a3cf8236f04" + integrity sha512-/rG5G7Opcw68/Yrb4qYkz07h3bESVRJjUl4X/FrKLXzoUJleKm6D7K7rTTz8V5LUWnd+BbTOyxJX2XprRqHD8A== + +electron-to-chromium@^1.3.413: + version "1.3.453" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.453.tgz#758a8565a64b7889b27132a51d2abb8b135c9d01" + integrity sha512-IQbCfjJR0NDDn/+vojTlq7fPSREcALtF8M1n01gw7nQghCtfFYrJ2dfhsp8APr8bANoFC8vRTFVXMOGpT0eetw== elliptic@^6.0.0: version "6.5.2" @@ -3665,11 +3626,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= - emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" @@ -3705,9 +3661,9 @@ engine.io-client@~3.2.0: yeast "0.1.2" engine.io-client@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.0.tgz#82a642b42862a9b3f7a188f41776b2deab643700" - integrity sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA== + version "3.4.1" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.1.tgz#922ddb47eecdcb541136a93aeead24718fd05461" + integrity sha512-RJNmA+A9Js+8Aoq815xpGAsgWH1VoSYM//2VgIiu9lNOaHFfLpTjH4tOzktBpjIs5lvOfiNY1dwf+NuU6D38Mw== dependencies: component-emitter "1.2.1" component-inherit "0.0.3" @@ -3755,15 +3711,6 @@ engine.io@~3.2.0: engine.io-parser "~2.1.0" ws "~3.3.1" -enhanced-resolve@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" - integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.4.0" - tapable "^1.0.0" - enhanced-resolve@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" @@ -3783,6 +3730,23 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== +epubjs@^0.3.85: + version "0.3.87" + resolved "https://registry.yarnpkg.com/epubjs/-/epubjs-0.3.87.tgz#0a2a94e59777e04548deff49a1c713ccbf3378fc" + integrity sha512-UlzXj04JQaUJ4p6ux/glQcVC4ayBtnpHT7niw4ozGy8EOQTAr8+/z7UZEHUmqQj4yHIoPYC4qGXtmzNqImWx1A== + dependencies: + "@types/jszip" "^3.4.1" + "@types/localforage" "0.0.34" + event-emitter "^0.3.5" + jszip "^3.4.0" + localforage "^1.7.3" + lodash "^4.17.15" + marks-pane "^1.0.9" + path-webpack "0.0.3" + stream-browserify "^2.0.1" + url-polyfill "^1.1.9" + xmldom "^0.1.27" + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -3797,7 +3761,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: version "1.17.5" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== @@ -3895,10 +3859,23 @@ eslint-module-utils@^2.4.1: debug "^2.6.9" pkg-dir "^2.0.0" -eslint-plugin-eslint-comments@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.1.2.tgz#4ef6c488dbe06aa1627fea107b3e5d059fc8a395" - integrity sha512-QexaqrNeteFfRTad96W+Vi4Zj1KFbkHHNMMaHZEYcovKav6gdomyGzaxSDSL3GoIyUOo078wRAdYlu1caiauIQ== +eslint-plugin-compat@^3.5.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-3.7.0.tgz#03f1ebb350a3c7eb93b6f461e200048e6008594b" + integrity sha512-A3uzSYqUjNj6rMyaBuU3l8wSCadZjeZRZ7WF3eU9vUT0JItiqRysjmYELkHHCpH8l7wRprUu4MZPr37lFCw7iA== + dependencies: + ast-metadata-inferer "^0.2.0-0" + browserslist "^4.12.0" + caniuse-db "^1.0.30001059" + core-js "^3.6.5" + lodash.memoize "4.1.2" + mdn-browser-compat-data "^1.0.21" + semver "7.3.2" + +eslint-plugin-eslint-comments@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz#9e1cd7b4413526abb313933071d7aba05ca12ffa" + integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ== dependencies: escape-string-regexp "^1.0.5" ignore "^5.0.5" @@ -3926,18 +3903,18 @@ eslint-plugin-promise@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== +eslint-scope@5.0.0, eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" - integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" @@ -4012,11 +3989,11 @@ esprima@^4.0.0: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe" - integrity sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q== + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== dependencies: - estraverse "^5.0.0" + estraverse "^5.1.0" esrecurse@^4.1.0: version "4.2.1" @@ -4030,10 +4007,10 @@ estraverse@^4.1.0, estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22" - integrity sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A== +estraverse@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" + integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== esutils@^2.0.2: version "2.0.3" @@ -4063,23 +4040,11 @@ eventemitter3@3.1.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA== -eventemitter3@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" - integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== - events@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== -eventsource@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" - integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== - dependencies: - original "^1.0.0" - evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -4166,42 +4131,6 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - ext-list@^2.0.0: version "2.2.2" resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" @@ -4239,7 +4168,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@~3.0.2: +extend@3.0.2, extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -4292,7 +4221,7 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== -fast-glob@^2.0.2, fast-glob@^2.2.6: +fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== @@ -4326,27 +4255,18 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-text-encoding@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.2.tgz#ff1ad5677bde049e0f8656aa6083a7ef2c5836e2" + integrity sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw== + fastq@^1.6.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.1.tgz#4570c74f2ded173e71cf0beb08ac70bb85826791" - integrity sha512-mpIH5sKYueh3YyeJwqtVo8sORi0CgtmkVbK6kZStpQlZBYQuTzG2CZ7idSiJuA7bY0SFCWUc5WIs+oYumGCQNw== + version "1.7.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.7.0.tgz#fcd79a08c5bd7ec5b55cd3f5c4720db551929801" + integrity sha512-YOadQRnHd5q6PogvAR/x62BGituF2ufiEA6s8aavQANw5YKHERI4AREboX6KotzP8oX2klxYF2wcV/7bn1clfQ== dependencies: reusify "^1.0.4" -faye-websocket@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" - integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= - dependencies: - websocket-driver ">=0.5.1" - -faye-websocket@~0.11.1: - version "0.11.3" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" - integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== - dependencies: - websocket-driver ">=0.5.1" - fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -4355,9 +4275,9 @@ fd-slicer@~1.1.0: pend "~1.2.0" figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== figures@^1.3.5: version "1.7.0" @@ -4480,19 +4400,6 @@ finalhandler@1.1.0: statuses "~1.3.1" unpipe "~1.0.0" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -4539,16 +4446,6 @@ find-versions@^3.0.0: dependencies: semver-regex "^2.0.0" -findup-sync@3.0.0, findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - findup-sync@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" @@ -4559,6 +4456,16 @@ findup-sync@^2.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" +findup-sync@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" + integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== + dependencies: + detect-file "^1.0.0" + is-glob "^4.0.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + fined@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" @@ -4585,9 +4492,9 @@ flat-cache@^2.0.1: write "1.0.3" flatted@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" - integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== flatten@^1.0.2: version "1.0.3" @@ -4617,13 +4524,6 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" -follow-redirects@^1.0.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb" - integrity sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ== - dependencies: - debug "^3.0.0" - for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -4655,11 +4555,6 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= - fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -4803,10 +4698,10 @@ get-stdin@^6.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== -get-stdin@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" - integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== get-stream@3.0.0, get-stream@^3.0.0: version "3.0.0" @@ -4917,13 +4812,6 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, gl once "^1.3.0" path-is-absolute "^1.0.0" -global-modules@2.0.0, global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - global-modules@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" @@ -4933,6 +4821,13 @@ global-modules@^1.0.0: is-windows "^1.0.1" resolve-dir "^1.0.0" +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + global-prefix@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" @@ -4991,17 +4886,6 @@ globby@^11.0.0: merge2 "^1.3.0" slash "^3.0.0" -globby@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" - integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= - dependencies: - array-union "^1.0.1" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - globby@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" @@ -5014,19 +4898,6 @@ globby@^7.1.1: pify "^3.0.0" slash "^1.0.0" -globby@^8.0.1: - version "8.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" - integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== - dependencies: - array-union "^1.0.1" - dir-glob "2.0.0" - fast-glob "^2.0.2" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - globby@^9.0.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -5062,13 +4933,20 @@ glogg@^1.0.0: dependencies: sparkles "^1.0.0" -gonzales-pe@^4.2.3, gonzales-pe@^4.2.4: +gonzales-pe@^4.2.3: version "4.2.4" resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.2.4.tgz#356ae36a312c46fe0f1026dd6cb539039f8500d2" integrity sha512-v0Ts/8IsSbh9n1OJRnSfa7Nlxi4AkXIsWB6vPept8FDbL4bXn3FNuxjYtO/nmBGu7GDkL9MFeGebeSu6l55EPQ== dependencies: minimist "1.1.x" +gonzales-pe@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" + integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== + dependencies: + minimist "^1.2.5" + got@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" @@ -5113,9 +4991,9 @@ got@^8.3.1: url-to-options "^1.0.1" graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" - integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== "graceful-readlink@>= 1.0.0": version "1.0.1" @@ -5144,10 +5022,10 @@ gulp-babel@^8.0.0: through2 "^2.0.0" vinyl-sourcemaps-apply "^0.2.0" -gulp-cli@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.2.0.tgz#5533126eeb7fe415a7e3e84a297d334d5cf70ebc" - integrity sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA== +gulp-cli@^2.2.0, gulp-cli@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.2.1.tgz#376e427661b7996430a89d71c15df75defa3360a" + integrity sha512-yEMxrXqY8mJFlaauFQxNrCpzWJThu0sH1sqlToaTOT063Hub9s/Nt2C+GSLe6feQ/IMWrHvGOOsyES7CQc9O+A== dependencies: ansi-colors "^1.0.1" archy "^1.0.0" @@ -5253,12 +5131,12 @@ gulp-postcss@^8.0.0: vinyl-sourcemaps-apply "^0.2.1" gulp-sass@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gulp-sass/-/gulp-sass-4.0.2.tgz#cfb1e3eff2bd9852431c7ce87f43880807d8d505" - integrity sha512-q8psj4+aDrblJMMtRxihNBdovfzGrXJp1l4JU0Sz4b/Mhsi2DPrKFYCGDwjIWRENs04ELVHxdOJQ7Vs98OFohg== + version "4.1.0" + resolved "https://registry.yarnpkg.com/gulp-sass/-/gulp-sass-4.1.0.tgz#486d7443c32d42bf31a6b1573ebbdaa361de7427" + integrity sha512-xIiwp9nkBLcJDpmYHbEHdoWZv+j+WtYaKD6Zil/67F3nrAaZtWYN5mDwerdo7EvcdBenSAj7Xb2hx2DqURLGdA== dependencies: chalk "^2.3.0" - lodash.clonedeep "^4.3.2" + lodash "^4.17.11" node-sass "^4.8.3" plugin-error "^1.0.1" replace-ext "^1.0.0" @@ -5334,11 +5212,6 @@ gulplog@^1.0.0: dependencies: glogg "^1.0.0" -handle-thing@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" - integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -5352,7 +5225,7 @@ har-validator@~5.1.3: ajv "^6.5.5" har-schema "^2.0.0" -hard-rejection@^2.0.0: +hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== @@ -5459,12 +5332,13 @@ has@^1.0.0, has@^1.0.3: function-bind "^1.1.1" hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" - integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" @@ -5479,6 +5353,11 @@ he@1.2.x, he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +headroom.js@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/headroom.js/-/headroom.js-0.11.0.tgz#26547a932025e4243abf8ace001b4ce5e110ed20" + integrity sha512-yI4ciZRD1WH22wa5uJDg2kMtRvhJwUJWo2l41Eby0BoAD+lzXL98lf5jDFxP4Q5W3HmlrpfItSfmqc3jCtasbw== + hex-color-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" @@ -5513,20 +5392,10 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== -howler@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/howler/-/howler-2.1.3.tgz#07c88618f8767e879407a4d647fe2d6d5f15f121" - integrity sha512-PSGbOi1EYgw80C5UQbxtJM7TmzD+giJunIMBYyH3RVzHZx2fZLYBoes0SpVVHi/SFa1GoNtgXj/j6I7NOKYBxQ== - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" +howler@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/howler/-/howler-2.2.0.tgz#0e2c780997ae65ab9a1a186333031beac1c63c6b" + integrity sha512-sGPkrAQy7jh5mNDbkRNG0F82R2HFDYNsQXBcX4smXQT0y0F4UMsa/+jXaGwWvcrajWr2tDB7JUkH7G5qSnuIyQ== hsl-regex@^1.0.0: version "1.0.0" @@ -5543,15 +5412,10 @@ html-comment-regex@^1.1.0, html-comment-regex@^1.1.2: resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== -html-entities@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" - integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= - 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== + version "5.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#95d3df037f04835e9d1a09d1767c0e361a7de916" + integrity sha512-tiYE76O1zunboByeB/nFGwUEb263Z3nkNv6Lz2oLC1s6M36bLKfTrjQ+7ssVfaucVllE+N7hh/FbpbxvnIA+LQ== dependencies: camel-case "^4.1.1" clean-css "^4.2.3" @@ -5584,11 +5448,14 @@ 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@^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== +html-webpack-plugin@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.3.0.tgz#53bf8f6d696c4637d5b656d3d9863d89ce8174fd" + integrity sha512-C0fzKN8yQoVLTelcJxZfJCE+aAvQiY2VUf3UuKrR4a9k5UMWYOtpDLsaXwATbcVCnI05hUS7L9ULQHWLZhyi3w== dependencies: + "@types/html-minifier-terser" "^5.0.0" + "@types/tapable" "^1.0.5" + "@types/webpack" "^4.41.8" html-minifier-terser "^5.0.1" loader-utils "^1.2.3" lodash "^4.17.15" @@ -5613,23 +5480,7 @@ http-cache-semantics@3.8.1: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= - -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@1.7.3, http-errors@~1.7.2: +http-errors@1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== @@ -5650,21 +5501,6 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -"http-parser-js@>=0.4.0 <0.4.11": - version "0.4.10" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" - integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= - -http-proxy-middleware@0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" - integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== - dependencies: - http-proxy "^1.17.0" - is-glob "^4.0.0" - lodash "^4.17.11" - micromatch "^3.1.10" - http-proxy@1.15.2: version "1.15.2" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.15.2.tgz#642fdcaffe52d3448d2bda3b0079e9409064da31" @@ -5673,15 +5509,6 @@ http-proxy@1.15.2: eventemitter3 "1.x.x" requires-port "1.x.x" -http-proxy@^1.17.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" - integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -5783,6 +5610,11 @@ imagemin@^7.0.0: p-pipe "^3.0.0" replace-ext "^1.0.0" +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + immutable@^3: version "3.8.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" @@ -5828,14 +5660,6 @@ import-lazy@^4.0.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== -import-local@2.0.0, import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== - dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -5886,7 +5710,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -5925,19 +5749,16 @@ inquirer@^7.0.0: strip-ansi "^6.0.0" through "^2.3.6" -internal-ip@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" - integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== - dependencies: - default-gateway "^4.2.0" - ipaddr.js "^1.9.0" - -interpret@1.2.0, interpret@^1.1.0: +interpret@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== +intersection-observer@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.10.0.tgz#4d11d63c1ff67e21e62987be24d55218da1a1a69" + integrity sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ== + into-stream@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" @@ -5958,26 +5779,6 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - -ip@^1.1.0, ip@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= - -ipaddr.js@1.9.1, ipaddr.js@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - irregular-plurals@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766" @@ -5993,11 +5794,6 @@ is-absolute-url@^2.0.0: resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= -is-absolute-url@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" - integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== - is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" @@ -6038,11 +5834,6 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" -is-arguments@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" - integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -6106,7 +5897,7 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== -is-decimal@^1.0.0: +is-decimal@^1.0.0, is-decimal@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== @@ -6248,25 +6039,11 @@ is-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= -is-path-cwd@^2.0.0, is-path-cwd@^2.2.0: +is-path-cwd@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== -is-path-in-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" - integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== - dependencies: - is-path-inside "^2.1.0" - -is-path-inside@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" - integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== - dependencies: - path-is-inside "^1.0.2" - is-path-inside@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" @@ -6277,6 +6054,11 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= +is-plain-obj@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -6289,12 +6071,12 @@ is-png@^2.0.0: resolved "https://registry.yarnpkg.com/is-png/-/is-png-2.0.0.tgz#ee8cbc9e9b050425cedeeb4a6fb74a649b0a4a8d" integrity sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g== -is-promise@^2.1, is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= +is-promise@^2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== -is-regex@^1.0.4, is-regex@^1.0.5: +is-regex@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== @@ -6451,14 +6233,19 @@ isurl@^1.0.0-alpha5: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" -"jellyfin-noto@https://github.com/jellyfin/jellyfin-noto": - version "1.0.2" - resolved "https://github.com/jellyfin/jellyfin-noto#a441b179c833288fc372cadb408d32a76c5479f1" +jellyfin-apiclient@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/jellyfin-apiclient/-/jellyfin-apiclient-1.2.1.tgz#1da577f7e22c37be8ec23c139b9ddab2c36da5a2" + integrity sha512-5aNtUq7YsoDPZ0LL6cq55HDnSTVfECfw05hbPFxNsFlUogEiHwaoIz+ahWRO13OUFQJuiu8f3fy16glcGzrBIQ== -jquery@>=1.9.1, jquery@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2" - integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw== +"jellyfin-noto@https://github.com/jellyfin/jellyfin-noto": + version "1.0.3" + resolved "https://github.com/jellyfin/jellyfin-noto#b784602db063734c721a46563ae5d6577ec2b35d" + +jquery@>=1.9.1, jquery@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5" + integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg== js-base64@^2.1.8, js-base64@^2.1.9: version "2.5.2" @@ -6523,11 +6310,6 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json3@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" - integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== - json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -6536,9 +6318,9 @@ json5@^1.0.1: minimist "^1.2.0" json5@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e" - integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ== + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== dependencies: minimist "^1.2.5" @@ -6566,6 +6348,16 @@ jstree@^3.3.7: dependencies: jquery ">=1.9.1" +jszip@*, jszip@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350" + integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + set-immediate-shim "~1.0.1" + junk@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" @@ -6583,11 +6375,6 @@ keyv@3.0.0: dependencies: json-buffer "3.0.0" -killable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" - integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== - kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -6617,10 +6404,10 @@ known-css-properties@^0.11.0: resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.11.0.tgz#0da784f115ea77c76b81536d7052e90ee6c86a8a" integrity sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w== -known-css-properties@^0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.18.0.tgz#d6e00b56ee1d5b0d171fd86df1583cfb012c521f" - integrity sha512-69AgJ1rQa7VvUsd2kpvVq+VeObDuo3zrj0CzM5Slmf6yduQFAI2kXPDQJR2IE/u6MSAUOJrwSzjg5vlz8qcMiw== +known-css-properties@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.19.0.tgz#5d92b7fa16c72d971bda9b7fe295bdf61836ee5b" + integrity sha512-eYboRV94Vco725nKMlpkn3nV2+96p9c3gKXRsYqAJSswSENvBhN7n5L+uDhY58xQa0UukWsDMTGELzmD8Q+wTA== last-run@^1.1.0: version "1.1.1" @@ -6651,13 +6438,6 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - lead@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" @@ -6690,9 +6470,23 @@ 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#4.0.0-jf": +"libass-wasm@https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv": version "4.0.0" - resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#7e6b75dcab9f7dad12719983510d05242803707c" + resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#58e9a3f1a7f7883556ee002545f445a430120639" + +lie@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4= + dependencies: + immediate "~3.0.5" + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" liftoff@^3.1.0: version "3.1.0" @@ -6759,15 +6553,6 @@ loader-runner@^2.4.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== -loader-utils@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.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" @@ -6786,6 +6571,13 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" +localforage@*, localforage@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.7.3.tgz#0082b3ca9734679e1bd534995bdd3b24cf10f204" + integrity sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ== + dependencies: + lie "3.1.1" + localtunnel@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-1.9.2.tgz#0012fcabc29cf964c130a01858768aa2bb65b5af" @@ -6869,11 +6661,6 @@ lodash.clone@^4.3.2: resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y= -lodash.clonedeep@^4.3.2: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.escape@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" @@ -6905,7 +6692,7 @@ lodash.keys@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash.memoize@^4.1.2: +lodash.memoize@4.1.2, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= @@ -6982,12 +6769,12 @@ log-symbols@^2.0.0, log-symbols@^2.2.0: dependencies: chalk "^2.0.1" -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== dependencies: - chalk "^2.4.2" + chalk "^4.0.0" logalot@^2.0.0, logalot@^2.1.0: version "2.1.0" @@ -6997,11 +6784,6 @@ logalot@^2.0.0, logalot@^2.1.0: figures "^1.3.5" squeak "^1.0.0" -loglevel@^1.6.6: - version "1.6.7" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.7.tgz#b3e034233188c68b889f5b862415306f565e2c56" - integrity sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A== - longest-streak@^2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" @@ -7097,9 +6879,9 @@ make-dir@^2.0.0: semver "^5.6.0" make-dir@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" - integrity sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w== + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" @@ -7110,13 +6892,6 @@ make-iterator@^1.0.0: dependencies: kind-of "^6.0.2" -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -7154,6 +6929,18 @@ markdown-table@^1.1.0: resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== +markdown-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" + integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A== + dependencies: + repeat-string "^1.0.0" + +marks-pane@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/marks-pane/-/marks-pane-1.0.9.tgz#c0b5ab813384d8cd81faaeb3bbf3397dc809c1b3" + integrity sha512-Ahs4oeG90tbdPWwAJkAAoHg2lRR8lAs9mZXETNPO9hYg3AkjUJBKi1NQ4aaIQZVGrig7c/3NUV1jANl8rFTeMg== + matchdep@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" @@ -7190,24 +6977,29 @@ mdast-util-compact@^1.0.0: dependencies: unist-util-visit "^1.1.0" +mdast-util-compact@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz#cabc69a2f43103628326f35b1acf735d55c99490" + integrity sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA== + dependencies: + unist-util-visit "^2.0.0" + +mdn-browser-compat-data@^1.0.21: + version "1.0.23" + resolved "https://registry.yarnpkg.com/mdn-browser-compat-data/-/mdn-browser-compat-data-1.0.23.tgz#52e21d74e52d40bacf1cc3377755897ef1639033" + integrity sha512-qzabBf9lN1UG6Ju6am5j4bsy8PJSxlE8zQEyDXzKqD+nAQsAnA8apvbkgTSIA/ZpKgz/7qOtpJgtgGN00MEsIg== + dependencies: + extend "3.0.2" + mdn-data@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" +mdn-data@2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" + integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== memoizee@0.4.X: version "0.4.14" @@ -7223,7 +7015,7 @@ memoizee@0.4.X: next-tick "1" timers-ext "^0.1.5" -memory-fs@^0.4.0, memory-fs@^0.4.1: +memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= @@ -7270,27 +7062,24 @@ meow@^5.0.0: trim-newlines "^2.0.0" yargs-parser "^10.0.0" -meow@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-6.1.0.tgz#4ff4641818d3502afcddc631f94cb6971a581cb3" - integrity sha512-iIAoeI01v6pmSfObAAWFoITAA4GgiT45m4SmJgoxtZfvI0fyZwhV4d0lTwiUXvAKIPlma05Feb2Xngl52Mj5Cg== +meow@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-7.0.1.tgz#1ed4a0a50b3844b451369c48362eb0515f04c1dc" + integrity sha512-tBKIQqVrAHqwit0vfuFPY3LlzJYkEOFyKa3bPgxzNl6q/RtN8KQ+ALYEASYuFayzSAsjlhXj/JZ10rH85Q6TUw== dependencies: "@types/minimist" "^1.2.0" - camelcase-keys "^6.1.1" + arrify "^2.0.1" + camelcase "^6.0.0" + camelcase-keys "^6.2.2" decamelize-keys "^1.1.0" - hard-rejection "^2.0.0" - minimist-options "^4.0.1" + hard-rejection "^2.1.0" + minimist-options "^4.0.2" normalize-package-data "^2.5.0" - read-pkg-up "^7.0.0" + read-pkg-up "^7.0.1" redent "^3.0.0" trim-newlines "^3.0.0" - type-fest "^0.8.1" - yargs-parser "^18.1.1" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + type-fest "^0.13.1" + yargs-parser "^18.1.3" merge-stream@^2.0.0: version "2.0.0" @@ -7302,11 +7091,6 @@ merge2@^1.2.3, merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -7342,34 +7126,24 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.43.0, "mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== +mime-db@1.44.0, mime-db@^1.28.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== dependencies: - mime-db "1.43.0" + mime-db "1.44.0" mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.4.4: - version "2.4.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" - integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== - -mimic-fn@^2.0.0, mimic-fn@^2.1.0: +mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -7409,7 +7183,7 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" -minimist-options@^4.0.1: +minimist-options@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.0.2.tgz#29c4021373ded40d546186725e57761e4b1984a7" integrity sha512-seq4hpWkYSUh1y7NXxzucwAN9yVlBc3Upgdjz8vLCP97jG8kaOmzYrVH/m7tQ1NYD1wdtZbSLfdy4zFmRWuc/w== @@ -7457,9 +7231,9 @@ mixin-deep@^1.2.0: is-extendable "^1.0.1" "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1: - version "0.5.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" - integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" @@ -7489,29 +7263,11 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= - -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== - dependencies: - dns-packet "^1.3.1" - thunky "^1.0.2" - multipipe@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" @@ -7530,9 +7286,9 @@ mute-stream@0.0.8: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.12.1, nan@^2.13.2: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== nanomatch@^1.2.9: version "1.2.13" @@ -7601,11 +7357,6 @@ no-case@^3.0.3: 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" - integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== - node-gyp@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" @@ -7653,17 +7404,38 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-releases@^1.1.52: - version "1.1.52" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9" - integrity sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ== - dependencies: - semver "^6.3.0" +node-releases@^1.1.53: + version "1.1.57" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.57.tgz#f6754ce225fad0611e61228df3e09232e017ea19" + integrity sha512-ZQmnWS7adi61A9JsllJ2gdj2PauElcjnOwTp2O011iGzoakTxUsDGSe+6vD7wXbKdqhSFymC0OSx35aAMhrSdw== -node-sass@^4.13.1, node-sass@^4.8.3: - version "4.13.1" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.13.1.tgz#9db5689696bb2eec2c32b98bfea4c7a2e992d0a3" - integrity sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw== +node-sass@^4.13.1: + version "4.14.1" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.1.tgz#99c87ec2efb7047ed638fb4c9db7f3a42e2217b5" + integrity sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g== + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash "^4.17.15" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.13.2" + node-gyp "^3.8.0" + npmlog "^4.0.0" + request "^2.88.0" + sass-graph "2.2.5" + stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + +node-sass@^4.8.3: + version "4.14.0" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.0.tgz#a8e9d7720f8e15b4a1072719dcf04006f5648eeb" + integrity sha512-AxqU+DFpk0lEz95sI6jO0hU0Rwyw7BXVEv6o9OItoXLyeygPeaSpiV4rwQb10JiTghHaa0gZeD21sz+OsQluaw== dependencies: async-foreach "^0.1.3" chalk "^1.1.1" @@ -7819,11 +7591,6 @@ object-inspect@^1.7.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== -object-is@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" - integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== - object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -7902,11 +7669,6 @@ object.values@^1.1.0: function-bind "^1.1.1" has "^1.0.3" -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -7914,11 +7676,6 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -7945,13 +7702,6 @@ opn@5.3.0: dependencies: is-wsl "^1.1.0" -opn@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" - integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== - dependencies: - is-wsl "^1.1.0" - optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -7980,13 +7730,6 @@ ordered-read-streams@^1.0.0: dependencies: readable-stream "^2.0.1" -original@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" - integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== - dependencies: - url-parse "^1.4.3" - os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -8011,15 +7754,6 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-locale@^3.0.0, os-locale@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -8043,11 +7777,6 @@ p-cancelable@^0.4.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= - p-event@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-event/-/p-event-1.3.0.tgz#8e6b4f4f65c72bc5b6fe28b75eda874f96a4a085" @@ -8072,11 +7801,6 @@ p-is-promise@^1.1.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -8085,9 +7809,9 @@ p-limit@^1.1.0: p-try "^1.0.0" p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" - integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" @@ -8119,11 +7843,6 @@ p-map-series@^1.0.0: dependencies: p-reduce "^1.0.0" -p-map@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== - p-map@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" @@ -8141,13 +7860,6 @@ p-reduce@^1.0.0: resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= -p-retry@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" - integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== - dependencies: - retry "^0.12.0" - p-timeout@^1.1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" @@ -8172,14 +7884,14 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -page@^1.11.5: - version "1.11.5" - resolved "https://registry.yarnpkg.com/page/-/page-1.11.5.tgz#0cfc8608be337f26f4377f31df0787aef0ca1af7" - integrity sha512-0JXUHc7Y8p1cPJQbhZSwaKO3p+bU3Rgny+OM5gJMKHWHvJKan/fsE5RUzEjRQolv9DzPOSVWfSOHz0lLxK19eA== +page@^1.11.6: + version "1.11.6" + resolved "https://registry.yarnpkg.com/page/-/page-1.11.6.tgz#5ef4efc7073749b8085ccdaa0dcd7c9e0de12fe3" + integrity sha512-P6e2JfzkBrPeFCIPplLP7vDDiU84RUUZMrWdsH4ZBGJ8OosnwFkcUkBHp1DTIjuipLliw9yQn/ZJsXZvarsO+g== dependencies: path-to-regexp "~1.2.1" -pako@~1.0.5: +pako@~1.0.2, pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== @@ -8239,6 +7951,18 @@ parse-entities@^1.0.2, parse-entities@^1.1.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + parse-filepath@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" @@ -8297,7 +8021,7 @@ parseuri@0.0.5: dependencies: better-assert "~1.0.0" -parseurl@~1.3.2, parseurl@~1.3.3: +parseurl@~1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -8347,11 +8071,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -8374,11 +8093,6 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - path-to-regexp@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.2.1.tgz#b33705c140234d873c8721c7b9fd8b541ed3aff9" @@ -8414,6 +8128,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path-webpack@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/path-webpack/-/path-webpack-0.0.3.tgz#ff6dec749eec5a94605c04d5f63fc55607a03a16" + integrity sha1-/23sdJ7sWpRgXATV9j/FVgegOhY= + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -8481,12 +8200,12 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pkg-up@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" - integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== +pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= dependencies: - find-up "^3.0.0" + find-up "^2.1.0" plugin-error@^1.0.1: version "1.0.1" @@ -8512,15 +8231,6 @@ plur@^3.0.1: dependencies: irregular-plurals "^2.0.0" -portfinder@^1.0.25: - version "1.0.25" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" - integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== - dependencies: - async "^2.6.2" - debug "^3.1.1" - mkdirp "^0.5.1" - portscanner@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/portscanner/-/portscanner-2.1.1.tgz#eabb409e4de24950f5a2a516d35ae769343fbb96" @@ -8738,7 +8448,7 @@ postcss-initial@^3.0.0: lodash.template "^4.5.0" postcss "^7.0.2" -postcss-jsx@^0.36.0, postcss-jsx@^0.36.4: +postcss-jsx@^0.36.0: version "0.36.4" resolved "https://registry.yarnpkg.com/postcss-jsx/-/postcss-jsx-0.36.4.tgz#37a68f300a39e5748d547f19a747b3257240bd50" integrity sha512-jwO/7qWUvYuWYnpOb0+4bIIgJt7003pgU3P6nETBLaOyBXuTD55ho21xnals5nBrlpTIFodyd3/jBi6UO3dHvA== @@ -8885,7 +8595,7 @@ postcss-modules-local-by-default@^3.0.2: postcss-selector-parser "^6.0.2" postcss-value-parser "^4.0.0" -postcss-modules-scope@^2.1.1: +postcss-modules-scope@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== @@ -9123,7 +8833,7 @@ postcss-resolve-nested-selector@^0.1.1: resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" integrity sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4= -postcss-safe-parser@^4.0.0, postcss-safe-parser@^4.0.1: +postcss-safe-parser@^4.0.0, postcss-safe-parser@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz#a6d4e48f0f37d9f7c11b2a581bf00f8ba4870b96" integrity sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g== @@ -9138,12 +8848,12 @@ postcss-sass@^0.3.5: gonzales-pe "^4.2.3" postcss "^7.0.1" -postcss-sass@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.4.2.tgz#7d1f8ddf6960d329de28fb3ff43c9c42013646bc" - integrity sha512-hcRgnd91OQ6Ot9R90PE/khUDCJHG8Uxxd3F7Y0+9VHjBiJgNv7sK5FxyHMCBtoLmmkzVbSj3M3OlqUfLJpq0CQ== +postcss-sass@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.4.4.tgz#91f0f3447b45ce373227a98b61f8d8f0785285a3" + integrity sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg== dependencies: - gonzales-pe "^4.2.4" + gonzales-pe "^4.3.0" postcss "^7.0.21" postcss-scss@^2.0.0: @@ -9250,10 +8960,10 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d" - integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg== +postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.0.3, postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: version "2.0.1" @@ -9274,10 +8984,10 @@ postcss@^5.0.0, postcss@^5.0.18: source-map "^0.5.6" supports-color "^3.2.3" -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.13, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.23, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.5, postcss@^7.0.6, postcss@^7.0.7: - version "7.0.27" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9" - integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ== +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.13, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.30, postcss@^7.0.5, postcss@^7.0.6, postcss@^7.0.7: + version "7.0.30" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.30.tgz#cc9378beffe46a02cbc4506a0477d05fcea9a8e2" + integrity sha512-nu/0m+NtIzoubO+xdAlwZl/u5S5vi/y6BCsoL8D+8IxsD3XvBS8X4YEADNIVXKVuQvduiucnRv+vPIqj56EGMQ== dependencies: chalk "^2.4.2" source-map "^0.6.1" @@ -9346,14 +9056,6 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.9.1" - prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -9365,9 +9067,9 @@ pseudomap@^1.0.2: integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= psl@^1.1.28: - version "1.7.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" - integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== public-encrypt@^4.0.0: version "4.0.3" @@ -9431,11 +9133,6 @@ qs@6.2.3: resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" integrity sha1-HPyyXBCpsrSDBT/zn138kjOQjP4= -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -9451,9 +9148,9 @@ query-string@^5.0.1: strict-uri-encode "^1.0.0" query-string@^6.11.1: - version "6.11.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.11.1.tgz#ab021f275d463ce1b61e88f0ce6988b3e8fe7c2c" - integrity sha512-1ZvJOUl8ifkkBxu2ByVM/8GijMIPx+cef7u3yroO3Ogm4DOdZcF5dcrWTIlSHe3Pg/mtlt6/eFjObDfJureZZA== + version "6.12.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.12.1.tgz#2ae4d272db4fba267141665374e49a1de09e8a7c" + integrity sha512-OHj+zzfRMyj3rmo/6G8a5Ifvw3AleL/EbcHMD27YA31Q+cO5lfmQxECkImuNVjcskLcvBRVHNAB3w6udMs1eAA== dependencies: decode-uri-component "^0.2.0" split-on-first "^1.0.0" @@ -9469,11 +9166,6 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -querystringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" - integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== - quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" @@ -9499,21 +9191,11 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@^1.2.1, range-parser@~1.2.0, range-parser@~1.2.1: +range-parser@~1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@^2.3.2: version "2.4.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" @@ -9555,7 +9237,7 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" -read-pkg-up@^7.0.0: +read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== @@ -9614,7 +9296,7 @@ read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@^3.0.6, readable-stream@^3.1.1: +"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -9706,14 +9388,6 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" - integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -9732,9 +9406,9 @@ regexpu-core@^4.7.0: unicode-match-property-value-ecmascript "^1.2.0" regjsgen@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" - integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== + version "0.5.2" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== regjsparser@^0.6.4: version "0.6.4" @@ -9769,6 +9443,28 @@ remark-parse@^6.0.0: vfile-location "^2.0.0" xtend "^4.0.1" +remark-parse@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.2.tgz#5999bc0b9c2e3edc038800a64ff103d0890b318b" + integrity sha512-eMI6kMRjsAGpMXXBAywJwiwAse+KNpmt+BK55Oofy4KvBZEqUDj6mWbGLJZrujoPIPPxDXzn3T9baRlpsm2jnQ== + dependencies: + ccount "^1.0.0" + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^2.0.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^2.0.0" + vfile-location "^3.0.0" + xtend "^4.0.1" + remark-stringify@^6.0.0: version "6.0.4" resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-6.0.4.tgz#16ac229d4d1593249018663c7bddf28aafc4e088" @@ -9789,6 +9485,26 @@ remark-stringify@^6.0.0: unherit "^1.0.4" xtend "^4.0.1" +remark-stringify@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-8.0.0.tgz#33423ab8bf3076fb197f4cf582aaaf866b531625" + integrity sha512-cABVYVloFH+2ZI5bdqzoOmemcz/ZuhQSH6W6ZNYnLojAUUn3xtX7u+6BpnYp35qHoGr2NFBsERV14t4vCIeW8w== + dependencies: + ccount "^1.0.0" + is-alphanumeric "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + longest-streak "^2.0.1" + markdown-escapes "^1.0.0" + markdown-table "^2.0.0" + mdast-util-compact "^2.0.0" + parse-entities "^2.0.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + stringify-entities "^3.0.0" + unherit "^1.0.4" + xtend "^4.0.1" + remark@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/remark/-/remark-10.0.1.tgz#3058076dc41781bf505d8978c291485fe47667df" @@ -9798,6 +9514,15 @@ remark@^10.0.1: remark-stringify "^6.0.0" unified "^7.0.0" +remark@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/remark/-/remark-12.0.0.tgz#d1c145c07341c9232f93b2f8539d56da15a2548c" + integrity sha512-oX4lMIS0csgk8AEbzY0h2jdR0ngiCHOpwwpxjmRa5TqAkeknY+tkhjRJGZqnCmvyuWh55/0SW5WY3R3nn3PH9A== + dependencies: + remark-parse "^8.0.0" + remark-stringify "^8.0.0" + unified "^9.0.0" + remove-bom-buffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" @@ -9836,7 +9561,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.5.4, repeat-string@^1.6.1: +repeat-string@^1.0.0, repeat-string@^1.5.4, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -9853,11 +9578,16 @@ replace-ext@0.0.1: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= -replace-ext@1.0.0, replace-ext@^1.0.0: +replace-ext@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= +replace-ext@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" + integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== + replace-homedir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" @@ -9908,7 +9638,7 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -requires-port@1.x.x, requires-port@^1.0.0: +requires-port@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= @@ -9918,13 +9648,6 @@ resize-observer-polyfill@^1.5.1: resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= - dependencies: - resolve-from "^3.0.0" - resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" @@ -9961,9 +9684,9 @@ resolve-url@^0.2.1: integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.3.2, resolve@^1.4.0: - version "1.15.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" - integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== dependencies: path-parse "^1.0.6" @@ -9995,11 +9718,6 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -10045,11 +9763,9 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: inherits "^2.0.1" run-async@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" - integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== - dependencies: - is-promise "^2.1.0" + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.9: version "1.1.9" @@ -10076,9 +9792,9 @@ rxjs@^5.5.6: symbol-observable "1.0.1" rxjs@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" - integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== + version "6.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" + integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== dependencies: tslib "^1.9.0" @@ -10087,7 +9803,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== @@ -10104,10 +9820,20 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sass-graph@2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.5.tgz#a981c87446b8319d96dce0671e487879bd24c2e8" + integrity sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag== + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + scss-tokenizer "^0.2.3" + yargs "^13.3.2" + sass-graph@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" - integrity sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k= + version "2.2.6" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.6.tgz#09fda0e4287480e3e4967b72a2d133ba09b8d827" + integrity sha512-MKuEYXFSGuRSi8FZ3A7imN1CeVn9Gpw0/SFJKdL1ejXJneI9a5rwlEZrKejhEFAA3O6yr3eIyl/WuvASvlT36g== dependencies: glob "^7.0.0" lodash "^4.0.0" @@ -10119,14 +9845,6 @@ sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -schema-utils@^0.4.5: - version "0.4.7" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" - integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ== - dependencies: - ajv "^6.1.0" - ajv-keywords "^3.1.0" - schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -10136,14 +9854,19 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -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== +schema-utils@^2.6.5, schema-utils@^2.6.6: + version "2.6.6" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.6.tgz#299fe6bd4a3365dc23d99fd446caff8f1d6c330c" + integrity sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA== dependencies: ajv "^6.12.0" ajv-keywords "^3.4.1" +screenfull@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.2.tgz#b9acdcf1ec676a948674df5cd0ff66b902b0bed7" + integrity sha512-cCF2b+L/mnEiORLN5xSAz6H3t18i2oHh9BA8+CQlAh5DRw2+NFAGQJOSYbcGw8B2k04g/lVvFcfZ83b3ysH5UQ== + scss-tokenizer@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" @@ -10159,18 +9882,6 @@ seek-bzip@^1.0.5: dependencies: commander "~2.8.1" -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= - -selfsigned@^1.10.7: - version "1.10.7" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" - integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== - dependencies: - node-forge "0.9.0" - semver-greatest-satisfied-range@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" @@ -10200,6 +9911,11 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== +semver@7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + semver@^6.0.0, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -10229,31 +9945,12 @@ send@0.16.2: range-parser "~1.2.0" statuses "~1.4.0" -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - serialize-javascript@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== -serve-index@1.9.1, serve-index@^1.9.1: +serve-index@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= @@ -10276,16 +9973,6 @@ serve-static@1.13.2: parseurl "~1.3.2" send "0.16.2" -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - server-destroy@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" @@ -10296,6 +9983,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-immediate-shim@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -10307,9 +9999,9 @@ set-value@^2.0.0, set-value@^2.0.1: split-string "^3.0.1" set-value@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-3.0.1.tgz#52c82af7653ba69eb1db92e81f5cdb32739b9e95" - integrity sha512-w6n3GUPYAWQj4ZyHWzD7K2FnFXHx9OTwJYbWg+6nXjG8sCLfs9DGv+KlqglKIIJx+ks7MlFuwFW2RBPb+8V+xg== + version "3.0.2" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-3.0.2.tgz#74e8ecd023c33d0f77199d415409a40f21e61b90" + integrity sha512-npjkVoz+ank0zjlV9F47Fdbjfj/PfXyVhZvGALWsyIYU/qrMzpi6avjKW3/7KeSU2Df3I46BrN1xOI1+6vW0hA== dependencies: is-plain-object "^2.0.4" @@ -10336,10 +10028,10 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -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== +shaka-player@^2.5.12: + version "2.5.12" + resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-2.5.12.tgz#4e8d9b2ab4147368b2a32f537ffed5e884301729" + integrity sha512-mnoMzE5iLnj6HYrovcnd55Lvd8bwV8712Inq3ECMJmBl2nYi7GXDRwYd3fLk6T0EfOsXwSELuHRQ1iyzk4NVNg== dependencies: eme-encryption-scheme-polyfill "^2.0.1" @@ -10356,9 +10048,9 @@ shebang-regex@^1.0.0: integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== simple-swizzle@^0.2.2: version "0.2.2" @@ -10496,26 +10188,6 @@ socket.io@2.1.1: socket.io-client "2.1.1" socket.io-parser "~3.2.0" -sockjs-client@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" - integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== - dependencies: - debug "^3.2.5" - eventsource "^1.0.7" - faye-websocket "~0.11.1" - inherits "^2.0.3" - json3 "^3.3.2" - url-parse "^1.4.3" - -sockjs@0.3.19: - version "0.3.19" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" - integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== - dependencies: - faye-websocket "^0.10.0" - uuid "^3.0.1" - sort-keys-length@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" @@ -10559,9 +10231,9 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: urix "^0.1.0" source-map-support@~0.5.12: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -10602,9 +10274,9 @@ spdx-correct@^3.0.0: spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" - integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.0" @@ -10619,29 +10291,6 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" - -spdy@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.1.tgz#6f12ed1c5db7ea4f24ebb8b89ba58c87c08257f2" - integrity sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA== - dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" - specificity@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019" @@ -10693,10 +10342,10 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssr-window@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ssr-window/-/ssr-window-1.0.1.tgz#30752a6a4666e7767f0b7e6aa6fc2fdbd0d9b369" - integrity sha512-dgFqB+f00LJTEgb6UXhx0h+SrG50LJvti2yMKMqAgzfUmUXZrLSv2fjULF7AWGwK25EXu8+smLR3jYsJQChPsg== +ssr-window@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ssr-window/-/ssr-window-2.0.0.tgz#98c301aef99523317f8d69618f0010791096efc4" + integrity sha512-NXzN+/HPObKAx191H3zKlYomE5WrVIkoCB5IaSdvKokxTpjBdWfr0RaP+1Z5KOfDT0ZVz+2tdtiBkhsEQ9p+0A== ssri@^6.0.1: version "6.0.1" @@ -10728,7 +10377,7 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -10829,7 +10478,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2": version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -10855,21 +10504,39 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.trimleft@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" - integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== +string.prototype.trimend@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" + +string.prototype.trimleft@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" + integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimstart "^1.0.0" string.prototype.trimright@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" - integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" + integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" + string.prototype.trimend "^1.0.0" + +string.prototype.trimstart@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" @@ -10900,6 +10567,17 @@ stringify-entities@^1.0.1: is-alphanumerical "^1.0.0" is-hexadecimal "^1.0.0" +stringify-entities@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-3.0.0.tgz#455abe501f8b7859ba5726a25a8872333c65b0a7" + integrity sha512-h7NJJIssprqlyjHT2eQt2W1F+MCcNmwPGlKb0bWEdET/3N44QN3QbUF/ueKCgAssyKRZ3Br9rQ7FcXjHr0qLHw== + dependencies: + character-entities-html4 "^1.0.0" + character-entities-legacy "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.2" + is-hexadecimal "^1.0.0" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -10977,9 +10655,9 @@ strip-indent@^3.0.0: min-indent "^1.0.0" strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" + integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== strip-outer@^1.0.0: version "1.0.1" @@ -10989,12 +10667,12 @@ strip-outer@^1.0.0: escape-string-regexp "^1.0.2" style-loader@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.1.3.tgz#9e826e69c683c4d9bf9db924f85e9abb30d5e200" - integrity sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw== + version "1.2.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" + integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg== dependencies: - loader-utils "^1.2.3" - schema-utils "^2.6.4" + loader-utils "^2.0.0" + schema-utils "^2.6.6" style-search@^0.1.0: version "0.1.0" @@ -11061,19 +10739,21 @@ stylelint-order@^4.0.0: postcss "^7.0.26" postcss-sorting "^5.0.1" -stylelint@^13.1.0: - version "13.2.1" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.2.1.tgz#9101fcd70791856530049816ff53d980ecd561df" - integrity sha512-461ZV4KpUe7pEHHgMOsH4kkjF7qsjkCIMJYOf7QQC4cvgPUJ0z4Nj+ah5fvKl1rzqBqc5EZa6P0nna4CGoJX+A== +stylelint@^13.5.0: + version "13.5.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.5.0.tgz#9edbf90c8c02c47fd0c4818376e3799145f22cab" + integrity sha512-+Jy7ieKAWKTf2tmcAE7jgScxH39Urb87i0bjK/enScFaGWWaFn4kAPwepGOSk2b7CLUDVt/O6kwA0x0p/V7moQ== dependencies: - autoprefixer "^9.7.4" + "@stylelint/postcss-css-in-js" "^0.37.1" + "@stylelint/postcss-markdown" "^0.36.1" + autoprefixer "^9.7.6" balanced-match "^1.0.0" - chalk "^3.0.0" + chalk "^4.0.0" cosmiconfig "^6.0.0" debug "^4.1.1" execall "^2.0.0" file-entry-cache "^5.0.1" - get-stdin "^7.0.0" + get-stdin "^8.0.0" global-modules "^2.0.0" globby "^11.0.0" globjoin "^0.1.4" @@ -11081,28 +10761,26 @@ stylelint@^13.1.0: ignore "^5.1.4" import-lazy "^4.0.0" imurmurhash "^0.1.4" - known-css-properties "^0.18.0" + known-css-properties "^0.19.0" leven "^3.1.0" lodash "^4.17.15" - log-symbols "^3.0.0" + log-symbols "^4.0.0" mathml-tag-names "^2.1.3" - meow "^6.0.1" + meow "^7.0.1" micromatch "^4.0.2" normalize-selector "^0.2.0" - postcss "^7.0.27" + postcss "^7.0.30" postcss-html "^0.36.0" - postcss-jsx "^0.36.4" postcss-less "^3.1.4" - postcss-markdown "^0.36.0" postcss-media-query-parser "^0.2.3" postcss-reporter "^6.0.1" postcss-resolve-nested-selector "^0.1.1" - postcss-safe-parser "^4.0.1" - postcss-sass "^0.4.2" + postcss-safe-parser "^4.0.2" + postcss-sass "^0.4.4" postcss-scss "^2.0.0" postcss-selector-parser "^6.0.2" postcss-syntax "^0.36.2" - postcss-value-parser "^4.0.3" + postcss-value-parser "^4.1.0" resolve-from "^5.0.0" slash "^3.0.0" specificity "^0.4.1" @@ -11175,13 +10853,6 @@ sugarss@^2.0.0: dependencies: postcss "^7.0.2" -supports-color@6.1.0, supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -11201,6 +10872,13 @@ supports-color@^5.3.0, supports-color@^5.5.0: dependencies: has-flag "^3.0.0" +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + supports-color@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" @@ -11240,13 +10918,13 @@ svgo@^1.0.0, svgo@^1.3.2: unquote "~1.1.1" util.promisify "~1.0.0" -swiper@^5.3.1: - version "5.3.6" - resolved "https://registry.yarnpkg.com/swiper/-/swiper-5.3.6.tgz#102b7f8145d734ec4c30e04602160382356b5948" - integrity sha512-FUz50g6RuvGAuXQWmR5lRPoA129leRUZ/p57ckr8+P5kR7VktElVQ47JGmWD86mOJCFfvMhUf0hinyC5UFL5iw== +swiper@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/swiper/-/swiper-5.4.1.tgz#6731e000e97f8b6560c11b141ebaf559063af565" + integrity sha512-l2EiWe7uOXB2EBMVLtJqn51FW22wF9e24WETT+S+tuFNvSDq1gadc/hyGGsAMqFGKJKIO6q6cqk7ToVaOI+onw== dependencies: - dom7 "^2.1.3" - ssr-window "^1.0.1" + dom7 "^2.1.5" + ssr-window "^2.0.0" symbol-observable@1.0.1: version "1.0.1" @@ -11329,9 +11007,9 @@ terser-webpack-plugin@^1.4.3: worker-farm "^1.7.0" 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== + version "4.6.13" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.13.tgz#e879a7364a5e0db52ba4891ecde007422c56a916" + integrity sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -11385,11 +11063,6 @@ through@^2.3.6, through@^2.3.8, through@~2.3.4: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - time-stamp@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" @@ -11585,6 +11258,11 @@ type-fest@^0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" @@ -11595,14 +11273,6 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - type@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" @@ -11630,14 +11300,6 @@ ua-parser-js@0.7.17: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" integrity sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g== -uglify-es@^3.3.9: - version "3.3.9" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" - integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== - dependencies: - commander "~2.13.0" - source-map "~0.6.1" - uglify-js@3.4.x: version "3.4.10" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" @@ -11652,9 +11314,9 @@ ultron@~1.1.0: integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== unbzip2-stream@^1.0.9: - version "1.3.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" - integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== + version "1.4.2" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.2.tgz#84eb9e783b186d8fb397515fbb656f312f1a7dbf" + integrity sha512-pZMVAofMrrHX6Ik39hCk470kulCbmZ2SWfQLPmTWqfJV/oUm0gn1CblvHdUu4+54Je6Jq34x8kY6XjTy6dMkOg== dependencies: buffer "^5.2.1" through "^2.3.8" @@ -11729,6 +11391,18 @@ unified@^7.0.0: vfile "^3.0.0" x-is-string "^0.1.0" +unified@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.0.0.tgz#12b099f97ee8b36792dbad13d278ee2f696eed1d" + integrity sha512-ssFo33gljU3PdlWLjNp15Inqb77d6JnJSfyplGJPT/a+fNRNyCBeveBAYJdO5khKdF6WVHa/yYCC7Xl6BDwZUQ== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -11786,11 +11460,23 @@ unist-util-find-all-after@^1.0.2: dependencies: unist-util-is "^3.0.0" +unist-util-find-all-after@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-3.0.1.tgz#95cc62f48812d879b4685a0512bf1b838da50e9a" + integrity sha512-0GICgc++sRJesLwEYDjFVJPJttBpVQaTNgc6Jw0Jhzvfs+jtKePEMu+uD+PqkRUrAvGQqwhpDwLGWo1PK8PDEw== + dependencies: + unist-util-is "^4.0.0" + unist-util-is@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== +unist-util-is@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.0.2.tgz#c7d1341188aa9ce5b3cff538958de9895f14a5de" + integrity sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ== + unist-util-remove-position@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz#ec037348b6102c897703eee6d0294ca4755a2020" @@ -11798,6 +11484,13 @@ unist-util-remove-position@^1.0.0: dependencies: unist-util-visit "^1.1.0" +unist-util-remove-position@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" + integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== + dependencies: + unist-util-visit "^2.0.0" + unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" @@ -11817,6 +11510,14 @@ unist-util-visit-parents@^2.0.0: dependencies: unist-util-is "^3.0.0" +unist-util-visit-parents@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.0.2.tgz#d4076af3011739c71d2ce99d05de37d545f4351d" + integrity sha512-yJEfuZtzFpQmg1OSCyS9M5NJRrln/9FbYosH3iW0MG402QbdbaB8ZESwUv9RO6nRfLAKvWcMxCwdLWOov36x/g== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" @@ -11824,6 +11525,15 @@ unist-util-visit@^1.1.0: dependencies: unist-util-visit-parents "^2.0.0" +unist-util-visit@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.2.tgz#3843782a517de3d2357b4c193b24af2d9366afb7" + integrity sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -11847,7 +11557,7 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -upath@^1.1.0, upath@^1.1.1: +upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== @@ -11883,13 +11593,10 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@^1.4.3: - version "1.4.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" +url-polyfill@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/url-polyfill/-/url-polyfill-1.1.9.tgz#2c8d4224889a5c942800f708f5585368085603d9" + integrity sha512-q/R5sowGuRfKHm497swkV+s9cPYtZRkHxzpDjRhqLO58FwdWTIkt6Y/fJlznUD/exaKx/XnDzCYXz0V16ND7ow== url-to-options@^1.0.1: version "1.0.1" @@ -11966,11 +11673,6 @@ uuid@^3.0.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-compile-cache@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" - integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== - v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" @@ -11996,11 +11698,6 @@ value-or-function@^3.0.0: resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - vendors@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" @@ -12020,6 +11717,11 @@ vfile-location@^2.0.0: resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.6.tgz#8a274f39411b8719ea5728802e10d9e0dff1519e" integrity sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA== +vfile-location@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.0.1.tgz#d78677c3546de0f7cd977544c367266764d31bb3" + integrity sha512-yYBO06eeN/Ki6Kh1QAkgzYpWT1d3Qln+ZCtSbJqFExPl1S3y2qqotJQXoh6qEvl/jDlgpUJolBn3PItVnnZRqQ== + vfile-message@*: version "2.0.3" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.3.tgz#0dd4f6879fb240a8099b22bd3755536c92e59ba5" @@ -12035,6 +11737,14 @@ vfile-message@^1.0.0: dependencies: unist-util-stringify-position "^1.1.1" +vfile-message@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" + integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + vfile@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/vfile/-/vfile-3.0.1.tgz#47331d2abe3282424f4a4bb6acd20a44c4121803" @@ -12045,6 +11755,17 @@ vfile@^3.0.0: unist-util-stringify-position "^1.0.0" vfile-message "^1.0.0" +vfile@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.1.0.tgz#d79248957f43225d57ff67a56effc67bef08946e" + integrity sha512-BaTPalregj++64xbGK6uIlsurN3BCRNM/P2Pg8HezlGzKd1O9PrwIac6bd9Pdx2uTb0QHoioZ+rXKolbVXEgJg== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + replace-ext "1.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + vinyl-fs@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" @@ -12114,105 +11835,20 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -watchpack@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" - integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== +watchpack@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.1.tgz#280da0a8718592174010c078c7585a74cd8cd0e2" + integrity sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA== dependencies: - chokidar "^2.0.2" + chokidar "^2.1.8" graceful-fs "^4.1.2" neo-async "^2.5.0" -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - webcomponents.js@^0.7.24: version "0.7.24" resolved "https://registry.yarnpkg.com/webcomponents.js/-/webcomponents.js-0.7.24.tgz#2116fbfa1468ec416a7befdaa333e1d118f69c04" integrity sha1-IRb7+hRo7EFqe+/aozPh0Rj2nAQ= -webpack-cli@^3.3.10: - version "3.3.11" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.11.tgz#3bf21889bf597b5d82c38f215135a411edfdc631" - integrity sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g== - dependencies: - chalk "2.4.2" - cross-spawn "6.0.5" - enhanced-resolve "4.1.0" - findup-sync "3.0.0" - global-modules "2.0.0" - import-local "2.0.0" - interpret "1.2.0" - loader-utils "1.2.3" - supports-color "6.1.0" - v8-compile-cache "2.0.3" - yargs "13.2.4" - -webpack-concat-plugin@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/webpack-concat-plugin/-/webpack-concat-plugin-3.0.0.tgz#db34ae230794b634061bc2944053ed407619f138" - integrity sha512-DLdDbZXyrFR99wyAVC9P06HLjr2XujBmQdSbnQMK2o01H9U2NHsN5W76jeTVeXDq5OLvZf8r/se65ftRo3Prow== - dependencies: - concat-with-sourcemaps "^1.0.5" - globby "^8.0.1" - schema-utils "^0.4.5" - uglify-es "^3.3.9" - upath "^1.1.0" - -webpack-dev-middleware@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" - integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== - dependencies: - memory-fs "^0.4.1" - mime "^2.4.4" - mkdirp "^0.5.1" - range-parser "^1.2.1" - webpack-log "^2.0.0" - -webpack-dev-server@^3.10.3: - version "3.10.3" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz#f35945036813e57ef582c2420ef7b470e14d3af0" - integrity sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ== - dependencies: - ansi-html "0.0.7" - bonjour "^3.5.0" - chokidar "^2.1.8" - compression "^1.7.4" - connect-history-api-fallback "^1.6.0" - debug "^4.1.1" - del "^4.1.1" - express "^4.17.1" - html-entities "^1.2.1" - http-proxy-middleware "0.19.1" - import-local "^2.0.0" - internal-ip "^4.3.0" - ip "^1.1.5" - is-absolute-url "^3.0.3" - killable "^1.0.1" - loglevel "^1.6.6" - opn "^5.5.0" - p-retry "^3.0.1" - portfinder "^1.0.25" - schema-utils "^1.0.0" - selfsigned "^1.10.7" - semver "^6.3.0" - serve-index "^1.9.1" - sockjs "0.3.19" - sockjs-client "1.4.0" - spdy "^4.0.1" - strip-ansi "^3.0.1" - supports-color "^6.1.0" - url "^0.11.0" - webpack-dev-middleware "^3.7.2" - webpack-log "^2.0.0" - ws "^6.2.1" - yargs "12.0.5" - webpack-log@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" @@ -12252,15 +11888,15 @@ webpack-stream@^5.2.1: webpack "^4.26.1" webpack@^4.26.1, webpack@^4.41.5: - version "4.42.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.42.1.tgz#ae707baf091f5ca3ef9c38b884287cfe8f1983ef" - integrity sha512-SGfYMigqEfdGchGhFFJ9KyRpQKnipvEvjc1TwrXEPCM6H5Wywu10ka8o3KGrMzSMxMQKt8aCHUFh5DaQ9UmyRg== + version "4.43.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6" + integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" "@webassemblyjs/wasm-edit" "1.9.0" "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.2.1" + acorn "^6.4.1" ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" @@ -12277,23 +11913,9 @@ webpack@^4.26.1, webpack@^4.41.5: schema-utils "^1.0.0" tapable "^1.1.3" terser-webpack-plugin "^1.4.3" - watchpack "^1.6.0" + watchpack "^1.6.1" webpack-sources "^1.4.1" -websocket-driver@>=0.5.1: - version "0.7.3" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" - integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg== - dependencies: - http-parser-js ">=0.4.0 <0.4.11" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" - integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== - webworkify@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/webworkify/-/webworkify-1.5.0.tgz#734ad87a774de6ebdd546e1d3e027da5b8f4a42c" @@ -12389,13 +12011,6 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" -ws@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== - dependencies: - async-limiter "~1.0.0" - ws@~3.3.1: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" @@ -12417,6 +12032,11 @@ x-is-string@^0.1.0: resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI= +xmldom@^0.1.27: + version "0.1.31" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" + integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + xmlhttprequest-ssl@~1.5.4: version "1.5.5" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" @@ -12432,7 +12052,7 @@ y18n@^3.2.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: +y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== @@ -12448,11 +12068,11 @@ yallist@^3.0.2: integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yaml@^1.7.2: - version "1.8.3" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.3.tgz#2f420fca58b68ce3a332d0ca64be1d191dd3f87a" - integrity sha512-X/v7VDnK+sxbQ2Imq4Jt2PRUsRsP7UcpSl3Llg6+NRRqWLIvxkMFYtH1FmvwNGYRKKPa+EPA4qDBlI9WVG1UKw== + version "1.9.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.9.2.tgz#f0cfa865f003ab707663e4f04b3956957ea564ed" + integrity sha512-HPT7cGGI0DuRcsO51qC1j9O16Dh1mZ2bnXwsi0jrSpsLz0WxOLSLXfkABVl6bZO629py3CU+OMJtpNHDLB97kg== dependencies: - "@babel/runtime" "^7.8.7" + "@babel/runtime" "^7.9.2" yargs-parser@^10.0.0: version "10.1.0" @@ -12461,15 +12081,7 @@ yargs-parser@^10.0.0: dependencies: camelcase "^4.1.0" -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" - integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^13.1.0: +yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== @@ -12477,10 +12089,10 @@ yargs-parser@^13.1.0: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.1.1: - version "18.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.1.tgz#bf7407b915427fc760fcbbccc6c82b4f0ffcbd37" - integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA== +yargs-parser@^18.1.3: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" @@ -12499,41 +12111,6 @@ yargs-parser@^5.0.0: dependencies: camelcase "^3.0.0" -yargs@12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" - integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== - dependencies: - cliui "^4.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" - -yargs@13.2.4: - version "13.2.4" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" - integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - os-locale "^3.1.0" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.0" - yargs@6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.4.0.tgz#816e1a866d5598ccf34e5596ddce22d92da490d4" @@ -12573,6 +12150,22 @@ yargs@6.6.0: y18n "^3.2.1" yargs-parser "^4.2.0" +yargs@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + yargs@^7.0.0, yargs@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
${ApiKeysCaption}
${HeaderApiKey}${HeaderApp}${HeaderDateIssued}${HeaderApiKey}${HeaderApp}${HeaderDateIssued}
'; - html += '"; - html += "'; - html += item.AccessToken; - html += "'; - html += item.AppName || ""; - html += "'; - var date = datetime.parseISO8601Date(item.DateCreated, true); - html += datetime.toLocaleDateString(date) + " " + datetime.getDisplayTime(date); - html += "
'; + html += ''; + html += ''; + html += item.AccessToken; + html += ''; + html += item.AppName || ''; + html += ''; + var date = datetime.parseISO8601Date(item.DateCreated, true); + html += datetime.toLocaleDateString(date) + ' ' + datetime.getDisplayTime(date); + html += '