diff --git a/.eslintrc.js b/.eslintrc.js index 53dd01ff25..91a4cf29c3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -46,6 +46,7 @@ module.exports = { 'keyword-spacing': ['error'], 'no-throw-literal': ['error'], 'max-statements-per-line': ['error'], + 'max-params': ['error', 7], 'no-duplicate-imports': ['error'], 'no-empty-function': ['error'], 'no-floating-decimal': ['error'], @@ -67,6 +68,7 @@ module.exports = { 'padded-blocks': ['error', 'never'], 'prefer-const': ['error', { 'destructuring': 'all' }], 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }], + 'radix': ['error'], '@babel/semi': ['error'], 'space-before-blocks': ['error'], 'space-infix-ops': 'error', diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d0d11b4c5f..9d4ab5b257 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -21,11 +21,11 @@ jobs: - name: Checkout repository uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Initialize CodeQL - uses: github/codeql-action/init@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2.2.5 + uses: github/codeql-action/init@16964e90ba004cdf0cd845b866b5df21038b7723 # v2.2.6 with: languages: ${{ matrix.language }} queries: +security-extended - name: Autobuild - uses: github/codeql-action/autobuild@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2.2.5 + uses: github/codeql-action/autobuild@16964e90ba004cdf0cd845b866b5df21038b7723 # v2.2.6 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2.2.5 + uses: github/codeql-action/analyze@16964e90ba004cdf0cd845b866b5df21038b7723 # v2.2.6 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index a61691021a..e377432eeb 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -60,6 +60,7 @@ - [edvwib](https://github.com/edvwib) - [Rob Farraher](https://github.com/farraherbg) - [TelepathicWalrus](https://github.com/TelepathicWalrus) + - [Pier-Luc Ducharme](https://github.com/pl-ducharme) # Emby Contributors diff --git a/deployment/Dockerfile.fedora b/deployment/Dockerfile.fedora index da3537327f..9248c209d7 100644 --- a/deployment/Dockerfile.fedora +++ b/deployment/Dockerfile.fedora @@ -1,4 +1,4 @@ -FROM fedora:37 +FROM fedora:38 # Docker build arguments ARG SOURCE_DIR=/jellyfin diff --git a/package-lock.json b/package-lock.json index bee1500884..dcc6fb05f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,10 +18,10 @@ "@jellyfin/libass-wasm": "4.1.1", "@jellyfin/sdk": "unstable", "@loadable/component": "5.15.3", - "blurhash": "2.0.4", + "blurhash": "2.0.5", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", "classnames": "2.3.2", - "core-js": "3.28.0", + "core-js": "3.29.0", "date-fns": "2.29.3", "dompurify": "2.4.4", "epubjs": "0.4.2", @@ -30,11 +30,11 @@ "flv.js": "1.6.2", "headroom.js": "0.12.0", "history": "5.3.0", - "hls.js": "0.14.17", + "hls.js": "1.3.4", "intersection-observer": "0.12.2", "jellyfin-apiclient": "1.10.0", "jquery": "3.6.3", - "jstree": "3.3.14", + "jstree": "3.3.15", "libarchive.js": "1.3.0", "lodash-es": "4.17.21", "marked": "4.2.12", @@ -43,7 +43,7 @@ "pdfjs-dist": "2.16.105", "react": "17.0.2", "react-dom": "17.0.2", - "react-router-dom": "6.8.1", + "react-router-dom": "6.8.2", "resize-observer-polyfill": "1.5.1", "screenfull": "6.0.2", "sortablejs": "1.15.0", @@ -54,7 +54,7 @@ "workbox-precaching": "6.5.4" }, "devDependencies": { - "@babel/core": "7.20.12", + "@babel/core": "7.21.0", "@babel/eslint-parser": "7.19.1", "@babel/eslint-plugin": "7.19.1", "@babel/plugin-proposal-class-properties": "7.18.6", @@ -62,14 +62,14 @@ "@babel/plugin-transform-modules-umd": "7.18.6", "@babel/preset-env": "7.20.2", "@babel/preset-react": "7.18.6", - "@babel/preset-typescript": "7.18.6", + "@babel/preset-typescript": "7.21.0", "@types/escape-html": "1.0.2", "@types/loadable__component": "5.13.4", "@types/lodash-es": "4.17.6", "@types/react": "17.0.53", "@types/react-dom": "17.0.19", - "@typescript-eslint/eslint-plugin": "5.52.0", - "@typescript-eslint/parser": "5.52.0", + "@typescript-eslint/eslint-plugin": "5.54.0", + "@typescript-eslint/parser": "5.54.0", "@uupaa/dynamic-import-polyfill": "1.0.2", "autoprefixer": "10.4.13", "babel-loader": "9.1.2", @@ -81,7 +81,7 @@ "css-loader": "6.7.3", "cssnano": "5.1.15", "es-check": "7.1.0", - "eslint": "8.34.0", + "eslint": "8.35.0", "eslint-plugin-compat": "4.1.2", "eslint-plugin-eslint-comments": "3.2.0", "eslint-plugin-import": "2.27.5", @@ -98,11 +98,11 @@ "postcss-loader": "7.0.2", "postcss-preset-env": "8.0.1", "postcss-scss": "4.0.6", - "sass": "1.58.1", + "sass": "1.58.3", "sass-loader": "13.2.0", "source-map-loader": "4.0.1", "style-loader": "3.3.1", - "stylelint": "14.16.1", + "stylelint": "15.2.0", "stylelint-config-rational-order": "0.1.2", "stylelint-no-browser-hacks": "1.2.1", "stylelint-order": "6.0.2", @@ -123,12 +123,26 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ampproject/remapping/node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" }, "engines": { "node": ">=6.0.0" @@ -182,21 +196,21 @@ } }, "node_modules/@babel/core": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", + "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.1.0", + "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", + "@babel/generator": "^7.21.0", "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.0", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.0", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -246,13 +260,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", "dev": true, "dependencies": { - "@babel/types": "^7.20.7", + "@babel/types": "^7.21.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -319,17 +334,18 @@ "dev": true }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", - "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", + "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/helper-split-export-declaration": "^7.18.6" }, "engines": { @@ -394,13 +410,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -419,12 +435,12 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", - "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", + "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", "dev": true, "dependencies": { - "@babel/types": "^7.20.7" + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -443,9 +459,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", - "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", @@ -454,8 +470,8 @@ "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.10", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" }, "engines": { "node": ">=6.9.0" @@ -530,12 +546,12 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -572,9 +588,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -596,14 +612,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dev": true, "dependencies": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -624,9 +640,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1124,12 +1140,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1679,14 +1695,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.3.tgz", - "integrity": "sha512-z6fnuK9ve9u/0X0rRvI9MY0xg+DOUaABDYOe+/SQTxtlptaBB/V9JIUxJn6xp3lMBeb9qe8xSFmHU35oZDXD+w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz", + "integrity": "sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-typescript": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -1852,14 +1868,14 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz", + "integrity": "sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-transform-typescript": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -1894,19 +1910,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", - "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1915,9 +1931,9 @@ } }, "node_modules/@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.19.4", @@ -2378,19 +2394,19 @@ } }, "node_modules/@csstools/selector-specificity": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", - "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", + "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", "dev": true, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2", + "postcss": "^8.4", "postcss-selector-parser": "^6.0.10" } }, @@ -2415,9 +2431,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", + "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -2517,6 +2533,15 @@ "node": ">=4" } }, + "node_modules/@eslint/js": { + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", + "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@fontsource/noto-sans": { "version": "4.5.11", "resolved": "https://registry.npmjs.org/@fontsource/noto-sans/-/noto-sans-4.5.11.tgz", @@ -2609,9 +2634,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "devOptional": true, "engines": { "node": ">=6.0.0" @@ -2637,19 +2662,19 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "devOptional": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "devOptional": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "node_modules/@leichtgewicht/ip-codec": { @@ -2742,9 +2767,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", - "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.3.tgz", + "integrity": "sha512-YRHie1yQEj0kqqCTCJEfHqYSSNlZQ696QJG+MMiW4mxSl9I0ojz/eRhJS4fs88Z5i6D1SmoF9d3K99/QOhI8/w==", "engines": { "node": ">=14" } @@ -3175,14 +3200,14 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz", - "integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.0.tgz", + "integrity": "sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.52.0", - "@typescript-eslint/type-utils": "5.52.0", - "@typescript-eslint/utils": "5.52.0", + "@typescript-eslint/scope-manager": "5.54.0", + "@typescript-eslint/type-utils": "5.54.0", + "@typescript-eslint/utils": "5.54.0", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -3224,14 +3249,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz", - "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.0.tgz", + "integrity": "sha512-aAVL3Mu2qTi+h/r04WI/5PfNWvO6pdhpeMRWk9R7rEV4mwJNzoWf5CCU5vDKBsPIFQFjEq1xg7XBI2rjiMXQbQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.52.0", - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/scope-manager": "5.54.0", + "@typescript-eslint/types": "5.54.0", + "@typescript-eslint/typescript-estree": "5.54.0", "debug": "^4.3.4" }, "engines": { @@ -3251,13 +3276,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", - "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.0.tgz", + "integrity": "sha512-VTPYNZ7vaWtYna9M4oD42zENOBrb+ZYyCNdFs949GcN8Miwn37b8b7eMj+EZaq7VK9fx0Jd+JhmkhjFhvnovhg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/visitor-keys": "5.52.0" + "@typescript-eslint/types": "5.54.0", + "@typescript-eslint/visitor-keys": "5.54.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3268,13 +3293,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz", - "integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.0.tgz", + "integrity": "sha512-WI+WMJ8+oS+LyflqsD4nlXMsVdzTMYTxl16myXPaCXnSgc7LWwMsjxQFZCK/rVmTZ3FN71Ct78ehO9bRC7erYQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.52.0", - "@typescript-eslint/utils": "5.52.0", + "@typescript-eslint/typescript-estree": "5.54.0", + "@typescript-eslint/utils": "5.54.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -3295,9 +3320,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", - "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.0.tgz", + "integrity": "sha512-nExy+fDCBEgqblasfeE3aQ3NuafBUxZxgxXcYfzYRZFHdVvk5q60KhCSkG0noHgHRo/xQ/BOzURLZAafFpTkmQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3308,13 +3333,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", - "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.0.tgz", + "integrity": "sha512-X2rJG97Wj/VRo5YxJ8Qx26Zqf0RRKsVHd4sav8NElhbZzhpBI8jU54i6hfo9eheumj4oO4dcRN1B/zIVEqR/MQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/visitor-keys": "5.52.0", + "@typescript-eslint/types": "5.54.0", + "@typescript-eslint/visitor-keys": "5.54.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -3379,16 +3404,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz", - "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.0.tgz", + "integrity": "sha512-cuwm8D/Z/7AuyAeJ+T0r4WZmlnlxQ8wt7C7fLpFlKMR+dY6QO79Cq1WpJhvZbMA4ZeZGHiRWnht7ZJ8qkdAunw==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.52.0", - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/scope-manager": "5.54.0", + "@typescript-eslint/types": "5.54.0", + "@typescript-eslint/typescript-estree": "5.54.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -3420,12 +3445,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", - "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.0.tgz", + "integrity": "sha512-xu4wT7aRCakGINTLGeyGqDn+78BwFlggwBjnHa1ar/KaGagnmwLYmlrXIrgAaQ3AE1Vd6nLfKASm7LrFHNbKGA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/types": "5.54.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -4278,9 +4303,9 @@ } }, "node_modules/blurhash": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/blurhash/-/blurhash-2.0.4.tgz", - "integrity": "sha512-r/As72u2FbucLoK5NTegM/GucxJc3d8GvHc4ngo13IO/nt2HU4gONxNLq1XPN6EM/V8Y9URIa7PcSz2RZu553A==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/blurhash/-/blurhash-2.0.5.tgz", + "integrity": "sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==" }, "node_modules/body-parser": { "version": "1.20.0", @@ -5141,9 +5166,9 @@ } }, "node_modules/core-js": { - "version": "3.28.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.28.0.tgz", - "integrity": "sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw==", + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.0.tgz", + "integrity": "sha512-VG23vuEisJNkGl6XQmFJd3rEG/so/CNatqeE+7uZAwTSwFeB/qaO0be8xZYUNWprJ/GIwL8aMt9cj1kvbpTZhg==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -6385,12 +6410,13 @@ } }, "node_modules/eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", - "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", + "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.4.1", + "@eslint/eslintrc": "^2.0.0", + "@eslint/js": "8.35.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -6404,7 +6430,7 @@ "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", "espree": "^9.4.0", - "esquery": "^1.4.0", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", @@ -7189,9 +7215,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -7275,7 +7301,8 @@ "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true }, "node_modules/events": { "version": "3.3.0", @@ -8441,13 +8468,9 @@ } }, "node_modules/hls.js": { - "version": "0.14.17", - "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-0.14.17.tgz", - "integrity": "sha512-25A7+m6qqp6UVkuzUQ//VVh2EEOPYlOBg32ypr34bcPO7liBMOkKFvbjbCBfiPAOTA/7BSx1Dujft3Th57WyFg==", - "dependencies": { - "eventemitter3": "^4.0.3", - "url-toolkit": "^2.1.6" - } + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.3.4.tgz", + "integrity": "sha512-iFEwVqtEDk6sKotcTwtJ5OMo/nuDTk9PrpB8FI2J2WYf8EriTVfR4FaK0aNyYtwbYeRSWCXJKlz23xeREdlNYg==" }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", @@ -8824,9 +8847,9 @@ "dev": true }, "node_modules/ignore": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -9785,11 +9808,11 @@ } }, "node_modules/jstree": { - "version": "3.3.14", - "resolved": "https://registry.npmjs.org/jstree/-/jstree-3.3.14.tgz", - "integrity": "sha512-W8t+nFOKENXNIulvu+DW4gPcnpOXY0FswiTiOn1Fnhs6poRe6eA/Kf6fS1/GJJ8C8KEy0q3ttF6tbGRDmHIM/g==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/jstree/-/jstree-3.3.15.tgz", + "integrity": "sha512-fNK2EBgGjaJQ3cJuINX/80vDeAufYWtM0csudgYl3eJG+eRAH/1r1IJVUOvAlJIa+uSgg+Fi8uGrt+Xbs92eKg==", "dependencies": { - "jquery": "^3.6.0" + "jquery": "^3.5.0" } }, "node_modules/jsx-ast-utils": { @@ -13096,11 +13119,11 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-router": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz", - "integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.2.tgz", + "integrity": "sha512-lF7S0UmXI5Pd8bmHvMdPKI4u4S5McxmHnzJhrYi9ZQ6wE+DA8JN5BzVC5EEBuduWWDaiJ8u6YhVOCmThBli+rw==", "dependencies": { - "@remix-run/router": "1.3.2" + "@remix-run/router": "1.3.3" }, "engines": { "node": ">=14" @@ -13110,12 +13133,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz", - "integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.2.tgz", + "integrity": "sha512-N/oAF1Shd7g4tWy+75IIufCGsHBqT74tnzHQhbiUTYILYF0Blk65cg+HPZqwC+6SqEyx033nKqU7by38v3lBZg==", "dependencies": { - "@remix-run/router": "1.3.2", - "react-router": "6.8.1" + "@remix-run/router": "1.3.3", + "react-router": "6.8.2" }, "engines": { "node": ">=14" @@ -13671,9 +13694,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.58.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.1.tgz", - "integrity": "sha512-bnINi6nPXbP1XNRaranMFEBZWUfdW/AF16Ql5+ypRxfTvCRTTKrLsMIakyDcayUt2t/RZotmL4kgJwNH5xO+bg==", + "version": "1.58.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz", + "integrity": "sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -14766,16 +14789,20 @@ } }, "node_modules/stylelint": { - "version": "14.16.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz", - "integrity": "sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.2.0.tgz", + "integrity": "sha512-wjg5OLn8zQwjlj5cYUgyQpMWKzct42AG5dYlqkHRJQJqsystFFn3onqEc263KH4xfEI0W3lZCnlIhFfS64uwSA==", "dev": true, "dependencies": { - "@csstools/selector-specificity": "^2.0.2", + "@csstools/css-parser-algorithms": "^2.0.1", + "@csstools/css-tokenizer": "^2.0.1", + "@csstools/media-query-list-parser": "^2.0.1", + "@csstools/selector-specificity": "^2.1.1", "balanced-match": "^2.0.0", "colord": "^2.9.3", - "cosmiconfig": "^7.1.0", + "cosmiconfig": "^8.0.0", "css-functions-list": "^3.1.0", + "css-tree": "^2.3.1", "debug": "^4.3.4", "fast-glob": "^3.2.12", "fastest-levenshtein": "^1.0.16", @@ -14784,7 +14811,7 @@ "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.2.0", - "ignore": "^5.2.1", + "ignore": "^5.2.4", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", @@ -14794,7 +14821,7 @@ "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.19", + "postcss": "^8.4.21", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", @@ -14808,13 +14835,13 @@ "svg-tags": "^1.0.0", "table": "^6.8.1", "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^4.0.2" + "write-file-atomic": "^5.0.0" }, "bin": { "stylelint": "bin/stylelint.js" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^14.13.1 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -17073,6 +17100,12 @@ "stylelint": "^14.5.1 || ^15.0.0" } }, + "node_modules/stylelint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/stylelint/node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -17089,19 +17122,34 @@ "dev": true }, "node_modules/stylelint/node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.0.tgz", + "integrity": "sha512-0tLZ9URlPGU7JsKq0DQOQ3FoRsYX8xDZ7xMiATQfaiGMz7EHowNkbU9u1coAOmnh9p/1ySpm0RB3JNWRXM5GCg==", "dev": true, "dependencies": { - "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "path-type": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + } + }, + "node_modules/stylelint/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/stylelint/node_modules/globby": { @@ -17158,6 +17206,24 @@ "node": ">=0.10.0" } }, + "node_modules/stylelint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/stylelint/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, "node_modules/stylelint/node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -18160,11 +18226,6 @@ "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, - "node_modules/url-toolkit": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.3.tgz", - "integrity": "sha512-Da75SQoxsZ+2wXS56CZBrj2nukQ4nlGUZUP/dqUBG5E1su5GKThgT94Q00x81eVII7AyS1Pn+CtTTZ4Z0pLUtQ==" - }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -19102,16 +19163,16 @@ } }, "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", + "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/write-file-atomic/node_modules/signal-exit": { @@ -19210,12 +19271,25 @@ }, "dependencies": { "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } } }, "@apideck/better-ajv-errors": { @@ -19253,21 +19327,21 @@ "dev": true }, "@babel/core": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", + "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", "dev": true, "requires": { - "@ampproject/remapping": "^2.1.0", + "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", + "@babel/generator": "^7.21.0", "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.0", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.0", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -19296,13 +19370,14 @@ } }, "@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", "dev": true, "requires": { - "@babel/types": "^7.20.7", + "@babel/types": "^7.21.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" } }, @@ -19356,17 +19431,18 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", - "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", + "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/helper-split-export-declaration": "^7.18.6" } }, @@ -19410,13 +19486,13 @@ } }, "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" } }, "@babel/helper-hoist-variables": { @@ -19429,12 +19505,12 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", - "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", + "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", "dev": true, "requires": { - "@babel/types": "^7.20.7" + "@babel/types": "^7.21.0" } }, "@babel/helper-module-imports": { @@ -19447,9 +19523,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", - "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", @@ -19458,8 +19534,8 @@ "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.10", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" } }, "@babel/helper-optimise-call-expression": { @@ -19513,12 +19589,12 @@ } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dev": true, "requires": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" } }, "@babel/helper-split-export-declaration": { @@ -19543,9 +19619,9 @@ "dev": true }, "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "dev": true }, "@babel/helper-wrap-function": { @@ -19561,14 +19637,14 @@ } }, "@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dev": true, "requires": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" } }, "@babel/highlight": { @@ -19583,9 +19659,9 @@ } }, "@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -19912,12 +19988,12 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-arrow-functions": { @@ -20257,14 +20333,14 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.3.tgz", - "integrity": "sha512-z6fnuK9ve9u/0X0rRvI9MY0xg+DOUaABDYOe+/SQTxtlptaBB/V9JIUxJn6xp3lMBeb9qe8xSFmHU35oZDXD+w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz", + "integrity": "sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-typescript": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" } }, "@babel/plugin-transform-unicode-escapes": { @@ -20397,14 +20473,14 @@ } }, "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz", + "integrity": "sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-transform-typescript": "^7.21.0" } }, "@babel/runtime": { @@ -20427,27 +20503,27 @@ } }, "@babel/traverse": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", - "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.19.4", @@ -20671,9 +20747,9 @@ "requires": {} }, "@csstools/selector-specificity": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", - "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", + "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", "dev": true, "requires": {} }, @@ -20695,9 +20771,9 @@ "dev": true }, "@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", + "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -20771,6 +20847,12 @@ } } }, + "@eslint/js": { + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", + "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", + "dev": true + }, "@fontsource/noto-sans": { "version": "4.5.11", "resolved": "https://registry.npmjs.org/@fontsource/noto-sans/-/noto-sans-4.5.11.tgz", @@ -20850,9 +20932,9 @@ } }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "devOptional": true }, "@jridgewell/set-array": { @@ -20872,19 +20954,19 @@ } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "devOptional": true }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "devOptional": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, "@leichtgewicht/ip-codec": { @@ -20955,9 +21037,9 @@ } }, "@remix-run/router": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", - "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.3.tgz", + "integrity": "sha512-YRHie1yQEj0kqqCTCJEfHqYSSNlZQ696QJG+MMiW4mxSl9I0ojz/eRhJS4fs88Z5i6D1SmoF9d3K99/QOhI8/w==" }, "@rollup/plugin-babel": { "version": "5.3.1", @@ -21355,14 +21437,14 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz", - "integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.0.tgz", + "integrity": "sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.52.0", - "@typescript-eslint/type-utils": "5.52.0", - "@typescript-eslint/utils": "5.52.0", + "@typescript-eslint/scope-manager": "5.54.0", + "@typescript-eslint/type-utils": "5.54.0", + "@typescript-eslint/utils": "5.54.0", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -21384,53 +21466,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz", - "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.0.tgz", + "integrity": "sha512-aAVL3Mu2qTi+h/r04WI/5PfNWvO6pdhpeMRWk9R7rEV4mwJNzoWf5CCU5vDKBsPIFQFjEq1xg7XBI2rjiMXQbQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.52.0", - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/scope-manager": "5.54.0", + "@typescript-eslint/types": "5.54.0", + "@typescript-eslint/typescript-estree": "5.54.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", - "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.0.tgz", + "integrity": "sha512-VTPYNZ7vaWtYna9M4oD42zENOBrb+ZYyCNdFs949GcN8Miwn37b8b7eMj+EZaq7VK9fx0Jd+JhmkhjFhvnovhg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/visitor-keys": "5.52.0" + "@typescript-eslint/types": "5.54.0", + "@typescript-eslint/visitor-keys": "5.54.0" } }, "@typescript-eslint/type-utils": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz", - "integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.0.tgz", + "integrity": "sha512-WI+WMJ8+oS+LyflqsD4nlXMsVdzTMYTxl16myXPaCXnSgc7LWwMsjxQFZCK/rVmTZ3FN71Ct78ehO9bRC7erYQ==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.52.0", - "@typescript-eslint/utils": "5.52.0", + "@typescript-eslint/typescript-estree": "5.54.0", + "@typescript-eslint/utils": "5.54.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", - "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.0.tgz", + "integrity": "sha512-nExy+fDCBEgqblasfeE3aQ3NuafBUxZxgxXcYfzYRZFHdVvk5q60KhCSkG0noHgHRo/xQ/BOzURLZAafFpTkmQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", - "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.0.tgz", + "integrity": "sha512-X2rJG97Wj/VRo5YxJ8Qx26Zqf0RRKsVHd4sav8NElhbZzhpBI8jU54i6hfo9eheumj4oO4dcRN1B/zIVEqR/MQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/visitor-keys": "5.52.0", + "@typescript-eslint/types": "5.54.0", + "@typescript-eslint/visitor-keys": "5.54.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -21470,16 +21552,16 @@ } }, "@typescript-eslint/utils": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz", - "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.0.tgz", + "integrity": "sha512-cuwm8D/Z/7AuyAeJ+T0r4WZmlnlxQ8wt7C7fLpFlKMR+dY6QO79Cq1WpJhvZbMA4ZeZGHiRWnht7ZJ8qkdAunw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.52.0", - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/scope-manager": "5.54.0", + "@typescript-eslint/types": "5.54.0", + "@typescript-eslint/typescript-estree": "5.54.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -21497,12 +21579,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", - "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", + "version": "5.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.0.tgz", + "integrity": "sha512-xu4wT7aRCakGINTLGeyGqDn+78BwFlggwBjnHa1ar/KaGagnmwLYmlrXIrgAaQ3AE1Vd6nLfKASm7LrFHNbKGA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/types": "5.54.0", "eslint-visitor-keys": "^3.3.0" }, "dependencies": { @@ -22159,9 +22241,9 @@ "dev": true }, "blurhash": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/blurhash/-/blurhash-2.0.4.tgz", - "integrity": "sha512-r/As72u2FbucLoK5NTegM/GucxJc3d8GvHc4ngo13IO/nt2HU4gONxNLq1XPN6EM/V8Y9URIa7PcSz2RZu553A==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/blurhash/-/blurhash-2.0.5.tgz", + "integrity": "sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==" }, "body-parser": { "version": "1.20.0", @@ -22821,9 +22903,9 @@ } }, "core-js": { - "version": "3.28.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.28.0.tgz", - "integrity": "sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw==" + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.0.tgz", + "integrity": "sha512-VG23vuEisJNkGl6XQmFJd3rEG/so/CNatqeE+7uZAwTSwFeB/qaO0be8xZYUNWprJ/GIwL8aMt9cj1kvbpTZhg==" }, "core-js-compat": { "version": "3.25.3", @@ -23756,12 +23838,13 @@ "dev": true }, "eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", - "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", + "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.4.1", + "@eslint/eslintrc": "^2.0.0", + "@eslint/js": "8.35.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -23775,7 +23858,7 @@ "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", "espree": "^9.4.0", - "esquery": "^1.4.0", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", @@ -24332,9 +24415,9 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -24401,7 +24484,8 @@ "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true }, "events": { "version": "3.3.0", @@ -25314,13 +25398,9 @@ } }, "hls.js": { - "version": "0.14.17", - "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-0.14.17.tgz", - "integrity": "sha512-25A7+m6qqp6UVkuzUQ//VVh2EEOPYlOBg32ypr34bcPO7liBMOkKFvbjbCBfiPAOTA/7BSx1Dujft3Th57WyFg==", - "requires": { - "eventemitter3": "^4.0.3", - "url-toolkit": "^2.1.6" - } + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.3.4.tgz", + "integrity": "sha512-iFEwVqtEDk6sKotcTwtJ5OMo/nuDTk9PrpB8FI2J2WYf8EriTVfR4FaK0aNyYtwbYeRSWCXJKlz23xeREdlNYg==" }, "hoist-non-react-statics": { "version": "3.3.2", @@ -25592,9 +25672,9 @@ "dev": true }, "ignore": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "immediate": { @@ -26275,11 +26355,11 @@ "dev": true }, "jstree": { - "version": "3.3.14", - "resolved": "https://registry.npmjs.org/jstree/-/jstree-3.3.14.tgz", - "integrity": "sha512-W8t+nFOKENXNIulvu+DW4gPcnpOXY0FswiTiOn1Fnhs6poRe6eA/Kf6fS1/GJJ8C8KEy0q3ttF6tbGRDmHIM/g==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/jstree/-/jstree-3.3.15.tgz", + "integrity": "sha512-fNK2EBgGjaJQ3cJuINX/80vDeAufYWtM0csudgYl3eJG+eRAH/1r1IJVUOvAlJIa+uSgg+Fi8uGrt+Xbs92eKg==", "requires": { - "jquery": "^3.6.0" + "jquery": "^3.5.0" } }, "jsx-ast-utils": { @@ -28630,20 +28710,20 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "react-router": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz", - "integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.2.tgz", + "integrity": "sha512-lF7S0UmXI5Pd8bmHvMdPKI4u4S5McxmHnzJhrYi9ZQ6wE+DA8JN5BzVC5EEBuduWWDaiJ8u6YhVOCmThBli+rw==", "requires": { - "@remix-run/router": "1.3.2" + "@remix-run/router": "1.3.3" } }, "react-router-dom": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz", - "integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.2.tgz", + "integrity": "sha512-N/oAF1Shd7g4tWy+75IIufCGsHBqT74tnzHQhbiUTYILYF0Blk65cg+HPZqwC+6SqEyx033nKqU7by38v3lBZg==", "requires": { - "@remix-run/router": "1.3.2", - "react-router": "6.8.1" + "@remix-run/router": "1.3.3", + "react-router": "6.8.2" } }, "read-file-stdin": { @@ -29066,9 +29146,9 @@ "dev": true }, "sass": { - "version": "1.58.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.1.tgz", - "integrity": "sha512-bnINi6nPXbP1XNRaranMFEBZWUfdW/AF16Ql5+ypRxfTvCRTTKrLsMIakyDcayUt2t/RZotmL4kgJwNH5xO+bg==", + "version": "1.58.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz", + "integrity": "sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -29940,16 +30020,20 @@ } }, "stylelint": { - "version": "14.16.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz", - "integrity": "sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.2.0.tgz", + "integrity": "sha512-wjg5OLn8zQwjlj5cYUgyQpMWKzct42AG5dYlqkHRJQJqsystFFn3onqEc263KH4xfEI0W3lZCnlIhFfS64uwSA==", "dev": true, "requires": { - "@csstools/selector-specificity": "^2.0.2", + "@csstools/css-parser-algorithms": "^2.0.1", + "@csstools/css-tokenizer": "^2.0.1", + "@csstools/media-query-list-parser": "^2.0.1", + "@csstools/selector-specificity": "^2.1.1", "balanced-match": "^2.0.0", "colord": "^2.9.3", - "cosmiconfig": "^7.1.0", + "cosmiconfig": "^8.0.0", "css-functions-list": "^3.1.0", + "css-tree": "^2.3.1", "debug": "^4.3.4", "fast-glob": "^3.2.12", "fastest-levenshtein": "^1.0.16", @@ -29958,7 +30042,7 @@ "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.2.0", - "ignore": "^5.2.1", + "ignore": "^5.2.4", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", @@ -29968,7 +30052,7 @@ "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.19", + "postcss": "^8.4.21", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", @@ -29982,9 +30066,15 @@ "svg-tags": "^1.0.0", "table": "^6.8.1", "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^4.0.2" + "write-file-atomic": "^5.0.0" }, "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -29998,16 +30088,25 @@ "dev": true }, "cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.0.tgz", + "integrity": "sha512-0tLZ9URlPGU7JsKq0DQOQ3FoRsYX8xDZ7xMiATQfaiGMz7EHowNkbU9u1coAOmnh9p/1ySpm0RB3JNWRXM5GCg==", "dev": true, "requires": { - "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "path-type": "^4.0.0" + } + }, + "css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "requires": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" } }, "globby": { @@ -30048,6 +30147,21 @@ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -32573,11 +32687,6 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, - "url-toolkit": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.3.tgz", - "integrity": "sha512-Da75SQoxsZ+2wXS56CZBrj2nukQ4nlGUZUP/dqUBG5E1su5GKThgT94Q00x81eVII7AyS1Pn+CtTTZ4Z0pLUtQ==" - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -33334,9 +33443,9 @@ } }, "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", + "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", "dev": true, "requires": { "imurmurhash": "^0.1.4", diff --git a/package.json b/package.json index b1ed0c59b0..bc19c04a9e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "repository": "https://github.com/jellyfin/jellyfin-web", "license": "GPL-2.0-or-later", "devDependencies": { - "@babel/core": "7.20.12", + "@babel/core": "7.21.0", "@babel/eslint-parser": "7.19.1", "@babel/eslint-plugin": "7.19.1", "@babel/plugin-proposal-class-properties": "7.18.6", @@ -13,14 +13,14 @@ "@babel/plugin-transform-modules-umd": "7.18.6", "@babel/preset-env": "7.20.2", "@babel/preset-react": "7.18.6", - "@babel/preset-typescript": "7.18.6", + "@babel/preset-typescript": "7.21.0", "@types/escape-html": "1.0.2", "@types/loadable__component": "5.13.4", "@types/lodash-es": "4.17.6", "@types/react": "17.0.53", "@types/react-dom": "17.0.19", - "@typescript-eslint/eslint-plugin": "5.52.0", - "@typescript-eslint/parser": "5.52.0", + "@typescript-eslint/eslint-plugin": "5.54.0", + "@typescript-eslint/parser": "5.54.0", "@uupaa/dynamic-import-polyfill": "1.0.2", "autoprefixer": "10.4.13", "babel-loader": "9.1.2", @@ -32,7 +32,7 @@ "css-loader": "6.7.3", "cssnano": "5.1.15", "es-check": "7.1.0", - "eslint": "8.34.0", + "eslint": "8.35.0", "eslint-plugin-compat": "4.1.2", "eslint-plugin-eslint-comments": "3.2.0", "eslint-plugin-import": "2.27.5", @@ -49,11 +49,11 @@ "postcss-loader": "7.0.2", "postcss-preset-env": "8.0.1", "postcss-scss": "4.0.6", - "sass": "1.58.1", + "sass": "1.58.3", "sass-loader": "13.2.0", "source-map-loader": "4.0.1", "style-loader": "3.3.1", - "stylelint": "14.16.1", + "stylelint": "15.2.0", "stylelint-config-rational-order": "0.1.2", "stylelint-no-browser-hacks": "1.2.1", "stylelint-order": "6.0.2", @@ -77,10 +77,10 @@ "@jellyfin/libass-wasm": "4.1.1", "@jellyfin/sdk": "unstable", "@loadable/component": "5.15.3", - "blurhash": "2.0.4", + "blurhash": "2.0.5", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", "classnames": "2.3.2", - "core-js": "3.28.0", + "core-js": "3.29.0", "date-fns": "2.29.3", "dompurify": "2.4.4", "epubjs": "0.4.2", @@ -89,11 +89,11 @@ "flv.js": "1.6.2", "headroom.js": "0.12.0", "history": "5.3.0", - "hls.js": "0.14.17", + "hls.js": "1.3.4", "intersection-observer": "0.12.2", "jellyfin-apiclient": "1.10.0", "jquery": "3.6.3", - "jstree": "3.3.14", + "jstree": "3.3.15", "libarchive.js": "1.3.0", "lodash-es": "4.17.21", "marked": "4.2.12", @@ -102,7 +102,7 @@ "pdfjs-dist": "2.16.105", "react": "17.0.2", "react-dom": "17.0.2", - "react-router-dom": "6.8.1", + "react-router-dom": "6.8.2", "resize-observer-polyfill": "1.5.1", "screenfull": "6.0.2", "sortablejs": "1.15.0", diff --git a/src/components/ConnectionRequired.tsx b/src/components/ConnectionRequired.tsx index aa67a257e1..9c5f402b0a 100644 --- a/src/components/ConnectionRequired.tsx +++ b/src/components/ConnectionRequired.tsx @@ -1,5 +1,5 @@ import React, { FunctionComponent, useEffect, useState } from 'react'; -import { Outlet, useNavigate } from 'react-router-dom'; +import { Outlet, useLocation, useNavigate } from 'react-router-dom'; import type { ConnectResponse } from 'jellyfin-apiclient'; import alert from './alert'; @@ -31,11 +31,11 @@ const ConnectionRequired: FunctionComponent = ({ isUserRequired = true }) => { const navigate = useNavigate(); + const location = useLocation(); const [ isLoading, setIsLoading ] = useState(true); useEffect(() => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any const bounce = async (connectionResponse: ConnectResponse) => { switch (connectionResponse.State) { case ConnectionState.SignedIn: @@ -45,12 +45,12 @@ const ConnectionRequired: FunctionComponent = ({ return; case ConnectionState.ServerSignIn: // Bounce to the login page - console.debug('[ConnectionRequired] not logged in, redirecting to login page'); - navigate(BounceRoutes.Login, { - state: { - serverid: connectionResponse.ApiClient.serverId() - } - }); + if (location.pathname === BounceRoutes.Login) { + setIsLoading(false); + } else { + console.debug('[ConnectionRequired] not logged in, redirecting to login page'); + navigate(`${BounceRoutes.Login}?serverid=${connectionResponse.ApiClient.serverId()}`); + } return; case ConnectionState.ServerSelection: // Bounce to select server page @@ -144,7 +144,7 @@ const ConnectionRequired: FunctionComponent = ({ }; validateConnection(); - }, [ isAdminRequired, isUserRequired, navigate ]); + }, [ isAdminRequired, isUserRequired, location.pathname, navigate ]); if (isLoading) { return ; diff --git a/src/components/accessSchedule/accessSchedule.js b/src/components/accessSchedule/accessSchedule.js index 4db600ecef..33df97532e 100644 --- a/src/components/accessSchedule/accessSchedule.js +++ b/src/components/accessSchedule/accessSchedule.js @@ -19,7 +19,7 @@ import template from './accessSchedule.template.html'; const pct = hours % 1; if (pct) { - minutes = parseInt(60 * pct); + minutes = parseInt(60 * pct, 10); } return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0)); diff --git a/src/components/actionSheet/actionSheet.js b/src/components/actionSheet/actionSheet.js index eacf3624b2..1114908fcf 100644 --- a/src/components/actionSheet/actionSheet.js +++ b/src/components/actionSheet/actionSheet.js @@ -6,7 +6,7 @@ import dom from '../../scripts/dom'; import '../../elements/emby-button/emby-button'; import './actionSheet.scss'; import 'material-design-icons-iconfont'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import '../../components/listview/listview.scss'; function getOffsets(elems) { diff --git a/src/components/activitylog.js b/src/components/activitylog.js index 82f58e1879..54de02b97c 100644 --- a/src/components/activitylog.js +++ b/src/components/activitylog.js @@ -64,10 +64,10 @@ import { toBoolean } from '../utils/string.ts'; function reloadData(instance, elem, apiClient, startIndex, limit) { if (startIndex == null) { - startIndex = parseInt(elem.getAttribute('data-activitystartindex') || '0'); + startIndex = parseInt(elem.getAttribute('data-activitystartindex') || '0', 10); } - limit = limit || parseInt(elem.getAttribute('data-activitylimit') || '7'); + limit = limit || parseInt(elem.getAttribute('data-activitylimit') || '7', 10); const minDate = new Date(); const hasUserId = toBoolean(elem.getAttribute('data-useractivity'), true); diff --git a/src/components/appRouter.js b/src/components/appRouter.js index 05feb0ff9c..be7cfd5855 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -124,7 +124,7 @@ class AppRouter { isBack: action === Action.Pop }); } else { - console.info('[appRouter] "%s" route not found', normalizedPath, location); + // The route is not registered here, so it should be handled by react-router this.currentRouteInfo = { route: {}, path: normalizedPath + location.search diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index 658837df83..b09247f613 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -663,7 +663,7 @@ import { appRouter } from '../appRouter'; const character = String(str.slice(charIndex, charIndex + 1).charCodeAt()); let sum = 0; for (let i = 0; i < character.length; i++) { - sum += parseInt(character.charAt(i)); + sum += parseInt(character.charAt(i), 10); } const index = String(sum).slice(-1); @@ -773,27 +773,24 @@ import { appRouter } from '../appRouter'; * @param {Object} item - Item used to generate the footer text. * @param {Object} apiClient - API client instance. * @param {Object} options - Options used to generate the footer text. - * @param {string} showTitle - Flag to show the title in the footer. - * @param {boolean} forceName - Flag to force showing the name of the item. - * @param {boolean} overlayText - Flag to show overlay text. - * @param {Object} imgUrl - Object representing the card's image URL. * @param {string} footerClass - CSS classes of the footer element. * @param {string} progressHtml - HTML markup of the progress bar element. - * @param {string} logoUrl - URL of the logo for the item. - * @param {boolean} isOuterFooter - Flag to mark the text as outer footer. + * @param {Object} flags - Various flags for the footer + * @param {Object} urls - Various urls for the footer * @returns {string} HTML markup of the card's footer text element. */ - function getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerClass, progressHtml, logoUrl, isOuterFooter) { + function getCardFooterText(item, apiClient, options, footerClass, progressHtml, flags, urls) { item = item.ProgramInfo || item; let html = ''; - if (logoUrl) { - html += ''; + if (urls.logoUrl) { + html += ''; } - const showOtherText = isOuterFooter ? !overlayText : overlayText; + const showTitle = options.showTitle === 'auto' ? true : (options.showTitle || item.Type === 'PhotoAlbum' || item.Type === 'Folder'); + const showOtherText = flags.isOuterFooter ? !flags.overlayText : flags.overlayText; - if (isOuterFooter && options.cardLayout && layoutManager.mobile && options.cardFooterAside !== 'none') { + if (flags.isOuterFooter && options.cardLayout && layoutManager.mobile && options.cardFooterAside !== 'none') { html += ``; } @@ -805,7 +802,7 @@ import { appRouter } from '../appRouter'; let titleAdded; if (showOtherText && (options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath) { - if (isOuterFooter && item.Type === 'Episode' && item.SeriesName) { + if (flags.isOuterFooter && item.Type === 'Episode' && item.SeriesName) { if (item.SeriesId) { lines.push(getTextActionButton({ Id: item.SeriesId, @@ -835,7 +832,7 @@ import { appRouter } from '../appRouter'; } let showMediaTitle = (showTitle && !titleAdded) || (options.showParentTitleOrTitle && !lines.length); - if (!showMediaTitle && !titleAdded && (showTitle || forceName)) { + if (!showMediaTitle && !titleAdded && (showTitle || flags.forceName)) { showMediaTitle = true; } @@ -856,7 +853,7 @@ import { appRouter } from '../appRouter'; if (showOtherText) { if (options.showParentTitle && parentTitleUnderneath) { - if (isOuterFooter && item.AlbumArtists && item.AlbumArtists.length) { + if (flags.isOuterFooter && item.AlbumArtists && item.AlbumArtists.length) { item.AlbumArtists[0].Type = 'MusicArtist'; item.AlbumArtists[0].IsFolder = true; lines.push(getTextActionButton(item.AlbumArtists[0], null, serverId)); @@ -991,23 +988,23 @@ import { appRouter } from '../appRouter'; } } - if ((showTitle || !imgUrl) && forceName && overlayText && lines.length === 1) { + if ((showTitle || !urls.imgUrl) && flags.forceName && flags.overlayText && lines.length === 1) { lines = []; } - if (overlayText && showTitle) { + if (flags.overlayText && showTitle) { lines = [escapeHtml(item.Name)]; } - const addRightTextMargin = isOuterFooter && options.cardLayout && !options.centerText && options.cardFooterAside !== 'none' && layoutManager.mobile; + const addRightTextMargin = flags.isOuterFooter && options.cardLayout && !options.centerText && options.cardFooterAside !== 'none' && layoutManager.mobile; - html += getCardTextLines(lines, cssClass, !options.overlayText, isOuterFooter, options.cardLayout, addRightTextMargin, options.lines); + html += getCardTextLines(lines, cssClass, !options.overlayText, flags.isOuterFooter, options.cardLayout, addRightTextMargin, options.lines); if (progressHtml) { html += progressHtml; } - if (html && (!isOuterFooter || logoUrl || options.cardLayout)) { + if (html && (!flags.isOuterFooter || urls.logoUrl || options.cardLayout)) { html = '
' + html; //cardFooter @@ -1217,7 +1214,6 @@ import { appRouter } from '../appRouter'; const forceName = imgInfo.forceName; - const showTitle = options.showTitle === 'auto' ? true : (options.showTitle || item.Type === 'PhotoAlbum' || item.Type === 'Folder'); const overlayText = options.overlayText; let cardImageContainerClass = 'cardImageContainer'; @@ -1265,7 +1261,7 @@ import { appRouter } from '../appRouter'; logoUrl = null; footerCssClass = progressHtml ? 'innerCardFooter fullInnerCardFooter' : 'innerCardFooter'; - innerCardFooter += getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, false); + innerCardFooter += getCardFooterText(item, apiClient, options, footerCssClass, progressHtml, { forceName, overlayText, isOuterFooter: false }, { imgUrl, logoUrl }); footerOverlayed = true; } else if (progressHtml) { innerCardFooter += '
'; @@ -1292,7 +1288,7 @@ import { appRouter } from '../appRouter'; logoUrl = null; } - outerCardFooter = getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, true); + outerCardFooter = getCardFooterText(item, apiClient, options, footerCssClass, progressHtml, { forceName, overlayText, isOuterFooter: true }, { imgUrl, logoUrl }); } if (outerCardFooter && !options.cardLayout) { diff --git a/src/components/collectionEditor/collectionEditor.js b/src/components/collectionEditor/collectionEditor.js index 8af78fede2..89862725a5 100644 --- a/src/components/collectionEditor/collectionEditor.js +++ b/src/components/collectionEditor/collectionEditor.js @@ -12,7 +12,7 @@ import '../../elements/emby-input/emby-input'; import '../../elements/emby-select/emby-select'; import 'material-design-icons-iconfont'; import '../formdialog.scss'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import ServerConnections from '../ServerConnections'; import toast from '../toast/toast'; diff --git a/src/components/dialog/dialog.js b/src/components/dialog/dialog.js index 9629e9bb23..85e81abb02 100644 --- a/src/components/dialog/dialog.js +++ b/src/components/dialog/dialog.js @@ -10,7 +10,7 @@ import '../../elements/emby-button/emby-button'; import '../../elements/emby-button/paper-icon-button-light'; import '../../elements/emby-input/emby-input'; import '../formdialog.scss'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import template from './dialog.template.html'; /* eslint-disable indent */ diff --git a/src/components/dialogHelper/dialogHelper.js b/src/components/dialogHelper/dialogHelper.js index 9c1ecec625..6811525465 100644 --- a/src/components/dialogHelper/dialogHelper.js +++ b/src/components/dialogHelper/dialogHelper.js @@ -7,7 +7,7 @@ import { toBoolean } from '../../utils/string.ts'; import dom from '../../scripts/dom'; import './dialoghelper.scss'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; /* eslint-disable indent */ diff --git a/src/components/displaySettings/displaySettings.js b/src/components/displaySettings/displaySettings.js index f351368a9a..195c919e9b 100644 --- a/src/components/displaySettings/displaySettings.js +++ b/src/components/displaySettings/displaySettings.js @@ -8,6 +8,7 @@ import datetime from '../../scripts/datetime'; import globalize from '../../scripts/globalize'; import loading from '../loading/loading'; import skinManager from '../../scripts/themeManager'; +import { PluginType } from '../../types/plugin.ts'; import Events from '../../utils/events.ts'; import '../../elements/emby-select/emby-select'; import '../../elements/emby-checkbox/emby-checkbox'; @@ -35,7 +36,7 @@ import template from './displaySettings.template.html'; function loadScreensavers(context, userSettings) { const selectScreensaver = context.querySelector('.selectScreensaver'); - const options = pluginManager.ofType('screensaver').map(plugin => { + const options = pluginManager.ofType(PluginType.Screensaver).map(plugin => { return { name: plugin.name, value: plugin.id diff --git a/src/components/favoriteitems.js b/src/components/favoriteitems.js index 67137b9174..6bba5c3bdc 100644 --- a/src/components/favoriteitems.js +++ b/src/components/favoriteitems.js @@ -6,7 +6,7 @@ import imageLoader from './images/imageLoader'; import globalize from '../scripts/globalize'; import layoutManager from './layoutManager'; import { getParameterByName } from '../utils/url.ts'; -import '../assets/css/scrollstyles.scss'; +import '../styles/scrollstyles.scss'; import '../elements/emby-itemscontainer/emby-itemscontainer'; /* eslint-disable indent */ diff --git a/src/components/filtermenu/filtermenu.js b/src/components/filtermenu/filtermenu.js index d6b85256a9..4c1b6d625b 100644 --- a/src/components/filtermenu/filtermenu.js +++ b/src/components/filtermenu/filtermenu.js @@ -13,7 +13,7 @@ import '../../elements/emby-button/paper-icon-button-light'; import '../../elements/emby-select/emby-select'; import 'material-design-icons-iconfont'; import '../formdialog.scss'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import ServerConnections from '../ServerConnections'; import template from './filtermenu.template.html'; diff --git a/src/components/groupedcards.js b/src/components/groupedcards.js index a90423a6b0..295c3170cc 100644 --- a/src/components/groupedcards.js +++ b/src/components/groupedcards.js @@ -13,7 +13,7 @@ import ServerConnections from './ServerConnections'; const playedIndicator = card.querySelector('.playedIndicator'); const playedIndicatorHtml = playedIndicator ? playedIndicator.innerHTML : null; const options = { - Limit: parseInt(playedIndicatorHtml || '10'), + Limit: parseInt(playedIndicatorHtml || '10', 10), Fields: 'PrimaryImageAspectRatio,DateCreated', ParentId: itemId, GroupItems: false diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index 14c973d511..36137af21d 100644 --- a/src/components/guide/guide.js +++ b/src/components/guide/guide.js @@ -17,13 +17,13 @@ import dom from '../../scripts/dom'; import './guide.scss'; import './programs.scss'; import 'material-design-icons-iconfont'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import '../../elements/emby-programcell/emby-programcell'; import '../../elements/emby-button/emby-button'; import '../../elements/emby-button/paper-icon-button-light'; import '../../elements/emby-tabs/emby-tabs'; import '../../elements/emby-scroller/emby-scroller'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import 'webcomponents.js/webcomponents-lite'; import ServerConnections from '../ServerConnections'; import template from './tvguide.template.html'; @@ -345,7 +345,9 @@ function Guide(options) { } apiClient.getLiveTvPrograms(programQuery).then(function (programsResult) { - renderGuide(context, date, channelsResult.Items, programsResult.Items, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender); + const guideOptions = { focusProgramOnRender, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs }; + + renderGuide(context, date, channelsResult.Items, programsResult.Items, renderOptions, guideOptions, apiClient); hideLoading(); }); @@ -667,7 +669,7 @@ function Guide(options) { return (channelIndex * 10000000) + (start.getTime() / 60000); } - function renderGuide(context, date, channels, programs, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { + function renderGuide(context, date, channels, programs, renderOptions, guideOptions, apiClient) { programs.sort(function (a, b) { return getProgramSortOrder(a, channels) - getProgramSortOrder(b, channels); }); @@ -689,11 +691,11 @@ function Guide(options) { items = {}; renderPrograms(context, date, channels, programs, renderOptions); - if (focusProgramOnRender) { - focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs); + if (guideOptions.focusProgramOnRender) { + focusProgram(context, itemId, channelRowId, guideOptions.focusToTimeMs, guideOptions.startTimeOfDayMs); } - scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs); + scrollProgramGridToTimeMs(context, guideOptions.scrollToTimeMs, guideOptions.startTimeOfDayMs); } function scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs) { @@ -1147,12 +1149,12 @@ function Guide(options) { guideContext.querySelector('.guideDateTabs').addEventListener('tabchange', function (e) { const allTabButtons = e.target.querySelectorAll('.guide-date-tab-button'); - const tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex)]; + const tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex, 10)]; if (tabButton) { - const previousButton = e.detail.previousIndex == null ? null : allTabButtons[parseInt(e.detail.previousIndex)]; + const previousButton = e.detail.previousIndex == null ? null : allTabButtons[parseInt(e.detail.previousIndex, 10)]; const date = new Date(); - date.setTime(parseInt(tabButton.getAttribute('data-date'))); + date.setTime(parseInt(tabButton.getAttribute('data-date'), 10)); const scrollWidth = programGrid.scrollWidth; let scrollToTimeMs; @@ -1164,7 +1166,7 @@ function Guide(options) { if (previousButton) { const previousDate = new Date(); - previousDate.setTime(parseInt(previousButton.getAttribute('data-date'))); + previousDate.setTime(parseInt(previousButton.getAttribute('data-date'), 10)); scrollToTimeMs += (previousDate.getHours() * 60 * 60 * 1000); scrollToTimeMs += (previousDate.getMinutes() * 60 * 1000); diff --git a/src/components/imageeditor/imageeditor.js b/src/components/imageeditor/imageeditor.js index 039770a217..1711169dbc 100644 --- a/src/components/imageeditor/imageeditor.js +++ b/src/components/imageeditor/imageeditor.js @@ -96,7 +96,7 @@ import template from './imageeditor.template.html'; return apiClient.getScaledImageUrl(item.Id || item.ItemId, options); } - function getCardHtml(image, index, numImages, apiClient, imageProviders, imageSize, tagName, enableFooterButtons) { + function getCardHtml(image, apiClient, options) { // TODO move card creation code to Card component let html = ''; @@ -106,7 +106,7 @@ import template from './imageeditor.template.html'; cssClass += ' backdropCard backdropCard-scalable'; - if (tagName === 'button') { + if (options.tagName === 'button') { cssClass += ' btnImageCard'; if (layoutManager.tv) { @@ -122,7 +122,7 @@ import template from './imageeditor.template.html'; html += '
'; @@ -151,23 +151,23 @@ import template from './imageeditor.template.html'; } html += '
'; - if (enableFooterButtons) { + if (options.enableFooterButtons) { html += '
'; if (image.ImageType === 'Backdrop') { - if (index > 0) { + if (options.index > 0) { html += ''; } else { html += ''; } - if (index < numImages - 1) { + if (options.index < options.numImages - 1) { html += ''; } else { html += ''; } } else { - if (imageProviders.length) { + if (options.imageProviders.length) { html += ''; } } @@ -178,7 +178,7 @@ import template from './imageeditor.template.html'; html += '
'; html += '
'; - html += ''; + html += ''; return html; } @@ -226,7 +226,8 @@ import template from './imageeditor.template.html'; for (let i = 0, length = images.length; i < length; i++) { const image = images[i]; - html += getCardHtml(image, i, length, apiClient, imageProviders, imageSize, tagName, enableFooterButtons); + const options = { index: i, numImages: length, imageProviders, imageSize, tagName, enableFooterButtons }; + html += getCardHtml(image, apiClient, options); } elem.innerHTML = html; @@ -277,9 +278,9 @@ import template from './imageeditor.template.html'; const apiClient = ServerConnections.getApiClient(serverId); const type = imageCard.getAttribute('data-imagetype'); - const index = parseInt(imageCard.getAttribute('data-index')); - const providerCount = parseInt(imageCard.getAttribute('data-providers')); - const numImages = parseInt(imageCard.getAttribute('data-numimages')); + const index = parseInt(imageCard.getAttribute('data-index'), 10); + const providerCount = parseInt(imageCard.getAttribute('data-providers'), 10); + const numImages = parseInt(imageCard.getAttribute('data-numimages'), 10); import('../actionSheet/actionSheet').then(({default: actionSheet}) => { const commands = []; @@ -384,7 +385,7 @@ import template from './imageeditor.template.html'; addListeners(context, 'btnDeleteImage', 'click', function () { const type = this.getAttribute('data-imagetype'); let index = this.getAttribute('data-index'); - index = index === 'null' ? null : parseInt(index); + index = index === 'null' ? null : parseInt(index, 10); const apiClient = ServerConnections.getApiClient(currentItem.ServerId); deleteImage(context, currentItem.Id, type, index, apiClient, true); }); diff --git a/src/components/itemMediaInfo/itemMediaInfo.js b/src/components/itemMediaInfo/itemMediaInfo.js index d149a633a0..ac04df4815 100644 --- a/src/components/itemMediaInfo/itemMediaInfo.js +++ b/src/components/itemMediaInfo/itemMediaInfo.js @@ -20,7 +20,7 @@ import '../../elements/emby-button/emby-button'; import '../../elements/emby-button/paper-icon-button-light'; import '../formdialog.scss'; import 'material-design-icons-iconfont'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import ServerConnections from '../ServerConnections'; import template from './itemMediaInfo.template.html'; @@ -138,7 +138,7 @@ const attributeDelimiterHtml = layoutManager.tv ? '' : ': { diff --git a/src/components/mediaLibraryEditor/mediaLibraryEditor.js b/src/components/mediaLibraryEditor/mediaLibraryEditor.js index da0dc2909c..c90acc0078 100644 --- a/src/components/mediaLibraryEditor/mediaLibraryEditor.js +++ b/src/components/mediaLibraryEditor/mediaLibraryEditor.js @@ -17,7 +17,7 @@ import '../listview/listview.scss'; import '../../elements/emby-button/paper-icon-button-light'; import '../formdialog.scss'; import '../../elements/emby-toggle/emby-toggle'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import './style.scss'; import toast from '../toast/toast'; import confirm from '../confirm/confirm'; @@ -93,7 +93,7 @@ import template from './mediaLibraryEditor.template.html'; const listItem = dom.parentWithClass(e.target, 'listItem'); if (listItem) { - const index = parseInt(listItem.getAttribute('data-index')); + const index = parseInt(listItem.getAttribute('data-index'), 10); const pathInfos = (currentOptions.library.LibraryOptions || {}).PathInfos || []; const pathInfo = index == null ? {} : pathInfos[index] || {}; const originalPath = pathInfo.Path || (index == null ? null : currentOptions.library.Locations[index]); diff --git a/src/components/metadataEditor/metadataEditor.js b/src/components/metadataEditor/metadataEditor.js index a36779ba53..fb69d20aa2 100644 --- a/src/components/metadataEditor/metadataEditor.js +++ b/src/components/metadataEditor/metadataEditor.js @@ -15,8 +15,8 @@ import '../../elements/emby-textarea/emby-textarea'; import '../../elements/emby-button/emby-button'; import '../../elements/emby-button/paper-icon-button-light'; import '../formdialog.scss'; -import '../../assets/css/clearbutton.scss'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/clearbutton.scss'; +import '../../styles/flexstyles.scss'; import './style.scss'; import ServerConnections from '../ServerConnections'; import toast from '../toast/toast'; @@ -357,14 +357,14 @@ import template from './metadataEditor.template.html'; let index; const btnDeletePerson = dom.parentWithClass(e.target, 'btnDeletePerson'); if (btnDeletePerson) { - index = parseInt(btnDeletePerson.getAttribute('data-index')); + index = parseInt(btnDeletePerson.getAttribute('data-index'), 10); currentItem.People.splice(index, 1); populatePeople(context, currentItem.People); } const btnEditPerson = dom.parentWithClass(e.target, 'btnEditPerson'); if (btnEditPerson) { - index = parseInt(btnEditPerson.getAttribute('data-index')); + index = parseInt(btnEditPerson.getAttribute('data-index'), 10); editPerson(context, currentItem.People[index], index); } }); diff --git a/src/components/playback/mediasession.js b/src/components/playback/mediasession.js index 089820fb47..d16fe7756f 100644 --- a/src/components/playback/mediasession.js +++ b/src/components/playback/mediasession.js @@ -114,8 +114,8 @@ import shell from '../../scripts/shell'; const itemId = item.Id; // Convert to ms - const duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0); - const currentTime = parseInt(playState.PositionTicks ? (playState.PositionTicks / 10000) : 0); + const duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0, 10); + const currentTime = parseInt(playState.PositionTicks ? (playState.PositionTicks / 10000) : 0, 10); const isPaused = playState.IsPaused || false; const canSeek = playState.CanSeek || false; @@ -247,7 +247,7 @@ import shell from '../../scripts/shell'; navigator.mediaSession.setActionHandler('seekto', function (object) { const item = playbackManager.getPlayerState(currentPlayer).NowPlayingItem; // Convert to ms - const duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0); + const duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0, 10); const wantedTime = object.seekTime * 1000; playbackManager.seekPercent(wantedTime / duration * 100, currentPlayer); }); diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 2b8bd21280..31b04e0fbe 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -11,6 +11,7 @@ import { appHost } from '../apphost'; import Screenfull from 'screenfull'; import ServerConnections from '../ServerConnections'; import alert from '../alert'; +import { PluginType } from '../../types/plugin.ts'; import { includesAny } from '../../utils/container.ts'; const UNLIMITED_ITEMS = -1; @@ -299,20 +300,20 @@ function getAudioMaxValues(deviceProfile) { } let startingPlaySession = new Date().getTime(); -function getAudioStreamUrl(item, transcodingProfile, directPlayContainers, maxBitrate, apiClient, maxAudioSampleRate, maxAudioBitDepth, maxAudioBitrate, startPosition) { +function getAudioStreamUrl(item, transcodingProfile, directPlayContainers, apiClient, startPosition, maxValues) { const url = 'Audio/' + item.Id + '/universal'; startingPlaySession++; return apiClient.getUrl(url, { UserId: apiClient.getCurrentUserId(), DeviceId: apiClient.deviceId(), - MaxStreamingBitrate: maxAudioBitrate || maxBitrate, + MaxStreamingBitrate: maxValues.maxAudioBitrate || maxValues.maxBitrate, Container: directPlayContainers, TranscodingContainer: transcodingProfile.Container || null, TranscodingProtocol: transcodingProfile.Protocol || null, AudioCodec: transcodingProfile.AudioCodec, - MaxAudioSampleRate: maxAudioSampleRate, - MaxAudioBitDepth: maxAudioBitDepth, + MaxAudioSampleRate: maxValues.maxAudioSampleRate, + MaxAudioBitDepth: maxValues.maxAudioBitDepth, api_key: apiClient.accessToken(), PlaySessionId: startingPlaySession, StartTimeTicks: startPosition || 0, @@ -344,7 +345,7 @@ function getAudioStreamUrlFromDeviceProfile(item, deviceProfile, maxBitrate, api const maxValues = getAudioMaxValues(deviceProfile); - return getAudioStreamUrl(item, transcodingProfile, directPlayContainers, maxBitrate, apiClient, maxValues.maxAudioSampleRate, maxValues.maxAudioBitDepth, maxValues.maxAudioBitrate, startPosition); + return getAudioStreamUrl(item, transcodingProfile, directPlayContainers, apiClient, startPosition, { maxBitrate, ...maxValues }); } function getStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPosition) { @@ -377,7 +378,7 @@ function getStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositio let streamUrl; if (item.MediaType === 'Audio' && !itemHelper.isLocalItem(item)) { - streamUrl = getAudioStreamUrl(item, audioTranscodingProfile, audioDirectPlayContainers, maxBitrate, apiClient, maxValues.maxAudioSampleRate, maxValues.maxAudioBitDepth, maxValues.maxAudioBitrate, startPosition); + streamUrl = getAudioStreamUrl(item, audioTranscodingProfile, audioDirectPlayContainers, apiClient, startPosition, { maxBitrate, ...maxValues }); } streamUrls.push(streamUrl || ''); @@ -408,27 +409,12 @@ function setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositio }); } -function getPlaybackInfo(player, - apiClient, - item, - deviceProfile, - maxBitrate, - startPosition, - isPlayback, - mediaSourceId, - audioStreamIndex, - subtitleStreamIndex, - liveStreamId, - enableDirectPlay, - enableDirectStream, - allowVideoStreamCopy, - allowAudioStreamCopy, - secondarySubtitleStreamIndex) { +function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, liveStreamId, options) { if (!itemHelper.isLocalItem(item) && item.MediaType === 'Audio' && !player.useServerPlaybackInfoForAudio) { return Promise.resolve({ MediaSources: [ { - StreamUrl: getAudioStreamUrlFromDeviceProfile(item, deviceProfile, maxBitrate, apiClient, startPosition), + StreamUrl: getAudioStreamUrlFromDeviceProfile(item, deviceProfile, options.maxBitrate, apiClient, options.startPosition), Id: item.Id, MediaStreams: [], RunTimeTicks: item.RunTimeTicks @@ -446,10 +432,10 @@ function getPlaybackInfo(player, const query = { UserId: apiClient.getCurrentUserId(), - StartTimeTicks: startPosition || 0 + StartTimeTicks: options.startPosition || 0 }; - if (isPlayback) { + if (options.isPlayback) { query.IsPlayback = true; query.AutoOpenLiveStream = true; } else { @@ -457,27 +443,26 @@ function getPlaybackInfo(player, query.AutoOpenLiveStream = false; } - if (audioStreamIndex != null) { - query.AudioStreamIndex = audioStreamIndex; + if (options.audioStreamIndex != null) { + query.AudioStreamIndex = options.audioStreamIndex; } - if (subtitleStreamIndex != null) { - query.SubtitleStreamIndex = subtitleStreamIndex; + if (options.subtitleStreamIndex != null) { + query.SubtitleStreamIndex = options.subtitleStreamIndex; } - if (secondarySubtitleStreamIndex != null) { - query.SecondarySubtitleStreamIndex = secondarySubtitleStreamIndex; + if (options.secondarySubtitleStreamIndex != null) { + query.SecondarySubtitleStreamIndex = options.secondarySubtitleStreamIndex; } - if (enableDirectPlay != null) { - query.EnableDirectPlay = enableDirectPlay; + if (options.enableDirectPlay != null) { + query.EnableDirectPlay = options.enableDirectPlay; } - - if (enableDirectStream != null) { - query.EnableDirectStream = enableDirectStream; + if (options.enableDirectStream != null) { + query.EnableDirectStream = options.enableDirectStream; } - if (allowVideoStreamCopy != null) { - query.AllowVideoStreamCopy = allowVideoStreamCopy; + if (options.allowVideoStreamCopy != null) { + query.AllowVideoStreamCopy = options.allowVideoStreamCopy; } - if (allowAudioStreamCopy != null) { - query.AllowAudioStreamCopy = allowAudioStreamCopy; + if (options.allowAudioStreamCopy != null) { + query.AllowAudioStreamCopy = options.allowAudioStreamCopy; } if (mediaSourceId) { query.MediaSourceId = mediaSourceId; @@ -485,8 +470,8 @@ function getPlaybackInfo(player, if (liveStreamId) { query.LiveStreamId = liveStreamId; } - if (maxBitrate) { - query.MaxStreamingBitrate = maxBitrate; + if (options.maxBitrate) { + query.MaxStreamingBitrate = options.maxBitrate; } if (player.enableMediaProbe && !player.enableMediaProbe(item)) { query.EnableMediaProbe = false; @@ -537,7 +522,7 @@ function getOptimalMediaSource(apiClient, item, versions) { }); } -function getLiveStream(player, apiClient, item, playSessionId, deviceProfile, maxBitrate, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex) { +function getLiveStream(player, apiClient, item, playSessionId, deviceProfile, mediaSource, options) { const postData = { DeviceProfile: deviceProfile, OpenToken: mediaSource.OpenToken @@ -545,19 +530,19 @@ function getLiveStream(player, apiClient, item, playSessionId, deviceProfile, ma const query = { UserId: apiClient.getCurrentUserId(), - StartTimeTicks: startPosition || 0, + StartTimeTicks: options.startPosition || 0, ItemId: item.Id, PlaySessionId: playSessionId }; - if (maxBitrate) { - query.MaxStreamingBitrate = maxBitrate; + if (options.maxBitrate) { + query.MaxStreamingBitrate = options.maxBitrate; } - if (audioStreamIndex != null) { - query.AudioStreamIndex = audioStreamIndex; + if (options.audioStreamIndex != null) { + query.AudioStreamIndex = options.audioStreamIndex; } - if (subtitleStreamIndex != null) { - query.SubtitleStreamIndex = subtitleStreamIndex; + if (options.subtitleStreamIndex != null) { + query.SubtitleStreamIndex = options.subtitleStreamIndex; } // lastly, enforce player overrides for special situations @@ -1706,7 +1691,7 @@ class PlaybackManager { function changeStream(player, ticks, params) { if (canPlayerSeek(player) && params == null) { - player.currentTime(parseInt(ticks / 10000)); + player.currentTime(parseInt(ticks / 10000, 10)); return; } @@ -1730,20 +1715,33 @@ class PlaybackManager { const apiClient = ServerConnections.getApiClient(currentItem.ServerId); if (ticks) { - ticks = parseInt(ticks); + ticks = parseInt(ticks, 10); } const maxBitrate = params.MaxStreamingBitrate || self.getMaxStreamingBitrate(player); const currentPlayOptions = currentItem.playOptions || getDefaultPlayOptions(); - getPlaybackInfo(player, apiClient, currentItem, deviceProfile, maxBitrate, ticks, true, currentMediaSource.Id, audioStreamIndex, subtitleStreamIndex, liveStreamId, params.EnableDirectPlay, params.EnableDirectStream, params.AllowVideoStreamCopy, params.AllowAudioStreamCopy).then(function (result) { + const options = { + maxBitrate, + startPosition: ticks, + isPlayback: true, + audioStreamIndex, + subtitleStreamIndex, + enableDirectPlay: params.EnableDirectPlay, + enableDirectStream: params.EnableDirectStream, + allowVideoStreamCopy: params.AllowVideoStreamCopy, + allowAudioStreamCopy: params.AllowAudioStreamCopy + }; + + getPlaybackInfo(player, apiClient, currentItem, deviceProfile, currentMediaSource.Id, liveStreamId, options).then(function (result) { if (validatePlaybackInfoResult(self, result)) { currentMediaSource = result.MediaSources[0]; const streamInfo = createStreamInfo(apiClient, currentItem.MediaType, currentItem, currentMediaSource, ticks, player); streamInfo.fullscreen = currentPlayOptions.fullscreen; streamInfo.lastMediaInfoQuery = lastMediaInfoQuery; + streamInfo.resetSubtitleOffset = false; if (!streamInfo.url) { showPlaybackInfoErrorMessage(self, 'PlaybackErrorNoCompatibleStream'); @@ -2268,7 +2266,7 @@ class PlaybackManager { function runInterceptors(item, playOptions) { return new Promise(function (resolve, reject) { - const interceptors = pluginManager.ofType('preplayintercept'); + const interceptors = pluginManager.ofType(PluginType.PreplayIntercept); interceptors.sort(function (a, b) { return (a.order || 0) - (b.order || 0); @@ -2303,17 +2301,17 @@ class PlaybackManager { }, reject); } - function sendPlaybackListToPlayer(player, items, deviceProfile, maxBitrate, apiClient, startPositionTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, startIndex) { - return setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositionTicks).then(function () { + function sendPlaybackListToPlayer(player, items, deviceProfile, apiClient, mediaSourceId, options) { + return setStreamUrls(items, deviceProfile, options.maxBitrate, apiClient, options.startPosition).then(function () { loading.hide(); return player.play({ - items: items, - startPositionTicks: startPositionTicks || 0, - mediaSourceId: mediaSourceId, - audioStreamIndex: audioStreamIndex, - subtitleStreamIndex: subtitleStreamIndex, - startIndex: startIndex + items, + startPositionTicks: options.startPosition || 0, + mediaSourceId, + audioStreamIndex: options.audioStreamIndex, + subtitleStreamIndex: options.subtitleStreamIndex, + startIndex: options.startIndex }); }); } @@ -2474,15 +2472,27 @@ class PlaybackManager { const mediaSourceId = playOptions.mediaSourceId; const audioStreamIndex = playOptions.audioStreamIndex; const subtitleStreamIndex = playOptions.subtitleStreamIndex; + const options = { + maxBitrate, + startPosition, + isPlayback: null, + audioStreamIndex, + subtitleStreamIndex, + startIndex: playOptions.startIndex, + enableDirectPlay: null, + enableDirectStream: null, + allowVideoStreamCopy: null, + allowAudioStreamCopy: null + }; if (player && !enableLocalPlaylistManagement(player)) { - return sendPlaybackListToPlayer(player, playOptions.items, deviceProfile, maxBitrate, apiClient, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex, playOptions.startIndex); + return sendPlaybackListToPlayer(player, playOptions.items, deviceProfile, apiClient, mediaSourceId, options); } // this reference was only needed by sendPlaybackListToPlayer playOptions.items = null; - return getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex).then(async (mediaSource) => { + return getPlaybackMediaSource(player, apiClient, deviceProfile, item, mediaSourceId, options).then(async (mediaSource) => { const user = await apiClient.getCurrentUser(); autoSetNextTracks(prevSource, mediaSource, user.Configuration.RememberAudioSelections, user.Configuration.RememberSubtitleSelections); @@ -2540,7 +2550,20 @@ class PlaybackManager { const maxBitrate = getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType); return player.getDeviceProfile(item).then(function (deviceProfile) { - return getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, options.mediaSourceId, options.audioStreamIndex, options.subtitleStreamIndex).then(function (mediaSource) { + const mediaOptions = { + maxBitrate, + startPosition, + isPlayback: null, + audioStreamIndex: options.audioStreamIndex, + subtitleStreamIndex: options.subtitleStreamIndex, + startIndex: null, + enableDirectPlay: null, + enableDirectStream: null, + allowVideoStreamCopy: null, + allowAudioStreamCopy: null + }; + + return getPlaybackMediaSource(player, apiClient, deviceProfile, item, options.mediaSourceId, mediaOptions).then(function (mediaSource) { return createStreamInfo(apiClient, item.MediaType, item, mediaSource, startPosition, player); }); }); @@ -2560,7 +2583,19 @@ class PlaybackManager { const maxBitrate = getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType); return player.getDeviceProfile(item).then(function (deviceProfile) { - return getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, false, null, null, null, null).then(function (playbackInfoResult) { + const mediaOptions = { + maxBitrate, + startPosition, + isPlayback: true, + audioStreamIndex: null, + subtitleStreamIndex: null, + enableDirectPlay: null, + enableDirectStream: null, + allowVideoStreamCopy: null, + allowAudioStreamCopy: null + }; + + return getPlaybackInfo(player, apiClient, item, deviceProfile, null, null, mediaOptions).then(function (playbackInfoResult) { return playbackInfoResult.MediaSources; }); }); @@ -2701,13 +2736,18 @@ class PlaybackManager { return tracks; } - function getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex) { - return getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, true, mediaSourceId, audioStreamIndex, subtitleStreamIndex, null).then(function (playbackInfoResult) { + function getPlaybackMediaSource(player, apiClient, deviceProfile, item, mediaSourceId, options) { + options.isPlayback = true; + + return getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, null, options).then(function (playbackInfoResult) { if (validatePlaybackInfoResult(self, playbackInfoResult)) { return getOptimalMediaSource(apiClient, item, playbackInfoResult.MediaSources).then(function (mediaSource) { if (mediaSource) { if (mediaSource.RequiresOpening && !mediaSource.LiveStreamId) { - return getLiveStream(player, apiClient, item, playbackInfoResult.PlaySessionId, deviceProfile, maxBitrate, startPosition, mediaSource, null, null).then(function (openLiveStreamResult) { + options.audioStreamIndex = null; + options.subtitleStreamIndex = null; + + return getLiveStream(player, apiClient, item, playbackInfoResult.PlaySessionId, deviceProfile, mediaSource, options).then(function (openLiveStreamResult) { return supportsDirectPlay(apiClient, item, openLiveStreamResult.MediaSource).then(function (result) { openLiveStreamResult.MediaSource.enableDirectPlay = result; return openLiveStreamResult.MediaSource; @@ -3387,12 +3427,12 @@ class PlaybackManager { } Events.on(pluginManager, 'registered', function (e, plugin) { - if (plugin.type === 'mediaplayer') { + if (plugin.type === PluginType.MediaPlayer) { initMediaPlayer(plugin); } }); - pluginManager.ofType('mediaplayer').forEach(initMediaPlayer); + pluginManager.ofType(PluginType.MediaPlayer).forEach(initMediaPlayer); function sendProgressUpdate(player, progressEventName, reportPlaylist) { if (!player) { @@ -3423,12 +3463,6 @@ class PlaybackManager { streamInfo.lastMediaInfoQuery = new Date().getTime(); - const apiClient = ServerConnections.getApiClient(serverId); - - if (!apiClient.isMinServerVersion('3.2.70.7')) { - return; - } - ServerConnections.getApiClient(serverId).getLiveStreamMediaInfo(liveStreamId).then(function (info) { mediaSource.MediaStreams = info.MediaStreams; Events.trigger(player, 'mediastreamschange'); @@ -3613,7 +3647,7 @@ class PlaybackManager { percent /= 100; ticks *= percent; - this.seek(parseInt(ticks), player); + this.seek(parseInt(ticks, 10), player); } seekMs(ms, player = this._currentPlayer) { @@ -4000,13 +4034,13 @@ class PlaybackManager { this.setBrightness(cmd.Arguments.Brightness, player); break; case 'SetAudioStreamIndex': - this.setAudioStreamIndex(parseInt(cmd.Arguments.Index), player); + this.setAudioStreamIndex(parseInt(cmd.Arguments.Index, 10), player); break; case 'SetSubtitleStreamIndex': - this.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index), player); + this.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index, 10), player); break; case 'SetMaxStreamingBitrate': - this.setMaxStreamingBitrate(parseInt(cmd.Arguments.Bitrate), player); + this.setMaxStreamingBitrate(parseInt(cmd.Arguments.Bitrate, 10), player); break; case 'ToggleFullscreen': this.toggleFullscreen(player); diff --git a/src/components/playback/playersettingsmenu.js b/src/components/playback/playersettingsmenu.js index c7f8f5a6da..da3be20970 100644 --- a/src/components/playback/playersettingsmenu.js +++ b/src/components/playback/playersettingsmenu.js @@ -43,7 +43,7 @@ function showQualityMenu(player, btn) { items: menuItems, positionTo: btn }).then(function (id) { - const bitrate = parseInt(id); + const bitrate = parseInt(id, 10); if (bitrate !== selectedBitrate) { playbackManager.setMaxStreamingBitrate({ enableAutomaticBitrateDetection: bitrate ? false : true, diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index a1ed503372..cc27b9beec 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -1,5 +1,3 @@ -/*eslint prefer-const: "error"*/ - let currentId = 0; function addUniquePlaylistItemId(item) { if (!item.PlaylistItemId) { diff --git a/src/components/pluginManager.js b/src/components/pluginManager.js index 80899d8bed..763f0eb2d1 100644 --- a/src/components/pluginManager.js +++ b/src/components/pluginManager.js @@ -8,6 +8,8 @@ import { appRouter } from '../components/appRouter'; import * as inputManager from '../scripts/inputManager'; import toast from '../components/toast/toast'; import confirm from '../components/confirm/confirm'; +import * as dashboard from '../utils/dashboard'; +import ServerConnections from '../components/ServerConnections'; // TODO: replace with each plugin version const cacheParam = new Date().getTime(); @@ -86,7 +88,9 @@ class PluginManager { appRouter, inputManager, toast, - confirm + confirm, + dashboard, + ServerConnections }); } else { console.debug(`Loading plugin (via dynamic import): ${pluginSpec}`); diff --git a/src/components/recordingcreator/recordingeditor.js b/src/components/recordingcreator/recordingeditor.js index 19cec8d88e..b55754f540 100644 --- a/src/components/recordingcreator/recordingeditor.js +++ b/src/components/recordingcreator/recordingeditor.js @@ -4,7 +4,7 @@ import globalize from '../../scripts/globalize'; import layoutManager from '../layoutManager'; import loading from '../loading/loading'; import scrollHelper from '../../scripts/scrollHelper'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import '../../elements/emby-button/emby-button'; import '../../elements/emby-collapse/emby-collapse'; import '../../elements/emby-input/emby-input'; @@ -12,7 +12,7 @@ import '../../elements/emby-button/paper-icon-button-light'; import '../formdialog.scss'; import './recordingcreator.scss'; import 'material-design-icons-iconfont'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import ServerConnections from '../ServerConnections'; import template from './recordingeditor.template.html'; diff --git a/src/components/recordingcreator/recordingfields.js b/src/components/recordingcreator/recordingfields.js index 4b912392ad..742a77df4b 100644 --- a/src/components/recordingcreator/recordingfields.js +++ b/src/components/recordingcreator/recordingfields.js @@ -7,13 +7,11 @@ import recordingHelper from './recordinghelper'; import '../../elements/emby-button/emby-button'; import '../../elements/emby-button/paper-icon-button-light'; import './recordingfields.scss'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import ServerConnections from '../ServerConnections'; import toast from '../toast/toast'; import template from './recordingfields.template.html'; -/*eslint prefer-const: "error"*/ - function loadData(parent, program) { if (program.IsSeries) { parent.querySelector('.recordSeriesContainer').classList.remove('hide'); diff --git a/src/components/recordingcreator/recordinghelper.js b/src/components/recordingcreator/recordinghelper.js index 1780668afd..a65ae006dc 100644 --- a/src/components/recordingcreator/recordinghelper.js +++ b/src/components/recordingcreator/recordinghelper.js @@ -5,8 +5,6 @@ import toast from '../toast/toast'; import confirm from '../confirm/confirm'; import dialog from '../dialog/dialog'; -/*eslint prefer-const: "error"*/ - function changeRecordingToSeries(apiClient, timerId, programId, confirmTimerCancellation) { loading.show(); diff --git a/src/components/recordingcreator/seriesrecordingeditor.js b/src/components/recordingcreator/seriesrecordingeditor.js index a649b1231b..79d35b9fe1 100644 --- a/src/components/recordingcreator/seriesrecordingeditor.js +++ b/src/components/recordingcreator/seriesrecordingeditor.js @@ -4,7 +4,7 @@ import layoutManager from '../layoutManager'; import loading from '../loading/loading'; import scrollHelper from '../../scripts/scrollHelper'; import datetime from '../../scripts/datetime'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import '../../elements/emby-button/emby-button'; import '../../elements/emby-checkbox/emby-checkbox'; import '../../elements/emby-input/emby-input'; @@ -13,12 +13,10 @@ import '../../elements/emby-button/paper-icon-button-light'; import '../formdialog.scss'; import './recordingcreator.scss'; import 'material-design-icons-iconfont'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import ServerConnections from '../ServerConnections'; import template from './seriesrecordingeditor.template.html'; -/*eslint prefer-const: "error"*/ - let currentDialog; let recordingUpdated = false; let recordingDeleted = false; diff --git a/src/components/refreshdialog/refreshdialog.js b/src/components/refreshdialog/refreshdialog.js index 27ce6b97cf..eac336389f 100644 --- a/src/components/refreshdialog/refreshdialog.js +++ b/src/components/refreshdialog/refreshdialog.js @@ -13,8 +13,6 @@ import '../formdialog.scss'; import ServerConnections from '../ServerConnections'; import toast from '../toast/toast'; -/*eslint prefer-const: "error"*/ - function getEditorHtml() { let html = ''; diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index cce2cf7b43..26b7220bce 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -20,8 +20,6 @@ import ServerConnections from '../ServerConnections'; import toast from '../toast/toast'; import { appRouter } from '../appRouter'; -/*eslint prefer-const: "error"*/ - let showMuteButton = true; let showVolumeSlider = true; @@ -46,7 +44,7 @@ function showAudioMenu(context, player, button) { items: menuItems, positionTo: button, callback: function (id) { - playbackManager.setAudioStreamIndex(parseInt(id), player); + playbackManager.setAudioStreamIndex(parseInt(id, 10), player); } }); }); @@ -78,7 +76,7 @@ function showSubtitleMenu(context, player, button) { items: menuItems, positionTo: button, callback: function (id) { - playbackManager.setSubtitleStreamIndex(parseInt(id), player); + playbackManager.setSubtitleStreamIndex(parseInt(id, 10), player); } }); }); diff --git a/src/components/search/SearchFields.tsx b/src/components/search/SearchFields.tsx index 68cf02f38d..8a0fa01dfd 100644 --- a/src/components/search/SearchFields.tsx +++ b/src/components/search/SearchFields.tsx @@ -7,7 +7,7 @@ import globalize from '../../scripts/globalize'; import 'material-design-icons-iconfont'; import '../../elements/emby-input/emby-input'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import './searchfields.scss'; import layoutManager from '../layoutManager'; import browser from '../../scripts/browser'; diff --git a/src/components/shortcuts.js b/src/components/shortcuts.js index bf8d1b74fe..60e51c784c 100644 --- a/src/components/shortcuts.js +++ b/src/components/shortcuts.js @@ -150,7 +150,7 @@ import toast from './toast/toast'; StartDate: card.getAttribute('data-startdate'), EndDate: card.getAttribute('data-enddate'), UserData: { - PlaybackPositionTicks: parseInt(card.getAttribute('data-positionticks') || '0') + PlaybackPositionTicks: parseInt(card.getAttribute('data-positionticks') || '0', 10) } }; } @@ -201,7 +201,7 @@ import toast from './toast/toast'; ServerId: serverId }); } else if (action === 'play' || action === 'resume') { - const startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0'); + const startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0', 10); if (playbackManager.canPlay(item)) { playbackManager.play({ diff --git a/src/components/sortmenu/sortmenu.js b/src/components/sortmenu/sortmenu.js index 394ced6dcb..65aef222e9 100644 --- a/src/components/sortmenu/sortmenu.js +++ b/src/components/sortmenu/sortmenu.js @@ -7,7 +7,7 @@ import '../../elements/emby-button/paper-icon-button-light'; import 'material-design-icons-iconfont'; import '../formdialog.scss'; import '../../elements/emby-button/emby-button'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import template from './sortmenu.template.html'; function onSubmit(e) { diff --git a/src/components/subtitleeditor/subtitleeditor.js b/src/components/subtitleeditor/subtitleeditor.js index 1aa6d57fc6..b332e4238f 100644 --- a/src/components/subtitleeditor/subtitleeditor.js +++ b/src/components/subtitleeditor/subtitleeditor.js @@ -14,7 +14,7 @@ import '../formdialog.scss'; import 'material-design-icons-iconfont'; import './subtitleeditor.scss'; import '../../elements/emby-button/emby-button'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import ServerConnections from '../ServerConnections'; import toast from '../toast/toast'; import confirm from '../confirm/confirm'; diff --git a/src/components/subtitlesettings/subtitlesettings.js b/src/components/subtitlesettings/subtitlesettings.js index 6cbced92d2..971b8c775d 100644 --- a/src/components/subtitlesettings/subtitlesettings.js +++ b/src/components/subtitlesettings/subtitlesettings.js @@ -13,7 +13,7 @@ import '../../elements/emby-select/emby-select'; import '../../elements/emby-slider/emby-slider'; import '../../elements/emby-input/emby-input'; import '../../elements/emby-checkbox/emby-checkbox'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import './subtitlesettings.scss'; import ServerConnections from '../ServerConnections'; import toast from '../toast/toast'; diff --git a/src/components/subtitlesettings/subtitlesettings.template.html b/src/components/subtitlesettings/subtitlesettings.template.html index 685b03997e..5f1f14ae9d 100644 --- a/src/components/subtitlesettings/subtitlesettings.template.html +++ b/src/components/subtitlesettings/subtitlesettings.template.html @@ -101,14 +101,16 @@
diff --git a/src/components/tvproviders/schedulesdirect.js b/src/components/tvproviders/schedulesdirect.js index d34fa40932..ca6f7a6e84 100644 --- a/src/components/tvproviders/schedulesdirect.js +++ b/src/components/tvproviders/schedulesdirect.js @@ -7,7 +7,7 @@ import '../listview/listview.scss'; import '../../elements/emby-button/paper-icon-button-light'; import '../../elements/emby-select/emby-select'; import '../../elements/emby-button/emby-button'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import './style.scss'; import Dashboard from '../../utils/dashboard'; import Events from '../../utils/events.ts'; diff --git a/src/components/upnextdialog/upnextdialog.js b/src/components/upnextdialog/upnextdialog.js index 31bb695f3e..2a37fcbe45 100644 --- a/src/components/upnextdialog/upnextdialog.js +++ b/src/components/upnextdialog/upnextdialog.js @@ -8,7 +8,7 @@ import globalize from '../../scripts/globalize'; import itemHelper from '../itemHelper'; import './upnextdialog.scss'; import '../../elements/emby-button/emby-button'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; /* eslint-disable indent */ diff --git a/src/components/upnextdialog/upnextdialog.scss b/src/components/upnextdialog/upnextdialog.scss index 08c4904bb5..fd2ae8d054 100644 --- a/src/components/upnextdialog/upnextdialog.scss +++ b/src/components/upnextdialog/upnextdialog.scss @@ -28,9 +28,9 @@ .upNextDialog-countdownText { font-weight: 500; + white-space: nowrap; } -.upNextDialog-nextVideoText, .upNextDialog-title { width: 25.5em; white-space: nowrap; diff --git a/src/components/userdatabuttons/userdatabuttons.js b/src/components/userdatabuttons/userdatabuttons.js index c170a7cc0f..f75bbce81e 100644 --- a/src/components/userdatabuttons/userdatabuttons.js +++ b/src/components/userdatabuttons/userdatabuttons.js @@ -12,7 +12,10 @@ const userDataMethods = { markFavorite: markFavorite }; -function getUserDataButtonHtml(method, itemId, serverId, buttonCssClass, iconCssClass, icon, tooltip, style) { +function getUserDataButtonHtml(method, itemId, serverId, icon, tooltip, style, classes) { + let buttonCssClass = classes.buttonCssClass; + let iconCssClass = classes.iconCssClass; + if (style === 'fab-mini') { style = 'fab'; buttonCssClass = buttonCssClass ? (buttonCssClass + ' mini') : 'mini'; @@ -96,7 +99,7 @@ function getIconsHtml(options) { } const iconCssClass = options.iconCssClass; - + const classes = { buttonCssClass: btnCssClass, iconCssClass: iconCssClass }; const serverId = item.ServerId; if (includePlayed !== false) { @@ -104,18 +107,21 @@ function getIconsHtml(options) { if (itemHelper.canMarkPlayed(item)) { if (userData.Played) { - html += getUserDataButtonHtml('markPlayed', itemId, serverId, btnCssClass + ' btnUserDataOn', iconCssClass, 'check', tooltipPlayed, style); + const buttonCssClass = classes.buttonCssClass + ' btnUserDataOn'; + html += getUserDataButtonHtml('markPlayed', itemId, serverId, 'check', tooltipPlayed, style, { buttonCssClass, ...classes }); } else { - html += getUserDataButtonHtml('markPlayed', itemId, serverId, btnCssClass, iconCssClass, 'check', tooltipPlayed, style); + html += getUserDataButtonHtml('markPlayed', itemId, serverId, 'check', tooltipPlayed, style, classes); } } } const tooltipFavorite = globalize.translate('Favorite'); if (userData.IsFavorite) { - html += getUserDataButtonHtml('markFavorite', itemId, serverId, btnCssClass + ' btnUserData btnUserDataOn', iconCssClass, 'favorite', tooltipFavorite, style); + const buttonCssClass = classes.buttonCssClass + ' btnUserData btnUserDataOn'; + html += getUserDataButtonHtml('markFavorite', itemId, serverId, 'favorite', tooltipFavorite, style, { buttonCssClass, ...classes }); } else { - html += getUserDataButtonHtml('markFavorite', itemId, serverId, btnCssClass + ' btnUserData', iconCssClass, 'favorite', tooltipFavorite, style); + classes.buttonCssClass += ' btnUserData'; + html += getUserDataButtonHtml('markFavorite', itemId, serverId, 'favorite', tooltipFavorite, style, classes); } return html; diff --git a/src/components/viewSettings/viewSettings.js b/src/components/viewSettings/viewSettings.js index 8dfa589aa2..f5cb864206 100644 --- a/src/components/viewSettings/viewSettings.js +++ b/src/components/viewSettings/viewSettings.js @@ -9,7 +9,7 @@ import '../../elements/emby-button/paper-icon-button-light'; import '../../elements/emby-select/emby-select'; import 'material-design-icons-iconfont'; import '../formdialog.scss'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import template from './viewSettings.template.html'; function onSubmit(e) { diff --git a/src/controllers/dashboard/dashboard.js b/src/controllers/dashboard/dashboard.js index 87704d406f..fd98d163e9 100644 --- a/src/controllers/dashboard/dashboard.js +++ b/src/controllers/dashboard/dashboard.js @@ -16,7 +16,7 @@ import imageHelper from '../../scripts/imagehelper'; import indicators from '../../components/indicators/indicators'; import '../../components/listview/listview.scss'; import '../../elements/emby-button/emby-button'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import '../../elements/emby-itemscontainer/emby-itemscontainer'; import taskButton from '../../scripts/taskbutton'; import Dashboard from '../../utils/dashboard'; diff --git a/src/controllers/dashboard/devices/devices.js b/src/controllers/dashboard/devices/devices.js index 3c3a039745..537e842d90 100644 --- a/src/controllers/dashboard/devices/devices.js +++ b/src/controllers/dashboard/devices/devices.js @@ -54,15 +54,11 @@ import confirm from '../../../components/confirm/confirm'; } function showDeviceMenu(view, btn, deviceId) { - const menuItems = []; - - if (canEdit) { - menuItems.push({ - name: globalize.translate('Edit'), - id: 'open', - icon: 'mode_edit' - }); - } + const menuItems = [{ + name: globalize.translate('Edit'), + id: 'open', + icon: 'mode_edit' + }]; if (canDelete(deviceId)) { menuItems.push({ @@ -100,7 +96,7 @@ import confirm from '../../../components/confirm/confirm'; deviceHtml += '
'; deviceHtml += ''; deviceHtml += '
'; - if (canEdit || canDelete(device.Id)) { + if (canDelete(device.Id)) { if (globalize.getIsRTL()) deviceHtml += '
'; else @@ -155,7 +151,6 @@ import confirm from '../../../components/confirm/confirm'; }); } - const canEdit = ApiClient.isMinServerVersion('3.4.1.31'); export default function (view) { view.querySelector('.devicesList').addEventListener('click', function (e) { const btnDeviceMenu = dom.parentWithClass(e.target, 'btnDeviceMenu'); diff --git a/src/controllers/dashboard/dlna/profile.js b/src/controllers/dashboard/dlna/profile.js index 3f5d6c365e..4d942f7cdc 100644 --- a/src/controllers/dashboard/dlna/profile.js +++ b/src/controllers/dashboard/dlna/profile.js @@ -100,7 +100,7 @@ import { getParameterByName } from '../../../utils/url.ts'; }).join('') + '
'; const elem = $('.httpHeaderIdentificationList', page).html(html).trigger('create'); $('.btnDeleteIdentificationHeader', elem).on('click', function () { - const itemIndex = parseInt(this.getAttribute('data-index')); + const itemIndex = parseInt(this.getAttribute('data-index'), 10); currentProfile.Identification.Headers.splice(itemIndex, 1); renderIdentificationHeaders(page, currentProfile.Identification.Headers); }); @@ -154,7 +154,7 @@ import { getParameterByName } from '../../../utils/url.ts'; }).join('') + '
'; const elem = $('.xmlDocumentAttributeList', page).html(html).trigger('create'); $('.btnDeleteXmlAttribute', elem).on('click', function () { - const itemIndex = parseInt(this.getAttribute('data-index')); + const itemIndex = parseInt(this.getAttribute('data-index'), 10); currentProfile.XmlRootAttributes.splice(itemIndex, 1); renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes); }); @@ -198,12 +198,12 @@ import { getParameterByName } from '../../../utils/url.ts'; }).join('') + '
'; const elem = $('.subtitleProfileList', page).html(html).trigger('create'); $('.btnDeleteProfile', elem).on('click', function () { - const itemIndex = parseInt(this.getAttribute('data-index')); + const itemIndex = parseInt(this.getAttribute('data-index'), 10); currentProfile.SubtitleProfiles.splice(itemIndex, 1); renderSubtitleProfiles(page, currentProfile.SubtitleProfiles); }); $('.lnkEditSubProfile', elem).on('click', function () { - const itemIndex = parseInt(this.getAttribute('data-index')); + const itemIndex = parseInt(this.getAttribute('data-index'), 10); editSubtitleProfile(page, currentProfile.SubtitleProfiles[itemIndex]); }); } @@ -292,7 +292,7 @@ import { getParameterByName } from '../../../utils/url.ts'; deleteDirectPlayProfile(page, index); }); $('.lnkEditSubProfile', elem).on('click', function () { - const index = parseInt(this.getAttribute('data-profileindex')); + const index = parseInt(this.getAttribute('data-profileindex'), 10); editDirectPlayProfile(page, currentProfile.DirectPlayProfiles[index]); }); } @@ -353,7 +353,7 @@ import { getParameterByName } from '../../../utils/url.ts'; deleteTranscodingProfile(page, index); }); $('.lnkEditSubProfile', elem).on('click', function () { - const index = parseInt(this.getAttribute('data-profileindex')); + const index = parseInt(this.getAttribute('data-profileindex'), 10); editTranscodingProfile(page, currentProfile.TranscodingProfiles[index]); }); } @@ -437,7 +437,7 @@ import { getParameterByName } from '../../../utils/url.ts'; deleteContainerProfile(page, index); }); $('.lnkEditSubProfile', elem).on('click', function () { - const index = parseInt(this.getAttribute('data-profileindex')); + const index = parseInt(this.getAttribute('data-profileindex'), 10); editContainerProfile(page, currentProfile.ContainerProfiles[index]); }); } @@ -509,7 +509,7 @@ import { getParameterByName } from '../../../utils/url.ts'; deleteCodecProfile(page, index); }); $('.lnkEditSubProfile', elem).on('click', function () { - const index = parseInt(this.getAttribute('data-profileindex')); + const index = parseInt(this.getAttribute('data-profileindex'), 10); editCodecProfile(page, currentProfile.CodecProfiles[index]); }); } @@ -589,7 +589,7 @@ import { getParameterByName } from '../../../utils/url.ts'; deleteResponseProfile(page, index); }); $('.lnkEditSubProfile', elem).on('click', function () { - const index = parseInt(this.getAttribute('data-profileindex')); + const index = parseInt(this.getAttribute('data-profileindex'), 10); editResponseProfile(page, currentProfile.ResponseProfiles[index]); }); } diff --git a/src/controllers/dashboard/encodingsettings.js b/src/controllers/dashboard/encodingsettings.js index 67001ccb82..b8c89726cd 100644 --- a/src/controllers/dashboard/encodingsettings.js +++ b/src/controllers/dashboard/encodingsettings.js @@ -98,8 +98,8 @@ import alert from '../../components/alert'; config.VppTonemappingBrightness = form.querySelector('#txtVppTonemappingBrightness').value; config.VppTonemappingContrast = form.querySelector('#txtVppTonemappingContrast').value; config.EncoderPreset = form.querySelector('#selectEncoderPreset').value; - config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0'); - config.H265Crf = parseInt(form.querySelector('#txtH265Crf').value || '0'); + config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0', 10); + config.H265Crf = parseInt(form.querySelector('#txtH265Crf').value || '0', 10); config.DeinterlaceMethod = form.querySelector('#selectDeinterlaceMethod').value; config.DeinterlaceDoubleRate = form.querySelector('#chkDoubleRateDeinterlacing').checked; config.EnableSubtitleExtraction = form.querySelector('#chkEnableSubtitleExtraction').checked; diff --git a/src/controllers/dashboard/library.js b/src/controllers/dashboard/library.js index 24e7a477d1..49ff203902 100644 --- a/src/controllers/dashboard/library.js +++ b/src/controllers/dashboard/library.js @@ -92,7 +92,7 @@ import cardBuilder from '../../components/cardbuilder/cardBuilder'; function showCardMenu(page, elem, virtualFolders) { const card = dom.parentWithClass(elem, 'card'); - const index = parseInt(card.getAttribute('data-index')); + const index = parseInt(card.getAttribute('data-index'), 10); const virtualFolder = virtualFolders[index]; const menuItems = []; menuItems.push({ @@ -192,7 +192,7 @@ import cardBuilder from '../../components/cardbuilder/cardBuilder'; }); $('.editLibrary', divVirtualFolders).on('click', function () { const card = $(this).parents('.card')[0]; - const index = parseInt(card.getAttribute('data-index')); + const index = parseInt(card.getAttribute('data-index'), 10); const virtualFolder = virtualFolders[index]; if (virtualFolder.ItemId) { diff --git a/src/controllers/dashboard/logs.js b/src/controllers/dashboard/logs.js index aa6436962d..23a6d43853 100644 --- a/src/controllers/dashboard/logs.js +++ b/src/controllers/dashboard/logs.js @@ -3,7 +3,7 @@ import loading from '../../components/loading/loading'; import globalize from '../../scripts/globalize'; import '../../elements/emby-button/emby-button'; import '../../components/listview/listview.scss'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import Dashboard from '../../utils/dashboard'; import alert from '../../components/alert'; diff --git a/src/controllers/dashboard/metadataimages.html b/src/controllers/dashboard/metadataimages.html index d0de7cb738..a407ae387b 100644 --- a/src/controllers/dashboard/metadataimages.html +++ b/src/controllers/dashboard/metadataimages.html @@ -26,7 +26,7 @@
${LabelDummyChapterDurationHelp}
- +
${LabelDummyChapterCountHelp}
diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 5e0a2d3f14..150fe3e1fd 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -68,7 +68,7 @@ function renderPackage(pkg, installedPlugins, page) { if (installedPlugin) { const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '' + installedPlugin.Version + ''); - $('#pCurrentVersion', page).show().text(currentVersionText); + $('#pCurrentVersion', page).show().html(currentVersionText); } else { $('#pCurrentVersion', page).hide().text(''); } diff --git a/src/controllers/dashboard/scheduledtasks/scheduledtask.js b/src/controllers/dashboard/scheduledtasks/scheduledtask.js index c326a689fc..64db2e4292 100644 --- a/src/controllers/dashboard/scheduledtasks/scheduledtask.js +++ b/src/controllers/dashboard/scheduledtasks/scheduledtask.js @@ -235,7 +235,7 @@ import { getParameterByName } from '../../../utils/url.ts'; const btnDeleteTrigger = dom.parentWithClass(e.target, 'btnDeleteTrigger'); if (btnDeleteTrigger) { - ScheduledTaskPage.confirmDeleteTrigger(view, parseInt(btnDeleteTrigger.getAttribute('data-index'))); + ScheduledTaskPage.confirmDeleteTrigger(view, parseInt(btnDeleteTrigger.getAttribute('data-index'), 10)); } }); view.addEventListener('viewshow', function () { diff --git a/src/controllers/dashboard/streaming.js b/src/controllers/dashboard/streaming.js index bc640a1f68..8832e4bff4 100644 --- a/src/controllers/dashboard/streaming.js +++ b/src/controllers/dashboard/streaming.js @@ -15,7 +15,7 @@ import Dashboard from '../../utils/dashboard'; loading.show(); const form = this; ApiClient.getServerConfiguration().then(function (config) { - config.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', form).val() || '0')); + config.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', form).val() || '0'), 10); ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult); }); diff --git a/src/controllers/itemDetails/index.js b/src/controllers/itemDetails/index.js index 4c2c429829..5c59483291 100644 --- a/src/controllers/itemDetails/index.js +++ b/src/controllers/itemDetails/index.js @@ -1,5 +1,6 @@ import { intervalToDuration } from 'date-fns'; import DOMPurify from 'dompurify'; +import { marked } from 'marked'; import escapeHtml from 'escape-html'; import isEqual from 'lodash-es/isEqual'; @@ -22,7 +23,7 @@ import libraryMenu from '../../scripts/libraryMenu'; import globalize from '../../scripts/globalize'; import browser from '../../scripts/browser'; import { playbackManager } from '../../components/playback/playbackmanager'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import '../../elements/emby-itemscontainer/emby-itemscontainer'; import '../../elements/emby-checkbox/emby-checkbox'; import '../../elements/emby-button/emby-button'; @@ -877,7 +878,7 @@ function renderOverview(page, item) { const overviewElements = page.querySelectorAll('.overview'); if (overviewElements.length > 0) { - const overview = DOMPurify.sanitize(item.Overview || ''); + const overview = DOMPurify.sanitize(marked(item.Overview || '')); if (overview) { for (const overviewElemnt of overviewElements) { @@ -1063,7 +1064,7 @@ function renderTagline(page, item) { } } -function renderDetails(page, item, apiClient, context, isStatic) { +function renderDetails(page, item, apiClient, context) { renderSimilarItems(page, item, context); renderMoreFromSeason(page, item, apiClient); renderMoreFromArtist(page, item, apiClient); @@ -1083,7 +1084,7 @@ function renderDetails(page, item, apiClient, context, isStatic) { } renderTags(page, item); - renderSeriesAirTime(page, item, isStatic); + renderSeriesAirTime(page, item); } function enableScrollX() { @@ -1156,12 +1157,7 @@ function renderMoreFromArtist(view, item, apiClient) { const section = view.querySelector('.moreFromArtistSection'); if (section) { - if (item.Type === 'MusicArtist') { - if (!apiClient.isMinServerVersion('3.4.1.19')) { - section.classList.add('hide'); - return; - } - } else if (item.Type !== 'MusicAlbum' || !item.AlbumArtists || !item.AlbumArtists.length) { + if (item.Type !== 'MusicArtist' && (item.Type !== 'MusicAlbum' || !item.AlbumArtists || !item.AlbumArtists.length)) { section.classList.add('hide'); return; } @@ -1262,7 +1258,7 @@ function renderSimilarItems(page, item, context) { } } -function renderSeriesAirTime(page, item, isStatic) { +function renderSeriesAirTime(page, item) { const seriesAirTime = page.querySelector('#seriesAirTime'); if (item.Type != 'Series') { seriesAirTime.classList.add('hide'); @@ -1281,19 +1277,6 @@ function renderSeriesAirTime(page, item, isStatic) { if (item.AirTime) { html += ' at ' + item.AirTime; } - if (item.Studios.length) { - if (isStatic) { - html += ' on ' + escapeHtml(item.Studios[0].Name); - } else { - const context = inferContext(item); - const href = appRouter.getRouteUrl(item.Studios[0], { - context: context, - itemType: 'Studio', - serverId: item.ServerId - }); - html += ' on ' + escapeHtml(item.Studios[0].Name) + ''; - } - } if (html) { html = (item.Status == 'Ended' ? 'Aired ' : 'Airs ') + html; seriesAirTime.innerHTML = html; diff --git a/src/controllers/livetv/livetvsuggested.js b/src/controllers/livetv/livetvsuggested.js index f2a63f3ecd..8b97984d0a 100644 --- a/src/controllers/livetv/livetvsuggested.js +++ b/src/controllers/livetv/livetvsuggested.js @@ -6,7 +6,7 @@ import globalize from '../../scripts/globalize'; import * as mainTabsManager from '../../components/maintabsmanager'; import cardBuilder from '../../components/cardbuilder/cardBuilder'; import imageLoader from '../../components/images/imageLoader'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import '../../elements/emby-itemscontainer/emby-itemscontainer'; import '../../elements/emby-tabs/emby-tabs'; import '../../elements/emby-button/emby-button'; @@ -222,17 +222,17 @@ export default function (view, params) { } function onBeforeTabChange(evt) { - preLoadTab(view, parseInt(evt.detail.selectedTabIndex)); + preLoadTab(view, parseInt(evt.detail.selectedTabIndex, 10)); } function onTabChange(evt) { - const previousTabController = tabControllers[parseInt(evt.detail.previousIndex)]; + const previousTabController = tabControllers[parseInt(evt.detail.previousIndex, 10)]; if (previousTabController && previousTabController.onHide) { previousTabController.onHide(); } - loadTab(view, parseInt(evt.detail.selectedTabIndex)); + loadTab(view, parseInt(evt.detail.selectedTabIndex, 10)); } function getTabContainers() { @@ -339,7 +339,7 @@ export default function (view, params) { let isViewRestored; const self = this; - let currentTabIndex = parseInt(params.tab || getDefaultTabIndex('livetv')); + let currentTabIndex = parseInt(params.tab || getDefaultTabIndex('livetv'), 10); let initialTabIndex = currentTabIndex; let lastFullRender = 0; [].forEach.call(view.querySelectorAll('.sectionTitleTextButton-programs'), function (link) { diff --git a/src/controllers/livetvstatus.js b/src/controllers/livetvstatus.js index d771464b1d..8b2b4bebc3 100644 --- a/src/controllers/livetvstatus.js +++ b/src/controllers/livetvstatus.js @@ -7,7 +7,7 @@ import layoutManager from '../components/layoutManager'; import loading from '../components/loading/loading'; import browser from '../scripts/browser'; import '../components/listview/listview.scss'; -import '../assets/css/flexstyles.scss'; +import '../styles/flexstyles.scss'; import '../elements/emby-itemscontainer/emby-itemscontainer'; import '../components/cardbuilder/card.scss'; import 'material-design-icons-iconfont'; diff --git a/src/controllers/music/musicrecommended.js b/src/controllers/music/musicrecommended.js index 238b7a6d60..bbb154a4e1 100644 --- a/src/controllers/music/musicrecommended.js +++ b/src/controllers/music/musicrecommended.js @@ -9,11 +9,11 @@ import imageLoader from '../../components/images/imageLoader'; import libraryMenu from '../../scripts/libraryMenu'; import * as mainTabsManager from '../../components/maintabsmanager'; import globalize from '../../scripts/globalize'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import '../../elements/emby-itemscontainer/emby-itemscontainer'; import '../../elements/emby-tabs/emby-tabs'; import '../../elements/emby-button/emby-button'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import Dashboard from '../../utils/dashboard'; /* eslint-disable indent */ @@ -245,11 +245,11 @@ import Dashboard from '../../utils/dashboard'; } function onBeforeTabChange(e) { - preLoadTab(view, parseInt(e.detail.selectedTabIndex)); + preLoadTab(view, parseInt(e.detail.selectedTabIndex, 10)); } function onTabChange(e) { - loadTab(view, parseInt(e.detail.selectedTabIndex)); + loadTab(view, parseInt(e.detail.selectedTabIndex, 10)); } function getTabContainers() { @@ -350,7 +350,7 @@ import Dashboard from '../../utils/dashboard'; } } - let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId)); + let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId), 10); const suggestionsTabIndex = 1; this.initTab = function () { diff --git a/src/controllers/playback/video/index.js b/src/controllers/playback/video/index.js index 19c1492d0e..d7bbab581d 100644 --- a/src/controllers/playback/video/index.js +++ b/src/controllers/playback/video/index.js @@ -15,10 +15,10 @@ import { appHost } from '../../../components/apphost'; import layoutManager from '../../../components/layoutManager'; import * as userSettings from '../../../scripts/settings/userSettings'; import keyboardnavigation from '../../../scripts/keyboardNavigation'; -import '../../../assets/css/scrollstyles.scss'; +import '../../../styles/scrollstyles.scss'; import '../../../elements/emby-slider/emby-slider'; import '../../../elements/emby-button/paper-icon-button-light'; -import '../../../assets/css/videoosd.scss'; +import '../../../styles/videoosd.scss'; import ServerConnections from '../../../components/ServerConnections'; import shell from '../../../scripts/shell'; import SubtitleSync from '../../../components/subtitlesync/subtitlesync'; @@ -975,7 +975,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components title: globalize.translate('Audio'), positionTo: positionTo }).then(function (id) { - const index = parseInt(id); + const index = parseInt(id, 10); if (index !== currentIndex) { playbackManager.setAudioStreamIndex(index, player); @@ -1022,7 +1022,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components positionTo }).then(function (id) { if (id) { - const index = parseInt(id); + const index = parseInt(id, 10); if (index !== currentIndex) { playbackManager.setSecondarySubtitleStreamIndex(index, player); } @@ -1099,7 +1099,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components console.error(e); } } else { - const index = parseInt(id); + const index = parseInt(id, 10); if (index !== currentIndex) { playbackManager.setSubtitleStreamIndex(index, player); @@ -1633,7 +1633,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components ms /= 100; ms *= value; ms += programStartDateMs; - return '

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

'; + return '

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

'; } return '--:--'; diff --git a/src/controllers/session/selectServer/index.js b/src/controllers/session/selectServer/index.js index 4665b2eba0..b45cb71060 100644 --- a/src/controllers/session/selectServer/index.js +++ b/src/controllers/session/selectServer/index.js @@ -10,7 +10,7 @@ import actionSheet from '../../../components/actionSheet/actionSheet'; import dom from '../../../scripts/dom'; import browser from '../../../scripts/browser'; import 'material-design-icons-iconfont'; -import '../../../assets/css/flexstyles.scss'; +import '../../../styles/flexstyles.scss'; import '../../../elements/emby-scroller/emby-scroller'; import '../../../elements/emby-itemscontainer/emby-itemscontainer'; import '../../../components/cardbuilder/card.scss'; diff --git a/src/controllers/shows/tvrecommended.js b/src/controllers/shows/tvrecommended.js index 1977b7cd4f..d1e53b38de 100644 --- a/src/controllers/shows/tvrecommended.js +++ b/src/controllers/shows/tvrecommended.js @@ -9,7 +9,7 @@ import cardBuilder from '../../components/cardbuilder/cardBuilder'; import { playbackManager } from '../../components/playback/playbackmanager'; import * as mainTabsManager from '../../components/maintabsmanager'; import globalize from '../../scripts/globalize'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import '../../elements/emby-itemscontainer/emby-itemscontainer'; import '../../elements/emby-button/emby-button'; import Dashboard from '../../utils/dashboard'; @@ -224,11 +224,11 @@ import autoFocuser from '../../components/autoFocuser'; export default function (view, params) { function onBeforeTabChange(e) { - preLoadTab(view, parseInt(e.detail.selectedTabIndex)); + preLoadTab(view, parseInt(e.detail.selectedTabIndex, 10)); } function onTabChange(e) { - const newIndex = parseInt(e.detail.selectedTabIndex); + const newIndex = parseInt(e.detail.selectedTabIndex, 10); loadTab(view, newIndex); } @@ -340,7 +340,7 @@ import autoFocuser from '../../components/autoFocuser'; } const self = this; - let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId)); + let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId), 10); const suggestionsTabIndex = 1; self.initTab = function () { diff --git a/src/controllers/shows/tvupcoming.js b/src/controllers/shows/tvupcoming.js index 0c1be00a18..fa6be1f356 100644 --- a/src/controllers/shows/tvupcoming.js +++ b/src/controllers/shows/tvupcoming.js @@ -4,7 +4,7 @@ import datetime from '../../scripts/datetime'; import cardBuilder from '../../components/cardbuilder/cardBuilder'; import imageLoader from '../../components/images/imageLoader'; import globalize from '../../scripts/globalize'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import '../../elements/emby-itemscontainer/emby-itemscontainer'; /* eslint-disable indent */ diff --git a/src/controllers/wizard/user/index.js b/src/controllers/wizard/user/index.js index 9d1f13ae31..d6647f7357 100644 --- a/src/controllers/wizard/user/index.js +++ b/src/controllers/wizard/user/index.js @@ -1,6 +1,6 @@ import loading from '../../../components/loading/loading'; import globalize from '../../../scripts/globalize'; -import '../../../assets/css/dashboard.scss'; +import '../../../styles/dashboard.scss'; import '../../../elements/emby-input/emby-input'; import '../../../elements/emby-button/emby-button'; import Dashboard from '../../../utils/dashboard'; diff --git a/src/elements/emby-itemscontainer/emby-itemscontainer.js b/src/elements/emby-itemscontainer/emby-itemscontainer.js index 00e2e94d3a..ae0f0fef54 100644 --- a/src/elements/emby-itemscontainer/emby-itemscontainer.js +++ b/src/elements/emby-itemscontainer/emby-itemscontainer.js @@ -414,7 +414,7 @@ import Sortable from 'sortablejs'; clearRefreshInterval(itemsContainer); if (!intervalMs) { - intervalMs = parseInt(itemsContainer.getAttribute('data-refreshinterval') || '0'); + intervalMs = parseInt(itemsContainer.getAttribute('data-refreshinterval') || '0', 10); } if (intervalMs) { diff --git a/src/elements/emby-progressbar/emby-progressbar.js b/src/elements/emby-progressbar/emby-progressbar.js index e232bbcde0..cad2f4bbfa 100644 --- a/src/elements/emby-progressbar/emby-progressbar.js +++ b/src/elements/emby-progressbar/emby-progressbar.js @@ -3,8 +3,8 @@ const ProgressBarPrototype = Object.create(HTMLDivElement.prototype); function onAutoTimeProgress() { - const start = parseInt(this.getAttribute('data-starttime')); - const end = parseInt(this.getAttribute('data-endtime')); + const start = parseInt(this.getAttribute('data-starttime'), 10); + const end = parseInt(this.getAttribute('data-endtime'), 10); const now = new Date().getTime(); const total = end - start; diff --git a/src/elements/emby-scrollbuttons/emby-scrollbuttons.js b/src/elements/emby-scrollbuttons/emby-scrollbuttons.js index e7fa1df29f..3d98918d7a 100644 --- a/src/elements/emby-scrollbuttons/emby-scrollbuttons.js +++ b/src/elements/emby-scrollbuttons/emby-scrollbuttons.js @@ -91,7 +91,7 @@ const EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype); return 0; } - value = parseInt(value); + value = parseInt(value, 10); if (isNaN(value)) { return 0; } diff --git a/src/elements/emby-scroller/Scroller.tsx b/src/elements/emby-scroller/Scroller.tsx index 6cd83fc784..14313318a6 100644 --- a/src/elements/emby-scroller/Scroller.tsx +++ b/src/elements/emby-scroller/Scroller.tsx @@ -75,7 +75,7 @@ const Scroller: FC = ({ return 0; } - if (isNaN(parseInt(value))) { + if (isNaN(parseInt(value, 10))) { return 0; } diff --git a/src/elements/emby-tabs/emby-tabs.js b/src/elements/emby-tabs/emby-tabs.js index 723cb7e8fe..7c07796f74 100644 --- a/src/elements/emby-tabs/emby-tabs.js +++ b/src/elements/emby-tabs/emby-tabs.js @@ -5,7 +5,7 @@ import browser from '../../scripts/browser'; import focusManager from '../../components/focusManager'; import layoutManager from '../../components/layoutManager'; import './emby-tabs.scss'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; /* eslint-disable indent */ const EmbyTabs = Object.create(HTMLDivElement.prototype); @@ -75,11 +75,11 @@ import '../../assets/css/scrollstyles.scss'; current.classList.remove(activeButtonClass); } - const previousIndex = current ? parseInt(current.getAttribute('data-index')) : null; + const previousIndex = current ? parseInt(current.getAttribute('data-index'), 10) : null; setActiveTabButton(tabButton); - const index = parseInt(tabButton.getAttribute('data-index')); + const index = parseInt(tabButton.getAttribute('data-index'), 10); triggerBeforeTabChange(tabs, index, previousIndex); @@ -194,7 +194,7 @@ import '../../assets/css/scrollstyles.scss'; initScroller(this); const current = this.querySelector('.' + activeButtonClass); - const currentIndex = current ? parseInt(current.getAttribute('data-index')) : parseInt(this.getAttribute('data-index') || '0'); + const currentIndex = current ? parseInt(current.getAttribute('data-index'), 10) : parseInt(this.getAttribute('data-index') || '0', 10); if (currentIndex !== -1) { this.selectedTabIndex = currentIndex; diff --git a/src/elements/emby-textarea/emby-textarea.js b/src/elements/emby-textarea/emby-textarea.js index 649578ce16..ed48f415ff 100644 --- a/src/elements/emby-textarea/emby-textarea.js +++ b/src/elements/emby-textarea/emby-textarea.js @@ -14,7 +14,7 @@ function calculateOffset(textarea) { let offset = 0; for (let i = 0; i < props.length; i++) { - offset += parseInt(style[props[i]]); + offset += parseInt(style[props[i]], 10); } return offset; } diff --git a/src/index.jsx b/src/index.jsx index b9a4d9603e..08b3bfada3 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -6,7 +6,7 @@ import 'intersection-observer'; import 'classlist.js'; import 'whatwg-fetch'; import 'resize-observer-polyfill'; -import './assets/css/site.scss'; +import './styles/site.scss'; import React, { StrictMode } from 'react'; import * as ReactDOM from 'react-dom'; import Events from './utils/events.ts'; @@ -24,7 +24,6 @@ import { appRouter, history } from './components/appRouter'; import './elements/emby-button/emby-button'; import './scripts/autoThemes'; import './scripts/libraryMenu'; -import './scripts/routes'; import './components/themeMediaPlayer'; import './scripts/autoBackdrops'; import { pageClassOn, serverAddress } from './utils/dashboard'; @@ -39,6 +38,10 @@ import { currentSettings } from './scripts/settings/userSettings'; import taskButton from './scripts/taskbutton'; import App from './App.tsx'; +import './styles/livetv.scss'; +import './styles/dashboard.scss'; +import './styles/detailtable.scss'; + function loadCoreDictionary() { const languages = ['af', 'ar', 'be-by', 'bg-bg', 'bn_bd', 'ca', 'cs', 'cy', 'da', 'de', 'el', 'en-gb', 'en-us', 'eo', 'es', 'es_419', 'es-ar', 'es_do', 'es-mx', 'et', 'eu', 'fa', 'fi', 'fil', 'fr', 'fr-ca', 'gl', 'gsw', 'he', 'hi-in', 'hr', 'hu', 'id', 'it', 'ja', 'kk', 'ko', 'lt-lt', 'lv', 'mr', 'ms', 'nb', 'nl', 'nn', 'pl', 'pr', 'pt', 'pt-br', 'pt-pt', 'ro', 'ru', 'sk', 'sl-si', 'sq', 'sv', 'ta', 'th', 'tr', 'uk', 'ur_pk', 'vi', 'zh-cn', 'zh-hk', 'zh-tw']; const translations = languages.map(function (language) { @@ -89,13 +92,13 @@ function onGlobalizeInit() { if (browser.tv && !browser.android) { console.debug('using system fonts with explicit sizes'); - import('./assets/css/fonts.sized.scss'); + import('./styles/fonts.sized.scss'); } else { console.debug('using default fonts'); - import('./assets/css/fonts.scss'); + import('./styles/fonts.scss'); } - import('./assets/css/librarybrowser.scss'); + import('./styles/librarybrowser.scss'); loadPlugins().then(onAppReady); } @@ -135,7 +138,7 @@ async function onAppReady() { console.debug('onAppReady: loading dependencies'); if (browser.iOS) { - import('./assets/css/ios.scss'); + import('./styles/ios.scss'); } Events.on(appHost, 'resume', () => { diff --git a/src/libraries/navdrawer/navdrawer.js b/src/libraries/navdrawer/navdrawer.js index bd27b6e55c..539682a076 100644 --- a/src/libraries/navdrawer/navdrawer.js +++ b/src/libraries/navdrawer/navdrawer.js @@ -6,7 +6,7 @@ import browser from '../../scripts/browser'; import dom from '../../scripts/dom'; import './navdrawer.scss'; -import '../../assets/css/scrollstyles.scss'; +import '../../styles/scrollstyles.scss'; import globalize from '../../scripts/globalize'; function getTouches(e) { diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index c388c52fe8..670a46f4b9 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -8,7 +8,7 @@ import layoutManager from '../components/layoutManager'; import dom from '../scripts/dom'; import focusManager from '../components/focusManager'; import ResizeObserver from 'resize-observer-polyfill'; -import '../assets/css/scrollstyles.scss'; +import '../styles/scrollstyles.scss'; import globalize from '../scripts/globalize'; /** diff --git a/src/plugins/backdropScreensaver/plugin.js b/src/plugins/backdropScreensaver/plugin.js index 7db319a34e..823ab7d7e0 100644 --- a/src/plugins/backdropScreensaver/plugin.js +++ b/src/plugins/backdropScreensaver/plugin.js @@ -1,10 +1,11 @@ /* eslint-disable indent */ import ServerConnections from '../../components/ServerConnections'; +import { PluginType } from '../../types/plugin.ts'; class BackdropScreensaver { constructor() { this.name = 'Backdrop ScreenSaver'; - this.type = 'screensaver'; + this.type = PluginType.Screensaver; this.id = 'backdropscreensaver'; this.supportsAnonymous = false; } diff --git a/src/plugins/bookPlayer/plugin.js b/src/plugins/bookPlayer/plugin.js index e95c8657b1..df5e213b72 100644 --- a/src/plugins/bookPlayer/plugin.js +++ b/src/plugins/bookPlayer/plugin.js @@ -9,6 +9,7 @@ import TableOfContents from './tableOfContents'; import dom from '../../scripts/dom'; import { translateHtml } from '../../scripts/globalize'; import * as userSettings from '../../scripts/settings/userSettings'; +import { PluginType } from '../../types/plugin.ts'; import Events from '../../utils/events.ts'; import '../../elements/emby-button/paper-icon-button-light'; @@ -19,7 +20,7 @@ import './style.scss'; export class BookPlayer { constructor() { this.name = 'Book Player'; - this.type = 'mediaplayer'; + this.type = PluginType.MediaPlayer; this.id = 'bookplayer'; this.priority = 1; diff --git a/src/plugins/chromecastPlayer/plugin.js b/src/plugins/chromecastPlayer/plugin.js index 9044cd3e30..95bee3838b 100644 --- a/src/plugins/chromecastPlayer/plugin.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -5,6 +5,7 @@ import globalize from '../../scripts/globalize'; import castSenderApiLoader from './castSenderApi'; import ServerConnections from '../../components/ServerConnections'; import alert from '../../components/alert'; +import { PluginType } from '../../types/plugin.ts'; import Events from '../../utils/events.ts'; // Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js @@ -569,7 +570,7 @@ class ChromecastPlayer { constructor() { // playbackManager needs this this.name = PlayerName; - this.type = 'mediaplayer'; + this.type = PluginType.MediaPlayer; this.id = 'chromecast'; this.isLocalPlayer = false; this.lastPlayerData = {}; @@ -683,7 +684,7 @@ class ChromecastPlayer { } seek(position) { - position = parseInt(position); + position = parseInt(position, 10); position = position / 10000000; diff --git a/src/plugins/comicsPlayer/plugin.js b/src/plugins/comicsPlayer/plugin.js index d209069ad3..eb6d3cb11f 100644 --- a/src/plugins/comicsPlayer/plugin.js +++ b/src/plugins/comicsPlayer/plugin.js @@ -6,13 +6,14 @@ import keyboardnavigation from '../../scripts/keyboardNavigation'; import { appRouter } from '../../components/appRouter'; import ServerConnections from '../../components/ServerConnections'; import * as userSettings from '../../scripts/settings/userSettings'; +import { PluginType } from '../../types/plugin.ts'; import './style.scss'; export class ComicsPlayer { constructor() { this.name = 'Comics Player'; - this.type = 'mediaplayer'; + this.type = PluginType.MediaPlayer; this.id = 'comicsplayer'; this.priority = 1; this.imageMap = new Map(); diff --git a/src/plugins/experimentalWarnings/plugin.js b/src/plugins/experimentalWarnings/plugin.js index 563e6264f2..395f906890 100644 --- a/src/plugins/experimentalWarnings/plugin.js +++ b/src/plugins/experimentalWarnings/plugin.js @@ -2,6 +2,7 @@ import globalize from '../../scripts/globalize'; import * as userSettings from '../../scripts/settings/userSettings'; import { appHost } from '../../components/apphost'; import alert from '../../components/alert'; +import { PluginType } from '../../types/plugin.ts'; // TODO: Replace with date-fns // https://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php @@ -46,7 +47,7 @@ function showIsoMessage() { class ExpirementalPlaybackWarnings { constructor() { this.name = 'Experimental playback warnings'; - this.type = 'preplayintercept'; + this.type = PluginType.PreplayIntercept; this.id = 'expirementalplaybackwarnings'; } diff --git a/src/plugins/htmlAudioPlayer/plugin.js b/src/plugins/htmlAudioPlayer/plugin.js index ee6f98ece1..1a306a6c7f 100644 --- a/src/plugins/htmlAudioPlayer/plugin.js +++ b/src/plugins/htmlAudioPlayer/plugin.js @@ -3,6 +3,7 @@ import { appHost } from '../../components/apphost'; import * as htmlMediaHelper from '../../components/htmlMediaHelper'; import profileBuilder from '../../scripts/browserDeviceProfile'; import { getIncludeCorsCredentials } from '../../scripts/settings/webSettings'; +import { PluginType } from '../../types/plugin.ts'; import Events from '../../utils/events.ts'; function getDefaultProfile() { @@ -48,6 +49,9 @@ function supportsFade() { function requireHlsPlayer(callback) { import('hls.js').then(({ default: hls }) => { + hls.DefaultConfig.lowLatencyMode = false; + hls.DefaultConfig.backBufferLength = Infinity; + hls.DefaultConfig.liveBackBufferLength = 90; window.Hls = hls; callback(); }); @@ -85,7 +89,7 @@ class HtmlAudioPlayer { const self = this; self.name = 'Html Audio Player'; - self.type = 'mediaplayer'; + self.type = PluginType.MediaPlayer; self.id = 'htmlaudioplayer'; // Let any players created by plugins take priority diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index f2a2b8d30b..e624fcd941 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -30,8 +30,10 @@ import ServerConnections from '../../components/ServerConnections'; import profileBuilder, { canPlaySecondaryAudio } from '../../scripts/browserDeviceProfile'; import { getIncludeCorsCredentials } from '../../scripts/settings/webSettings'; import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../components/backdrop/backdrop'; +import { PluginType } from '../../types/plugin.ts'; import Events from '../../utils/events.ts'; import { includesAny } from '../../utils/container.ts'; +import debounce from 'lodash-es/debounce'; /** * Returns resolved URL. @@ -106,6 +108,9 @@ function tryRemoveElement(elem) { function requireHlsPlayer(callback) { import('hls.js').then(({default: hls}) => { + hls.DefaultConfig.lowLatencyMode = false; + hls.DefaultConfig.backBufferLength = Infinity; + hls.DefaultConfig.liveBackBufferLength = 90; window.Hls = hls; callback(); }); @@ -166,7 +171,7 @@ function tryRemoveElement(elem) { /** * @type {string} */ - type = 'mediaplayer'; + type = PluginType.MediaPlayer; /** * @type {string} */ @@ -378,7 +383,7 @@ function tryRemoveElement(elem) { this.#currentTime = null; - this.resetSubtitleOffset(); + if (options.resetSubtitleOffset !== false) this.resetSubtitleOffset(); return this.createMediaElement(options).then(elem => { return this.updateVideoUrl(options).then(() => { @@ -571,7 +576,12 @@ function tryRemoveElement(elem) { } } - setSubtitleOffset(offset) { + setSubtitleOffset = debounce(this._setSubtitleOffset, 100); + + /** + * @private + */ + _setSubtitleOffset(offset) { const offsetValue = parseFloat(offset); // if .ass currently rendering @@ -620,6 +630,41 @@ function tryRemoveElement(elem) { return relativeOffset; } + /** + * @private + * These browsers will not clear the existing active cue when setting an offset + * for native TextTracks. + * Any previous text tracks that are on the screen when the offset changes will remain next + * to the new tracks until they reach the end time of the new offset's instance of the track. + */ + requiresHidingActiveCuesOnOffsetChange() { + return !!browser.firefox; + } + + /** + * @private + */ + hideTextTrackWithActiveCues(currentTrack) { + if (currentTrack.activeCues) { + currentTrack.mode = 'hidden'; + } + } + + /** + * Forces the active cue to clear by disabling then re-enabling the track. + * The track mode is reverted inside of a 0ms timeout to free up the track + * and allow it to disable and clear the active cue. + * @private + */ + forceClearTextTrackActiveCues(currentTrack) { + if (currentTrack.activeCues) { + currentTrack.mode = 'disabled'; + setTimeout(() => { + currentTrack.mode = 'showing'; + }, 0); + } + } + /** * @private */ @@ -629,11 +674,21 @@ function tryRemoveElement(elem) { if (offsetValue === 0) { return; } + + const shouldClearActiveCues = this.requiresHidingActiveCuesOnOffsetChange(); + if (shouldClearActiveCues) { + this.hideTextTrackWithActiveCues(currentTrack); + } + Array.from(currentTrack.cues) .forEach(function (cue) { cue.startTime -= offsetValue; cue.endTime -= offsetValue; }); + + if (shouldClearActiveCues) { + this.forceClearTextTrackActiveCues(currentTrack); + } } } @@ -771,6 +826,8 @@ function tryRemoveElement(elem) { } destroy() { + this.setSubtitleOffset.cancel(); + destroyHlsPlayer(this); destroyFlvPlayer(this); diff --git a/src/plugins/logoScreensaver/plugin.js b/src/plugins/logoScreensaver/plugin.js index 8d5f7031a5..b1168ab5bd 100644 --- a/src/plugins/logoScreensaver/plugin.js +++ b/src/plugins/logoScreensaver/plugin.js @@ -1,8 +1,10 @@ +import { PluginType } from '../../types/plugin.ts'; + export default function () { const self = this; self.name = 'Logo ScreenSaver'; - self.type = 'screensaver'; + self.type = PluginType.Screensaver; self.id = 'logoscreensaver'; self.supportsAnonymous = true; diff --git a/src/plugins/pdfPlayer/plugin.js b/src/plugins/pdfPlayer/plugin.js index 5f43d03fc9..1dc59ebdf8 100644 --- a/src/plugins/pdfPlayer/plugin.js +++ b/src/plugins/pdfPlayer/plugin.js @@ -4,6 +4,7 @@ import keyboardnavigation from '../../scripts/keyboardNavigation'; import dialogHelper from '../../components/dialogHelper/dialogHelper'; import dom from '../../scripts/dom'; import { appRouter } from '../../components/appRouter'; +import { PluginType } from '../../types/plugin.ts'; import Events from '../../utils/events.ts'; import './style.scss'; @@ -12,7 +13,7 @@ import '../../elements/emby-button/paper-icon-button-light'; export class PdfPlayer { constructor() { this.name = 'PDF Player'; - this.type = 'mediaplayer'; + this.type = PluginType.MediaPlayer; this.id = 'pdfplayer'; this.priority = 1; @@ -261,7 +262,7 @@ export class PdfPlayer { for (const page of pages) { if (!this.pages[page]) { this.pages[page] = document.createElement('canvas'); - this.renderPage(this.pages[page], parseInt(page.slice(4))); + this.renderPage(this.pages[page], parseInt(page.slice(4), 10)); } } diff --git a/src/plugins/photoPlayer/plugin.js b/src/plugins/photoPlayer/plugin.js index aa3ba5f364..7c41c53188 100644 --- a/src/plugins/photoPlayer/plugin.js +++ b/src/plugins/photoPlayer/plugin.js @@ -1,9 +1,10 @@ import ServerConnections from '../../components/ServerConnections'; +import { PluginType } from '../../types/plugin.ts'; export default class PhotoPlayer { constructor() { this.name = 'Photo Player'; - this.type = 'mediaplayer'; + this.type = PluginType.MediaPlayer; this.id = 'photoplayer'; this.priority = 1; } diff --git a/src/plugins/playAccessValidation/plugin.js b/src/plugins/playAccessValidation/plugin.js index c353ea9096..3081f8cf49 100644 --- a/src/plugins/playAccessValidation/plugin.js +++ b/src/plugins/playAccessValidation/plugin.js @@ -1,6 +1,7 @@ import globalize from '../../scripts/globalize'; import ServerConnections from '../../components/ServerConnections'; import alert from '../../components/alert'; +import { PluginType } from '../../types/plugin.ts'; function showErrorMessage() { return alert(globalize.translate('MessagePlayAccessRestricted')); @@ -9,7 +10,7 @@ function showErrorMessage() { class PlayAccessValidation { constructor() { this.name = 'Playback validation'; - this.type = 'preplayintercept'; + this.type = PluginType.PreplayIntercept; this.id = 'playaccessvalidation'; this.order = -2; } diff --git a/src/plugins/sessionPlayer/plugin.js b/src/plugins/sessionPlayer/plugin.js index a8ad32f791..98e0e843ab 100644 --- a/src/plugins/sessionPlayer/plugin.js +++ b/src/plugins/sessionPlayer/plugin.js @@ -1,6 +1,7 @@ import { playbackManager } from '../../components/playback/playbackmanager'; import serverNotifications from '../../scripts/serverNotifications'; import ServerConnections from '../../components/ServerConnections'; +import { PluginType } from '../../types/plugin.ts'; import Events from '../../utils/events.ts'; function getActivePlayerId() { @@ -181,7 +182,7 @@ class SessionPlayer { const self = this; this.name = 'Remote Control'; - this.type = 'mediaplayer'; + this.type = PluginType.MediaPlayer; this.isLocalPlayer = false; this.id = 'remoteplayer'; diff --git a/src/plugins/syncPlay/core/Manager.js b/src/plugins/syncPlay/core/Manager.js index 740e0709d9..ee1e3e3ae7 100644 --- a/src/plugins/syncPlay/core/Manager.js +++ b/src/plugins/syncPlay/core/Manager.js @@ -254,7 +254,7 @@ class Manager { if (typeof cmd.When === 'string') { cmd.When = new Date(cmd.When); cmd.EmittedAt = new Date(cmd.EmittedAt); - cmd.PositionTicks = cmd.PositionTicks ? parseInt(cmd.PositionTicks) : null; + cmd.PositionTicks = cmd.PositionTicks ? parseInt(cmd.PositionTicks, 10) : null; } if (!this.isSyncPlayEnabled()) { diff --git a/src/plugins/syncPlay/plugin.ts b/src/plugins/syncPlay/plugin.ts index e4ef965a0d..59c066aaf7 100644 --- a/src/plugins/syncPlay/plugin.ts +++ b/src/plugins/syncPlay/plugin.ts @@ -5,8 +5,9 @@ import SyncPlay from './core'; import SyncPlayNoActivePlayer from './ui/players/NoActivePlayer'; import SyncPlayHtmlVideoPlayer from './ui/players/HtmlVideoPlayer'; import SyncPlayHtmlAudioPlayer from './ui/players/HtmlAudioPlayer'; +import { Plugin, PluginType } from '../../types/plugin'; -class SyncPlayPlugin { +class SyncPlayPlugin implements Plugin { name: string; id: string; type: string; @@ -17,7 +18,7 @@ class SyncPlayPlugin { this.id = 'syncplay'; // NOTE: This should probably be a "mediaplayer" so the playback manager can handle playback logic, but // SyncPlay needs refactored so it does not have an independent playback manager. - this.type = 'syncplay'; + this.type = PluginType.SyncPlay; this.priority = 1; this.init(); diff --git a/src/plugins/youtubePlayer/plugin.js b/src/plugins/youtubePlayer/plugin.js index a9dd8e9beb..2a10f8fcdc 100644 --- a/src/plugins/youtubePlayer/plugin.js +++ b/src/plugins/youtubePlayer/plugin.js @@ -2,6 +2,7 @@ import browser from '../../scripts/browser'; import { appRouter } from '../../components/appRouter'; import loading from '../../components/loading/loading'; import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../components/backdrop/backdrop'; +import { PluginType } from '../../types/plugin.ts'; import Events from '../../utils/events.ts'; /* globals YT */ @@ -197,7 +198,7 @@ function setCurrentSrc(instance, elem, options) { class YoutubePlayer { constructor() { this.name = 'Youtube Player'; - this.type = 'mediaplayer'; + this.type = PluginType.MediaPlayer; this.id = 'youtubeplayer'; // Let any players created by plugins take priority diff --git a/src/routes/index.tsx b/src/routes/index.tsx index be24c811d8..06e3badaf3 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -4,7 +4,7 @@ import { Navigate, Route, Routes } from 'react-router-dom'; import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES, toAsyncPageRoute } from './asyncRoutes'; import ConnectionRequired from '../components/ConnectionRequired'; import ServerContentPage from '../components/ServerContentPage'; -import { LEGACY_ADMIN_ROUTES, LEGACY_USER_ROUTES, toViewManagerPageRoute } from './legacyRoutes'; +import { LEGACY_ADMIN_ROUTES, LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES, toViewManagerPageRoute } from './legacyRoutes'; const AppRoutes = () => ( @@ -28,6 +28,8 @@ const AppRoutes = () => ( {/* Public routes */} }> } /> + + {LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute)} {/* Suppress warnings for unhandled routes */} diff --git a/src/routes/legacyRoutes/index.tsx b/src/routes/legacyRoutes/index.tsx index b4e97250e9..8f498f98aa 100644 --- a/src/routes/legacyRoutes/index.tsx +++ b/src/routes/legacyRoutes/index.tsx @@ -21,4 +21,5 @@ export function toViewManagerPageRoute(route: LegacyRoute) { } export * from './admin'; +export * from './public'; export * from './user'; diff --git a/src/routes/legacyRoutes/public.ts b/src/routes/legacyRoutes/public.ts new file mode 100644 index 0000000000..0e05875520 --- /dev/null +++ b/src/routes/legacyRoutes/public.ts @@ -0,0 +1,81 @@ +import { LegacyRoute } from '.'; + +export const LEGACY_PUBLIC_ROUTES: LegacyRoute[] = [ + { + path: 'addserver.html', + pageProps: { + controller: 'session/addServer/index', + view: 'session/addServer/index.html' + } + }, + { + path: 'selectserver.html', + pageProps: { + controller: 'session/selectServer/index', + view: 'session/selectServer/index.html' + } + }, + { + path: 'login.html', + pageProps: { + controller: 'session/login/index', + view: 'session/login/index.html' + } + }, + { + path: 'forgotpassword.html', + pageProps: { + controller: 'session/forgotPassword/index', + view: 'session/forgotPassword/index.html' + } + }, + { + path: 'forgotpasswordpin.html', + pageProps: { + controller: 'session/resetPassword/index', + view: 'session/resetPassword/index.html' + } + }, + { + path: 'wizardremoteaccess.html', + pageProps: { + controller: 'wizard/remote/index', + view: 'wizard/remote/index.html' + } + }, + { + path: 'wizardfinish.html', + pageProps: { + controller: 'wizard/finish/index', + view: 'wizard/finish/index.html' + } + }, + { + path: 'wizardlibrary.html', + pageProps: { + controller: 'dashboard/library', + view: 'wizard/library.html' + } + }, + { + path: 'wizardsettings.html', + pageProps: { + controller: 'wizard/settings/index', + view: 'wizard/settings/index.html' + } + }, + { + path: 'wizardstart.html', + pageProps: { + controller: 'wizard/start/index', + view: 'wizard/start/index.html' + } + }, + { + path: 'wizarduser.html', + pageProps: { + controller: 'wizard/user/index', + view: 'wizard/user/index.html' + } + } +]; diff --git a/src/routes/movies/index.tsx b/src/routes/movies/index.tsx index 3b74b464d5..9b1405c9e5 100644 --- a/src/routes/movies/index.tsx +++ b/src/routes/movies/index.tsx @@ -55,7 +55,7 @@ const getTabs = () => { const Movies: FC = () => { const [ searchParams ] = useSearchParams(); - const currentTabIndex = parseInt(searchParams.get('tab') || getDefaultTabIndex(searchParams.get('topParentId')).toString()); + const currentTabIndex = parseInt(searchParams.get('tab') || getDefaultTabIndex(searchParams.get('topParentId')).toString(), 10); const [ selectedIndex, setSelectedIndex ] = useState(currentTabIndex); const element = useRef(null); @@ -95,7 +95,7 @@ const Movies: FC = () => { }; const onTabChange = useCallback((e: { detail: { selectedTabIndex: string; }; }) => { - const newIndex = parseInt(e.detail.selectedTabIndex); + const newIndex = parseInt(e.detail.selectedTabIndex, 10); setSelectedIndex(newIndex); }, []); diff --git a/src/routes/user/useredit.tsx b/src/routes/user/useredit.tsx index 5afa0c4fd8..1da3d8919c 100644 --- a/src/routes/user/useredit.tsx +++ b/src/routes/user/useredit.tsx @@ -228,8 +228,8 @@ const UserEdit: FunctionComponent = () => { user.Policy.EnableContentDownloading = (page.querySelector('.chkEnableDownloading') as HTMLInputElement).checked; user.Policy.EnableRemoteAccess = (page.querySelector('.chkRemoteAccess') as HTMLInputElement).checked; user.Policy.RemoteClientBitrateLimit = Math.floor(1e6 * parseFloat((page.querySelector('#txtRemoteClientBitrateLimit') as HTMLInputElement).value || '0')); - user.Policy.LoginAttemptsBeforeLockout = parseInt((page.querySelector('#txtLoginAttemptsBeforeLockout') as HTMLInputElement).value || '0'); - user.Policy.MaxActiveSessions = parseInt((page.querySelector('#txtMaxActiveSessions') as HTMLInputElement).value || '0'); + user.Policy.LoginAttemptsBeforeLockout = parseInt((page.querySelector('#txtLoginAttemptsBeforeLockout') as HTMLInputElement).value || '0', 10); + user.Policy.MaxActiveSessions = parseInt((page.querySelector('#txtMaxActiveSessions') as HTMLInputElement).value || '0', 10); user.Policy.AuthenticationProviderId = (page.querySelector('#selectLoginProvider') as HTMLSelectElement).value; user.Policy.PasswordResetProviderId = (page.querySelector('#selectPasswordResetProvider') as HTMLSelectElement).value; user.Policy.EnableContentDeletion = (page.querySelector('.chkEnableDeleteAllFolders') as HTMLInputElement).checked; diff --git a/src/routes/user/userprofiles.tsx b/src/routes/user/userprofiles.tsx index 7d46593ff6..ff39fae33f 100644 --- a/src/routes/user/userprofiles.tsx +++ b/src/routes/user/userprofiles.tsx @@ -11,7 +11,7 @@ import '../../elements/emby-button/emby-button'; import '../../elements/emby-button/paper-icon-button-light'; import '../../components/cardbuilder/card.scss'; import '../../components/indicators/indicators.scss'; -import '../../assets/css/flexstyles.scss'; +import '../../styles/flexstyles.scss'; import Page from '../../components/Page'; type MenuEntry = { diff --git a/src/scripts/browser.js b/src/scripts/browser.js index 2a28130150..a6042189dc 100644 --- a/src/scripts/browser.js +++ b/src/scripts/browser.js @@ -224,7 +224,7 @@ const uaMatch = function (ua) { version = version || match[2] || '0'; - let versionMajor = parseInt(version.split('.')[0]); + let versionMajor = parseInt(version.split('.')[0], 10); if (isNaN(versionMajor)) { versionMajor = 0; @@ -295,7 +295,7 @@ if (browser.web0s) { delete browser.safari; const v = (navigator.appVersion).match(/Tizen (\d+).(\d+)/); - browser.tizenVersion = parseInt(v[1]); + browser.tizenVersion = parseInt(v[1], 10); } else { browser.orsay = userAgent.toLowerCase().indexOf('smarthub') !== -1; } diff --git a/src/scripts/editorsidebar.js b/src/scripts/editorsidebar.js index 10a474798c..06624898d1 100644 --- a/src/scripts/editorsidebar.js +++ b/src/scripts/editorsidebar.js @@ -303,7 +303,7 @@ import { getParameterByName } from '../utils/url.ts'; updateEditorNode(this, item); }).on('pagebeforeshow', '.metadataEditorPage', function () { /* eslint-disable-next-line @babel/no-unused-expressions */ - import('../assets/css/metadataeditor.scss'); + import('../styles/metadataeditor.scss'); }).on('pagebeforeshow', '.metadataEditorPage', function () { const page = this; Dashboard.getCurrentUser().then(function (user) { diff --git a/src/scripts/keyboardNavigation.js b/src/scripts/keyboardNavigation.js index feef180630..39c187479d 100644 --- a/src/scripts/keyboardNavigation.js +++ b/src/scripts/keyboardNavigation.js @@ -50,6 +50,11 @@ const NavigationKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; */ const InteractiveElements = ['INPUT', 'TEXTAREA']; +/** + * Types of INPUT element for which navigation shouldn't be constrained. + */ +const NonInteractiveInputElements = ['button', 'checkbox', 'color', 'file', 'hidden', 'image', 'radio', 'reset', 'submit']; + let hasFieldKey = false; try { hasFieldKey = 'key' in new KeyboardEvent('keydown'); @@ -84,6 +89,24 @@ export function isNavigationKey(key) { return NavigationKeys.indexOf(key) != -1; } +/** + * Returns _true_ if the element is interactive. + * + * @param {Element} element - Element. + * @return {boolean} _true_ if the element is interactive. + */ +export function isInteractiveElement(element) { + if (element && InteractiveElements.includes(element.tagName)) { + if (element.tagName === 'INPUT') { + return !NonInteractiveInputElements.includes(element.type); + } + + return true; + } + + return false; +} + export function enable() { window.addEventListener('keydown', function (e) { const key = getKeyName(e); @@ -97,7 +120,7 @@ export function enable() { switch (key) { case 'ArrowLeft': - if (!InteractiveElements.includes(document.activeElement?.tagName)) { + if (!isInteractiveElement(document.activeElement)) { inputManager.handleCommand('left'); } else { capture = false; @@ -107,7 +130,7 @@ export function enable() { inputManager.handleCommand('up'); break; case 'ArrowRight': - if (!InteractiveElements.includes(document.activeElement?.tagName)) { + if (!isInteractiveElement(document.activeElement)) { inputManager.handleCommand('right'); } else { capture = false; diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js index 3afc730c33..77b38ab46a 100644 --- a/src/scripts/libraryMenu.js +++ b/src/scripts/libraryMenu.js @@ -16,14 +16,15 @@ import imageHelper from './imagehelper'; import { getMenuLinks } from '../scripts/settings/webSettings'; import Dashboard, { pageClassOn } from '../utils/dashboard'; import ServerConnections from '../components/ServerConnections'; +import { PluginType } from '../types/plugin.ts'; import Events from '../utils/events.ts'; import { getParameterByName } from '../utils/url.ts'; import '../elements/emby-button/paper-icon-button-light'; import 'material-design-icons-iconfont'; -import '../assets/css/scrollstyles.scss'; -import '../assets/css/flexstyles.scss'; +import '../styles/scrollstyles.scss'; +import '../styles/flexstyles.scss'; /* eslint-disable indent */ @@ -159,7 +160,7 @@ import '../assets/css/flexstyles.scss'; // Button is present headerSyncButton // SyncPlay plugin is loaded - && pluginManager.plugins.filter(plugin => plugin.id === 'syncplay').length > 0 + && pluginManager.ofType(PluginType.SyncPlay).length > 0 // SyncPlay enabled for user && policy?.SyncPlayAccess !== 'None' ) { diff --git a/src/scripts/routes.js b/src/scripts/routes.js deleted file mode 100644 index f1574dd53e..0000000000 --- a/src/scripts/routes.js +++ /dev/null @@ -1,123 +0,0 @@ -import '../elements/emby-button/emby-button'; -import '../elements/emby-input/emby-input'; -import '../scripts/livetvcomponents'; -import '../elements/emby-button/paper-icon-button-light'; -import '../elements/emby-itemscontainer/emby-itemscontainer'; -import '../elements/emby-collapse/emby-collapse'; -import '../elements/emby-select/emby-select'; -import '../elements/emby-checkbox/emby-checkbox'; -import '../elements/emby-slider/emby-slider'; -import '../assets/css/livetv.scss'; -import '../components/listview/listview.scss'; -import '../assets/css/dashboard.scss'; -import '../assets/css/detailtable.scss'; -import { appRouter } from '../components/appRouter'; - -/* eslint-disable indent */ - - console.groupCollapsed('defining core routes'); - - function defineRoute(newRoute) { - const path = newRoute.alias ? newRoute.alias : newRoute.path; - console.debug('defining route: ' + path); - newRoute.dictionary = 'core'; - appRouter.addRoute(path, newRoute); - } - - defineRoute({ - alias: '/addserver.html', - path: 'session/addServer/index.html', - autoFocus: false, - anonymous: true, - startup: true, - controller: 'session/addServer/index' - }); - - defineRoute({ - alias: '/selectserver.html', - path: 'session/selectServer/index.html', - autoFocus: false, - anonymous: true, - startup: true, - controller: 'session/selectServer/index', - type: 'selectserver' - }); - - defineRoute({ - alias: '/login.html', - path: 'session/login/index.html', - autoFocus: false, - anonymous: true, - startup: true, - controller: 'session/login/index', - type: 'login' - }); - - defineRoute({ - alias: '/forgotpassword.html', - path: 'session/forgotPassword/index.html', - anonymous: true, - startup: true, - controller: 'session/forgotPassword/index' - }); - - defineRoute({ - alias: '/forgotpasswordpin.html', - path: 'session/resetPassword/index.html', - autoFocus: false, - anonymous: true, - startup: true, - controller: 'session/resetPassword/index' - }); - - defineRoute({ - alias: '/wizardremoteaccess.html', - path: 'wizard/remote/index.html', - autoFocus: false, - anonymous: true, - controller: 'wizard/remote/index' - }); - - defineRoute({ - alias: '/wizardfinish.html', - path: 'wizard/finish/index.html', - autoFocus: false, - anonymous: true, - controller: 'wizard/finish/index' - }); - - defineRoute({ - alias: '/wizardlibrary.html', - path: 'wizard/library.html', - autoFocus: false, - anonymous: true, - controller: 'dashboard/library' - }); - - defineRoute({ - alias: '/wizardsettings.html', - path: 'wizard/settings/index.html', - autoFocus: false, - anonymous: true, - controller: 'wizard/settings/index' - }); - - defineRoute({ - alias: '/wizardstart.html', - path: 'wizard/start/index.html', - autoFocus: false, - anonymous: true, - controller: 'wizard/start/index' - }); - - defineRoute({ - alias: '/wizarduser.html', - path: 'wizard/user/index.html', - controller: 'wizard/user/index', - autoFocus: false, - anonymous: true - }); - - console.groupEnd('defining core routes'); - -/* eslint-enable indent */ diff --git a/src/scripts/screensavermanager.js b/src/scripts/screensavermanager.js index 4b0eedaa51..c63d530685 100644 --- a/src/scripts/screensavermanager.js +++ b/src/scripts/screensavermanager.js @@ -3,6 +3,7 @@ import { pluginManager } from '../components/pluginManager'; import inputManager from './inputManager'; import * as userSettings from './settings/userSettings'; import ServerConnections from '../components/ServerConnections'; +import { PluginType } from '../types/plugin.ts'; import Events from '../utils/events.ts'; import './screensavermanager.scss'; @@ -34,7 +35,7 @@ function getScreensaverPlugin(isLoggedIn) { option = isLoggedIn ? 'backdropscreensaver' : 'logoscreensaver'; } - const plugins = pluginManager.ofType('screensaver'); + const plugins = pluginManager.ofType(PluginType.Screensaver); for (const plugin of plugins) { if (plugin.id === option) { diff --git a/src/scripts/scrollHelper.js b/src/scripts/scrollHelper.js index 57c8f28fb7..725df86434 100644 --- a/src/scripts/scrollHelper.js +++ b/src/scripts/scrollHelper.js @@ -1,6 +1,6 @@ import focusManager from '../components/focusManager'; import dom from './dom'; -import '../assets/css/scrollstyles.scss'; +import '../styles/scrollstyles.scss'; function getBoundingClientRect(elem) { // Support: BlackBerry 5, iOS 3 (original iPhone) diff --git a/src/scripts/serverNotifications.js b/src/scripts/serverNotifications.js index df902bb286..de8c0c483c 100644 --- a/src/scripts/serverNotifications.js +++ b/src/scripts/serverNotifications.js @@ -98,11 +98,11 @@ function processGeneralCommand(cmd, apiClient) { break; case 'SetAudioStreamIndex': notifyApp(); - playbackManager.setAudioStreamIndex(parseInt(cmd.Arguments.Index)); + playbackManager.setAudioStreamIndex(parseInt(cmd.Arguments.Index, 10)); break; case 'SetSubtitleStreamIndex': notifyApp(); - playbackManager.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index)); + playbackManager.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index, 10)); break; case 'ToggleFullscreen': inputManager.handleCommand('togglefullscreen'); diff --git a/src/scripts/settings/appSettings.js b/src/scripts/settings/appSettings.js index 7a48943954..df18b0ddff 100644 --- a/src/scripts/settings/appSettings.js +++ b/src/scripts/settings/appSettings.js @@ -70,7 +70,7 @@ class AppSettings { // return a huge number so that it always direct plays return 150000000; } else { - return parseInt(this.get(key) || '0') || 1500000; + return parseInt(this.get(key) || '0', 10) || 1500000; } } @@ -80,7 +80,7 @@ class AppSettings { } const defaultValue = 320000; - return parseInt(this.get('maxStaticMusicBitrate') || defaultValue.toString()) || defaultValue; + return parseInt(this.get('maxStaticMusicBitrate') || defaultValue.toString(), 10) || defaultValue; } maxChromecastBitrate(val) { @@ -89,7 +89,7 @@ class AppSettings { } val = this.get('chromecastBitrate1'); - return val ? parseInt(val) : null; + return val ? parseInt(val, 10) : null; } /** diff --git a/src/scripts/settings/userSettings.js b/src/scripts/settings/userSettings.js index 9d3093518d..086ae747dc 100644 --- a/src/scripts/settings/userSettings.js +++ b/src/scripts/settings/userSettings.js @@ -361,7 +361,7 @@ export class UserSettings { return this.set('skipBackLength', val.toString()); } - return parseInt(this.get('skipBackLength') || '10000'); + return parseInt(this.get('skipBackLength') || '10000', 10); } /** @@ -374,7 +374,7 @@ export class UserSettings { return this.set('skipForwardLength', val.toString()); } - return parseInt(this.get('skipForwardLength') || '30000'); + return parseInt(this.get('skipForwardLength') || '30000', 10); } /** diff --git a/src/strings/be-by.json b/src/strings/be-by.json index 266b83683b..3e64e8e09b 100644 --- a/src/strings/be-by.json +++ b/src/strings/be-by.json @@ -1380,7 +1380,7 @@ "UnknownAudioStreamInfo": "Інфармацыя аб аўдыяплыні невядомая", "DirectPlayError": "Узнікла памылка пры запуску прамога прайгравання", "SelectAll": "Абраць усё", - "Clip": "Художнік", + "Clip": "Кліп", "Sample": "Прыклад", "LabelVppTonemappingBrightness": "Узмацненне яркасці танальнага адлюстравання VPP:", "LabelVppTonemappingBrightnessHelp": "Прымяніць узмацненне яркасці ў танальным адлюстраванні VPP. І рэкамендаванае, і стандартнае значэнне роўна 0.", @@ -1689,5 +1689,18 @@ "LabelDummyChapterCountHelp": "Максімальная колькасць выяваў раздзелаў, якія будуць выняты для кожнага медыяфайла.", "LabelChapterImageResolution": "Дазвол:", "LabelChapterImageResolutionHelp": "Дазвол вынятых малюнкаў раздзелаў.", - "ResolutionMatchSource": "Супадзенне з крыніцай" + "ResolutionMatchSource": "Супадзенне з крыніцай", + "SecondarySubtitles": "Дадатковыя субтытры", + "SubtitleBlack": "Чорны", + "SubtitleBlue": "Сіні", + "SubtitleCyan": "Блакітны", + "SubtitleGray": "Шэры", + "SubtitleGreen": "Зялёны", + "SubtitleLightGray": "Светла-шэры", + "SubtitleMagenta": "Пурпурны", + "SubtitleRed": "Чырвоны", + "SubtitleWhite": "Белы", + "SubtitleYellow": "Жоўты", + "Featurette": "Кароткаметражка", + "Short": "Кароткаметражка" } diff --git a/src/strings/cs.json b/src/strings/cs.json index 78acddc0c0..a8d7ae5656 100644 --- a/src/strings/cs.json +++ b/src/strings/cs.json @@ -1622,7 +1622,7 @@ "DeletedScene": "Vymazaná scéna", "BehindTheScenes": "Z natáčení", "Trailer": "Upoutávka", - "Clip": "Krátký film", + "Clip": "Filmový klip", "AllowEmbeddedSubtitlesHelp": "Zakázat titulky, které jsou vložené v kontejneru média. Vyžaduje kompletní přeskenování knihovny.", "AllowEmbeddedSubtitlesAllowTextOption": "Povolit textové titulky", "AllowEmbeddedSubtitlesAllowImageOption": "Povolit grafické titulky", @@ -1704,5 +1704,17 @@ "ResolutionMatchSource": "Stejné jako zdroj", "PreferEmbeddedExtrasTitlesOverFileNames": "Preferovat vložené názvy před názvy souborů pro doplňky", "PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Doplňky většinou mají stejný vložený název jako nadřazená položka. Zaškrtnutím je i přesto můžete upřednostnit.", - "SecondarySubtitles": "Sekundární titulky" + "SecondarySubtitles": "Sekundární titulky", + "SubtitleBlack": "Černá", + "SubtitleBlue": "Modrá", + "SubtitleCyan": "Tyrkysová", + "SubtitleGray": "Šedá", + "SubtitleGreen": "Zelená", + "SubtitleLightGray": "Světle šedá", + "SubtitleMagenta": "Fialová", + "SubtitleRed": "Červená", + "SubtitleWhite": "Bílá", + "SubtitleYellow": "Žlutá", + "Featurette": "Středně dlouhý film", + "Short": "Krátký film" } diff --git a/src/strings/de.json b/src/strings/de.json index 89856057b3..dedc1dfdc3 100644 --- a/src/strings/de.json +++ b/src/strings/de.json @@ -121,7 +121,7 @@ "DeleteMedia": "Medien löschen", "DeleteUser": "Benutzer löschen", "DeleteUserConfirmation": "Bist du dir sicher, dass du diesen Benutzer löschen willst?", - "Depressed": "gedrückt", + "Depressed": "Gedrückt", "Descending": "Absteigend", "DetectingDevices": "Suche Geräte", "DeviceAccessHelp": "Dies wird nur auf Geräte angewendet, die eindeutig identifiziert werden können, und verhindert nicht den Web-Zugriff. Gefilterter Zugriff auf Geräte verhindert die Nutzung neuer Geräte solange, bis der Zugriff für diese freigegeben wird.", @@ -279,11 +279,11 @@ "HeaderKeepRecording": "Aufnahme behalten", "HeaderKeepSeries": "Serie behalten", "HeaderKodiMetadataHelp": "Um NFO-Metadaten zu aktivieren oder zu deaktivieren, bearbeite eine Bibliothek und finde den Abschnitt zum Speichern von Metadaten.", - "HeaderLatestEpisodes": "Neueste Episoden", - "HeaderLatestMedia": "Neueste Medien", - "HeaderLatestMovies": "Neueste Filme", - "HeaderLatestMusic": "Neueste Musik", - "HeaderLatestRecordings": "Neueste Aufnahmen", + "HeaderLatestEpisodes": "Kürzlich hinzugefügte Episoden", + "HeaderLatestMedia": "Kürzlich hinzugefügte Medien", + "HeaderLatestMovies": "Kürzlich hinzugefügte Filme", + "HeaderLatestMusic": "Kürzlich hinzugefügte Musik", + "HeaderLatestRecordings": "Kürzlich hinzugefügte Aufnahmen", "HeaderLibraries": "Bibliotheken", "HeaderLibraryAccess": "Bibliothekszugriff", "HeaderLibraryFolders": "Bibliotheksverzeichnisse", @@ -301,7 +301,7 @@ "HeaderMyMediaSmall": "Meine Medien (Klein)", "HeaderNewApiKey": "Neuer API-Schlüssel", "HeaderNewDevices": "Neue Geräte", - "HeaderNextEpisodePlayingInValue": "Nächste Episode wird abgespielt in {0}", + "HeaderNextEpisodePlayingInValue": "Nächste Episode in {0}", "HeaderNextVideoPlayingInValue": "Nächstes Video wird abgespielt in {0}", "HeaderOnNow": "Gerade läuft", "HeaderOtherItems": "Andere Inhalte", @@ -696,7 +696,7 @@ "LabelffmpegPathHelp": "Verzeichnis zur FFmpeg-Applikationsdatei oder zum Ordner, der FFmpeg enthält.", "LanNetworksHelp": "Komma separierte Liste von IP-Adressen oder IP/Netzmasken die als lokale Netzwerke behandelt werden sollen, um Bandbreitenlimitationen auszusetzen. Wenn gesetzt, werden alle anderen IP-Adressen als extern behandelt und unterliegen den Bandbreitenlimitationen für externe Verbindungen. Wenn leer, wird nur das Subnetz des Servers als lokales Netzwerk behandelt.", "Large": "Groß", - "LatestFromLibrary": "Neueste {0}", + "LatestFromLibrary": "Kürzlich hinzugefügt in {0}", "LearnHowYouCanContribute": "Erfahre, wie du unterstützen kannst.", "LibraryAccessHelp": "Wähle die Bibliotheken aus, die du mit diesem Benutzer teilen möchtest. Administratoren können den Metadaten-Manager verwenden um alle Ordner zu bearbeiten.", "List": "Liste", @@ -1023,7 +1023,7 @@ "TabContainers": "Container", "TabDashboard": "Übersicht", "TabDirectPlay": "Direktwiedergabe", - "TabLatest": "Neueste", + "TabLatest": "Kürzlich hinzugefügt", "TabMusic": "Musik", "TabMyPlugins": "Meine Plugins", "TabNetworks": "Fernsehsender", @@ -1704,5 +1704,15 @@ "LabelDummyChapterCount": "Limit:", "LabelDummyChapterCountHelp": "Die maximale Anzahl von Kapitelbildern, die für jede Mediendatei extrahiert werden.", "LabelChapterImageResolution": "Auflösung:", - "LabelChapterImageResolutionHelp": "Die Auflösung der extrahierten Kapitelbilder." + "LabelChapterImageResolutionHelp": "Die Auflösung der extrahierten Kapitelbilder.", + "SubtitleBlack": "Schwarz", + "SubtitleBlue": "Blau", + "SubtitleCyan": "Cyan", + "SubtitleGray": "Grau", + "SubtitleGreen": "Grün", + "SubtitleLightGray": "Hellgrau", + "SubtitleMagenta": "Magenta", + "SubtitleRed": "Rot", + "SubtitleWhite": "Weiß", + "SubtitleYellow": "Gelb" } diff --git a/src/strings/en-gb.json b/src/strings/en-gb.json index a4e975a8a5..c52fb52d48 100644 --- a/src/strings/en-gb.json +++ b/src/strings/en-gb.json @@ -1629,7 +1629,7 @@ "DeletedScene": "Deleted Scene", "BehindTheScenes": "Behind the Scenes", "Trailer": "Trailer", - "Clip": "Featurette", + "Clip": "Clip", "ShowParentImages": "Show programme images", "NextUpRewatching": "Rewatching", "MixedMoviesShows": "Mixed Films and Programmes", @@ -1704,5 +1704,17 @@ "SaveRecordingNFO": "Save recording EPG metadata in NFO", "PreferEmbeddedExtrasTitlesOverFileNames": "Prefer embedded titles over filenames for extras", "PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Extras often have the same embedded name as the parent, check this to use embedded titles for them anyway.", - "SecondarySubtitles": "Secondary Subtitles" + "SecondarySubtitles": "Secondary Subtitles", + "SubtitleBlack": "Black", + "SubtitleBlue": "Blue", + "SubtitleCyan": "Cyan", + "SubtitleGray": "Grey", + "SubtitleGreen": "Green", + "SubtitleLightGray": "Light Grey", + "SubtitleMagenta": "Magenta", + "SubtitleRed": "Red", + "SubtitleWhite": "White", + "SubtitleYellow": "Yellow", + "Featurette": "Featurette", + "Short": "Short" } diff --git a/src/strings/en-us.json b/src/strings/en-us.json index ece0493026..fda860768b 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1464,10 +1464,20 @@ "Subtitle": "Subtitle", "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "These settings also apply to any Google Cast playback started by this device.", "SubtitleAppearanceSettingsDisclaimer": "Following settings do not apply to the graphical subtitles mentioned above or ASS/SSA subtitles that embed their own styles.", + "SubtitleBlack": "Black", + "SubtitleBlue": "Blue", + "SubtitleCyan": "Cyan", "SubtitleDownloadersHelp": "Enable and rank your preferred subtitle downloaders in order of priority.", + "SubtitleGray": "Gray", + "SubtitleGreen": "Green", + "SubtitleLightGray": "Light Gray", + "SubtitleMagenta": "Magenta", "SubtitleOffset": "Subtitle Offset", + "SubtitleRed": "Red", "Subtitles": "Subtitles", "SubtitleVerticalPositionHelp": "Line number where text appears. Positive numbers indicate top down. Negative numbers indicate bottom up.", + "SubtitleWhite": "White", + "SubtitleYellow": "Yellow", "Suggestions": "Suggestions", "Sunday": "Sunday", "Sync": "Sync", @@ -1663,7 +1673,9 @@ "UnknownAudioStreamInfo": "The audio stream info is unknown", "DirectPlayError": "There was an error starting direct playback", "SelectAll": "Select All", - "Clip": "Featurette", + "Featurette": "Featurette", + "Short": "Short", + "Clip": "Clip", "Trailer": "Trailer", "BehindTheScenes": "Behind the Scenes", "DeletedScene": "Deleted Scene", diff --git a/src/strings/es_419.json b/src/strings/es_419.json index 707713eeea..a09b64a0b9 100644 --- a/src/strings/es_419.json +++ b/src/strings/es_419.json @@ -16,7 +16,7 @@ "Books": "Libros", "Artists": "Artistas", "Albums": "Álbumes", - "TabLatest": "Recientes", + "TabLatest": "Agregado recientemente", "HeaderUser": "Usuario", "AlbumArtist": "Artista del álbum", "Album": "Álbum", @@ -309,7 +309,7 @@ "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.", - "LatestFromLibrary": "Últimas - {0}", + "LatestFromLibrary": "Agregado recientemente en {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 ejecutable FFmpeg, o la carpeta que contenga FFmpeg.", @@ -571,7 +571,7 @@ "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:", + "LabelSelectFolderGroups": "Agrupar automáticamente el contenido de las siguientes carpetas a vistas como «Películas», «Música» y «TV»:", "LabelSeasonNumber": "Temporada número:", "LabelScreensaver": "Protector de pantalla:", "LabelScheduledTaskLastRan": "Última ejecución {0}, tomando {1}.", @@ -875,7 +875,7 @@ "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»", + "HideWatchedContentFromLatestMedia": "Ocultar contenido ya visto de «Medios agregados recientemente»", "Hide": "Ocultar", "Help": "Ayuda", "HeaderYears": "Años", @@ -970,11 +970,11 @@ "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", + "HeaderLatestRecordings": "Grabaciones agregadas recientemente", + "HeaderLatestMusic": "Música agregada recientemente", + "HeaderLatestMovies": "Películas agregadas recientemente", + "HeaderLatestMedia": "Medios agregados recientemente", + "HeaderLatestEpisodes": "Episodios agregados recientemente", "HeaderKodiMetadataHelp": "Para habilitar o deshabilitar los metadatos NFO, edita una biblioteca y ubica la sección de 'Grabadores de metadatos'.", "HeaderKeepSeries": "Conservar serie", "HeaderKeepRecording": "Conservar grabación", @@ -1006,7 +1006,7 @@ "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:", + "LabelMaxChromecastBitrate": "Calidad de transmisión de Google Cast:", "LabelMaxBackdropsPerItem": "Número máximo de imágenes de fondo por elemento:", "LabelMatchType": "Tipo de coincidencia:", "LabelManufacturerUrl": "URL del fabricante:", @@ -1022,7 +1022,7 @@ "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.", + "LabelKodiMetadataUserHelp": "Guarda los datos de visualización en los archivos NFO para que otras aplicaciones lo usen.", "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", @@ -1166,7 +1166,7 @@ "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»", + "DisplayInOtherHomeScreenSections": "Mostrar en las secciones de la pantalla de inicio como «Medios agregados recientemente» o «Continuar viendo»", "DisplayInMyMedia": "Mostrar en la pantalla de inicio", "Display": "Pantalla", "Disconnect": "Desconectar", @@ -1354,7 +1354,7 @@ "MessagePluginInstallError": "Ocurrió un error instalando el complemento.", "PlaybackRate": "Tasa de reproducción", "Data": "Datos", - "ButtonUseQuickConnect": "Usar conexión rápida", + "ButtonUseQuickConnect": "Usar «Conexión rápida»", "ButtonActivate": "Activar", "Authorize": "Autorizar", "LabelCurrentStatus": "Estado actual:", @@ -1366,7 +1366,7 @@ "TonemappingAlgorithmHelp": "El mapeo de tonos puede ser modificado. Si no estas familiarizado con estas opciones, solo manten el predeterminado. El valor recomendado es Hable.", "LabelTonemappingThresholdHelp": "El algoritmo de mapeo de tonos puede ser finamente ajustado para cada escena. Y el umbral es usado para detectar si la escena ha cambiado. Si los valores entre el promedio de brillo del fotograma actual y el promedio actual excede el valor de umbral, se vuelve a calcular el brillo promedio y máximo. Los valores recomendados y por defecto son entre 0.2 y 0.8.", "ThumbCard": "Miniatura", - "LabelMaxMuxingQueueSizeHelp": "Numero máximo de paquetes que pueden estar en búfer mientras se espera a que se inicialicen todas las trasmisiones. Intenta incrementarlos si encuentras el error \"Too many packets buffered for output stream\" en los registros de ffmpeg. El valor recomendado es 2048.", + "LabelMaxMuxingQueueSizeHelp": "Numero máximo de paquetes que pueden estar en búfer mientras se espera a que se inicialicen todas las trasmisiones. Intenta incrementarlos si encuentras el error «Too many packets buffered for output stream» en los registros de ffmpeg. El valor recomendado es 2048.", "LabelMaxMuxingQueueSize": "Tamaño máximo cola de mezclado:", "LabelTonemappingParamHelp": "Modifica el algoritmo de mapeo de tonos. Los valores recomendados y por defecto son NaN. Se puede dejar en blanco.", "LabelTonemappingParam": "Parámetro Mapeo de tonos:", @@ -1380,7 +1380,7 @@ "LabelTonemappingAlgorithm": "Seleccione el algoritmo de mapeo de tonos:", "AllowTonemappingHelp": "El mapeo de tonos puede transformar el rango dinámico de un video de HDR a SDR manteniendo la calidad de imagen y los colores, que son datos muy importantes para mostrar la imagen original. Actualmente solo funciona con videos HDR10 o HLG. Esto requiere los tiempos de ejecución OpenCL o CUDA correspondientes.", "EnableTonemapping": "Habilitar mapeo de tonos", - "LabelOpenclDeviceHelp": "Este es el dispositivo OpenCL que se utiliza para el mapeo de tonos. El lado izquierdo del punto es el número de plataforma y el lado derecho es el número de dispositivo en la plataforma. El valor predeterminado es 0.0. Se requiere la aplicación ffmpeg que contiene el método de aceleración de hardware OpenCL.", + "LabelOpenclDeviceHelp": "Este es el dispositivo OpenCL que se utiliza para el mapeo de tonos. El lado izquierdo del punto es el número de plataforma y el lado derecho es el número de dispositivo en la plataforma. El valor predeterminado es 0.0. Se requiere la aplicación FFmpeg que contiene el método de aceleración de hardware OpenCL.", "LabelOpenclDevice": "Dispositivo OpenCL:", "LabelColorPrimaries": "Colores primarios:", "LabelColorTransfer": "Transferencia de Color:", @@ -1400,22 +1400,22 @@ "SelectServer": "Seleccionar Servidor", "Restart": "Reiniciar", "ResetPassword": "Resetear contraseña", - "QuickConnectNotActive": "Conexión Rápida no esta habilitado en el servidor", - "QuickConnectNotAvailable": "Pregunte al administrador del servidor para habilitar Conexión Rápida", - "QuickConnectInvalidCode": "Código Conexión Rápida inválido", - "QuickConnectDescription": "Para ingresar con Conexión Rápida, seleccione el botón Conexión Rápida en el dispositivo del cual estas ingresando e ingrese el siguiente código.", - "QuickConnectDeactivated": "Conexión Rápida se desactivó antes de que se pudiera aprobar la solicitud de inicio de sesión", - "QuickConnectAuthorizeFail": "Código Conexión Rápida desconocido", - "QuickConnectAuthorizeSuccess": "Requiere autorización", + "QuickConnectNotActive": "«Conexión rápida» no está habilitado en este servidor", + "QuickConnectNotAvailable": "Pide al administrador del servidor que habilite la «Conexión rápida»", + "QuickConnectInvalidCode": "Código de «Conexión rápida» inválido", + "QuickConnectDescription": "Para ingresar con «Conexión rápida», seleccione el botón «Conexión rápida» en el dispositivo del cual estas ingresando e ingrese el código mostrado a continuación.", + "QuickConnectDeactivated": "La «Conexión rápida» se desactivó antes de que se pudiera aprobar la solicitud de inicio de sesión", + "QuickConnectAuthorizeFail": "Código de «Conexión rápida» desconocido", + "QuickConnectAuthorizeSuccess": "Solicitud autorizada", "QuickConnectAuthorizeCode": "Ingrese el código {0} para acceder", - "QuickConnectActivationSuccessful": "Activación Exitosa", - "QuickConnect": "Conexión Rápida", + "QuickConnectActivationSuccessful": "Activado exitosamente", + "QuickConnect": "Conexión rápida", "Profile": "Perfil", "PosterCard": "Imagen del Cartel", "Poster": "Cartel", "Photo": "Foto", "MusicVideos": "Videos musicales", - "LabelQuickConnectCode": "Código conexión rápida:", + "LabelQuickConnectCode": "Código de «Conexión rápida»:", "LabelKnownProxies": "Proxies conocidos:", "LabelIconMaxResHelp": "Resolución maxima de los iconos por medio de la función 'upnp:icon'.", "EnableAutoCast": "Establecer como Predeterminado", @@ -1519,7 +1519,7 @@ "LabelSlowResponseEnabled": "Log de alarma si la respuesta del servidor fue lenta", "UseEpisodeImagesInNextUpHelp": "Las secciones Siguiente y Continuar viendo utilizaran imagenes del episodio como miniaturas en lugar de miniaturas del show.", "UseEpisodeImagesInNextUp": "Usar imágenes de los episodios en \"Siguiente\" y \"Continuar Viendo\"", - "LabelLocalCustomCss": "El CSS personalizado aplica solo a este cliente. Puede quieras deshabilitar el CSS del servidor.", + "LabelLocalCustomCss": "Código CSS personalizado para estilo que aplica solo a este cliente. Puede que quiera deshabilitar el código CSS personalizado del servidor.", "LabelDisableCustomCss": "Desactivar el código CSS personalizado para la tematización/marca proporcionada desde el servidor.", "DisableCustomCss": "Deshabilitar CSS proveniente del servidor", "LabelAutomaticallyAddToCollectionHelp": "Cuando al menos 2 películas tengan el mismo nombre de colección, se añadirán automáticamente a dicha colección.", @@ -1564,5 +1564,16 @@ "LabelSyncPlaySettingsSyncCorrection": "Corrección de sincronización", "LabelSyncPlaySettingsExtraTimeOffset": "Desfase de tiempo extra:", "LabelSyncPlaySettingsMinDelaySpeedToSync": "Retraso mínimo de SpeedToSync:", - "LabelOriginalName": "Nombre original:" + "LabelOriginalName": "Nombre original:", + "Experimental": "Experimental", + "HeaderRecordingMetadataSaving": "Metadatos de grabación", + "LabelStereoDownmixAlgorithm": "Algoritmo de downmix estéreo", + "LabelDummyChapterDuration": "Intervalo:", + "LabelDummyChapterCountHelp": "El máximo número de imágenes de capítulos que serán extraídas por cada archivo de medios.", + "HeaderDummyChapter": "Imágenes de capítulos", + "LabelDummyChapterDurationHelp": "El intervalo de la extracción de las imágenes de los capítulos, en segundos.", + "LabelDummyChapterCount": "Límite:", + "LabelChapterImageResolution": "Resolución:", + "LabelChapterImageResolutionHelp": "La resolución de las imágenes de capítulos extraídas.", + "LabelMaxDaysForNextUp": "Días máximos en «A continuación»:" } diff --git a/src/strings/eu.json b/src/strings/eu.json index e9fa50c682..bce8b4051f 100644 --- a/src/strings/eu.json +++ b/src/strings/eu.json @@ -129,7 +129,7 @@ "ValueCodec": "Kodeka: {0}", "ValueAudioCodec": "Audio-kodeka: {0}", "ValueAlbumCount": "{0} album", - "UserProfilesIntro": "Jellyfinek erabiltzaileen profilentzako euskarria barne hartzen du, eta erabiltzaile bakoitzak bere doikuntzak, ugaltze-egoera eta gurasoen kontrola izatea ahalbidetzen du.", + "UserProfilesIntro": "Jellyfinek erabiltzaileen profilentzako euskarria barne hartzen du, eta erabiltzaile bakoitzak bere ezarpenak, ugaltze-egoera eta gurasoen kontrola izatea ahalbidetzen du.", "UserAgentHelp": "Eman 'User-Agent' pertsonalizatutako http goiburu bat.", "UseEpisodeImagesInNextUpHelp": "\"Nobedadeak\" eta \"Ikusten jarraitu\" ataletan kapituluen irudiak agertuko dira miniatura gisa, seriearen miniatura nagusiaren ordez.", "UseEpisodeImagesInNextUp": "Erabili kapituluen irudiak \"Nobedadeak\" eta \"Ikusten jarraitu\" ataletan", @@ -188,7 +188,7 @@ "TabParentalControl": "Gurasoen kontrola", "TabOther": "Bestelakoak", "TabNotifications": "Jakinarazpenak", - "TabNfoSettings": "NFO doikuntzak", + "TabNfoSettings": "NFO ezarpenak", "TabNetworks": "Kate TB", "TabNetworking": "Sareak", "TabMyPlugins": "Nire osagarriak", @@ -212,7 +212,7 @@ "Subtitles": "Azpitituluak", "SubtitleOffset": "Azpitituluak lekualdatzea", "SubtitleDownloadersHelp": "Gaitu eta sailkatu hobetsitako azpitituluen deskargagailuak lehentasun-ordenaren arabera.", - "SubtitleAppearanceSettingsDisclaimer": "Ondorengo doikuntzak ez zaizkie aplikatuko aurretik aipatutako azpititulu grafikoei, ezta ASS/SSA azpitituluei ere, baldin eta beren estiloak badituzte.", + "SubtitleAppearanceSettingsDisclaimer": "Ondorengo ezarpenak ez zaizkie aplikatuko aurretik aipatutako azpititulu grafikoei, ezta ASS/SSA azpitituluei ere, baldin eta beren estiloak badituzte.", "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Doikuntza horiek gailu honek hasitako Chromecast edozein erreprodukziori ere aplikatzen zaizkio.", "Subtitle": "Azpititulua", "Studios": "Ikasketak", @@ -246,13 +246,13 @@ "SetUsingLastTracks": "Audioko/azpitituluetako pista aurreko itemarekin ezartzea", "SettingsWarning": "Balio horiek aldatzeak ezegonkortasuna edo konektagarritasun-akatsak eragin ditzake. Arazoren bat izanez gero, berriz ere bere balio lehenetsira aldatzea gomendatzen dizugu.", "SettingsSaved": "Konfigurazio gordea.", - "Settings": "Doikuntzak", + "Settings": "Ezarpenak", "ServerUpdateNeeded": "Zerbitzaria eguneratu egin behar da. Azken bertsioa deskargatzeko, bisitatu {0}", "ServerRestartNeededAfterPluginInstall": "Jellyfin berriz hasi beharko da osagarri bat instalatu ondoren.", "ServerNameIsShuttingDown": "{0}(e)ko zerbitzaria itzaltzen ari da.", "ServerNameIsRestarting": "{0}(e)ko zerbitzaria berrabiarazten ari da.", "SeriesYearToPresent": "{0} - Gaur egun", - "SeriesSettings": "Serie-doikuntzak", + "SeriesSettings": "Serie ezarpenak", "SeriesRecordingScheduled": "Programatutako serieak grabatzea.", "SeriesDisplayOrderHelp": "Ordenatu kapituluak jaulkipen-dataren, DVD-ordenaren edo zenbaki absolutuaren arabera.", "SeriesCancelled": "Serie ezeztatua.", @@ -911,7 +911,7 @@ "Hide": "Ezkutatu", "Help": "Laguntza", "HeaderYears": "Urteak", - "HeaderXmlSettings": "XML doikuntzak", + "HeaderXmlSettings": "XML ezarpenak", "HeaderXmlDocumentAttributes": "XML dokumentuaren atributuak", "HeaderXmlDocumentAttribute": "XML dokumentuaren atributua", "HeaderVideoTypes": "Bideo motak", @@ -953,7 +953,7 @@ "LibraryAccessHelp": "Aukeratu erabiltzaile honekin partekatu beharreko liburutegiak. Administratzaileek karpeta guztiak editatu ahal izango dituzte metadatuen kudeatzailea erabiliz.", "LeaveBlankToNotSetAPassword": "Hutsik utz dezakezu pasahitza ez konfiguratzeko.", "LearnHowYouCanContribute": "Ezagutu nola lagundu dezakezun.", - "LatestFromLibrary": "Duela gutxi {0} liburutegian", + "LatestFromLibrary": "Duela gutxi gehituta: {0}", "LastSeen": "Azkenekoz ikusita {0}", "Larger": "Handiagoa", "Large": "Handia", @@ -1113,7 +1113,7 @@ "HeaderSortOrder": "Agindua", "HeaderSortBy": "Ordenatu", "HeaderSetupLibrary": "Konfiguratu zure multimedia-liburutegiak", - "HeaderServerSettings": "Zerbitzariaren doikuntzak", + "HeaderServerSettings": "Zerbitzariaren ezarpenak", "HeaderServerAddressSettings": "Zerbitzariaren helbidea konfiguratzea", "HeaderSeriesStatus": "Serieen egoera", "HeaderSeriesOptions": "Serieen aukerak", @@ -1138,7 +1138,7 @@ "HeaderRemoteControl": "Urrutiko kontrola", "HeaderRemoteAccessSettings": "Urruneko sarbide-aukerak", "HeaderRecordingPostProcessing": "Prozesatu osteko grabazioa", - "HeaderRecordingOptions": "Grabazio-doikuntzak", + "HeaderRecordingOptions": "Grabazio ezarpenak", "HeaderRecentlyPlayed": "Duela gutxi erreproduzitua", "HeaderProfileServerSettingsHelp": "Balio horiek zerbitzaria bezeroei nola aurkeztuko zaien kontrolatzen dute.", "HeaderProfileInformation": "Profilari buruzko informazioa", @@ -1169,12 +1169,12 @@ "HeaderMyDevice": "Nire gailua", "HeaderMusicQuality": "Soinuaren kalitatea", "HeaderMoreLikeThis": "Hau bezalakoa", - "HeaderMetadataSettings": "Metadatuen doikuntzak", + "HeaderMetadataSettings": "Metadatuen ezarpenak", "HeaderMediaFolders": "Multimedia karpetak", "HeaderMedia": "Multimedia", "HeaderLoginFailure": "Saioaren hasierako akatsa", - "HeaderLiveTvTunerSetup": "Telebista-sintonizadorearen doikuntzak", - "HeaderLibrarySettings": "Liburutegiaren doikuntzak", + "HeaderLiveTvTunerSetup": "Telebista-sintonizadorearen ezarpenak", + "HeaderLibrarySettings": "Liburutegiaren ezarpenak", "HeaderLibraryOrder": "Liburutegiaren agindua", "HeaderLibraryFolders": "Liburutegiko karpetak", "HeaderLibraryAccess": "Liburutegirako sarbidea", @@ -1184,24 +1184,24 @@ "HeaderLatestMovies": "Azken filmak", "HeaderLatestMedia": "Berriki gehituak", "HeaderLatestEpisodes": "Azken kapituluak", - "HeaderKodiMetadataHelp": "NFO formatuko metadatuak aktibatu edo desaktibatu ditzakezu, liburutegi baten konfigurazioa irekiz eta metadatuen ataleko doikuntzak berrikusiz.", + "HeaderKodiMetadataHelp": "NFO formatuko metadatuak aktibatu edo desaktibatu ditzakezu, liburutegi baten konfigurazioa irekiz eta metadatuen ataleko ezarpenak berrikusiz.", "HeaderKeepSeries": "Serieak mantentzea", "HeaderKeepRecording": "Grabazioa mantendu", "HeaderInstantMix": "Bat-bateko nahasketa", "HeaderInstall": "Instalatu", - "HeaderImageSettings": "Irudi-doikuntzak", + "HeaderImageSettings": "Irudi ezarpenak", "HeaderImageOptions": "Irudi-aukerak", "HeaderIdentifyItemHelp": "Sartu bilaketa-parametro bat edo gehiago. Bilaketaren emaitzen kopurua handitzeko parametroak ezabatzen ditu.", "HeaderIdentificationHeader": "Identifikazio-goiburua", "HeaderIdentificationCriteriaHelp": "Gutxienez identifikazio-irizpide bat sartzen du.", "HeaderIdentification": "Identifikazioa", - "HeaderHttpsSettings": "https aukerak", - "HeaderHttpHeaders": "http buruak", + "HeaderHttpsSettings": "HTTPS aukerak", + "HeaderHttpHeaders": "HTTP buruak", "HeaderGuideProviders": "Gida-hornitzaileak", "HeaderFrequentlyPlayed": "Maiz erreproduzitua", "HeaderForKids": "Haurrentzat", "HeaderFetchImages": "Bilatu irudiak:", - "HeaderFetcherSettings": "Harrapatzailearen doikuntzak", + "HeaderFetcherSettings": "Harrapatzailearen ezarpenak", "HeaderFeatureAccess": "Sartzeko baimenak:", "HeaderExternalIds": "Kanpoko IDS:", "HeaderError": "Errorea", @@ -1421,8 +1421,8 @@ "DeleteAll": "Ezabatu dena", "Delete": "Ezabatu", "DeinterlaceMethodHelp": "Hautatu bihurketan zehar elkarri lotuta dagoen edukia askatzeko aplikatuko den iragazki mota. Lotura kentzeko hardware bidezko azelerazioa aktibatuta badago, iragazki horren ordez erabiliko da.", - "DefaultSubtitlesHelp": "Erabiliko diren azpitituluak fitxategien pistetan txertatutako metadatuen araberakoak izango dira (bai \"lehenetsita\" edo \"behartuta\"). Hizkuntza-doikuntzak kontuan hartzen dira hainbat aukera eskuragarri daudenean.", - "DefaultMetadataLangaugeDescription": "Hauek dira zure doikuntzak eta liburutegi bakoitzerako pertsonalizatu daitezke.", + "DefaultSubtitlesHelp": "Erabiliko diren azpitituluak fitxategien pistetan txertatutako metadatuen araberakoak izango dira (bai \"lehenetsita\" edo \"behartuta\"). Hizkuntza ezarpenak kontuan hartzen dira hainbat aukera eskuragarri daudenean.", + "DefaultMetadataLangaugeDescription": "Hauek dira zure ezarpenak eta liburutegi bakoitzerako pertsonalizatu daitezke.", "Default": "Lehenetsia", "DeathDateValue": "Hil egin zen: {0}", "DatePlayed": "Erreproduzitua", @@ -1455,7 +1455,7 @@ "ColorSpace": "Kolore-espazioa", "ColorPrimaries": "Kolore primarioak", "Collections": "Bildumak", - "ClientSettings": "Bezeroaren doikuntzak", + "ClientSettings": "Bezeroaren ezarpenak", "ClearQueue": "Ezabatu buztana", "CinemaModeConfigurationHelp": "Zinema moduak zinemaren esperientzia zuzenean ematen du bere aretoan, funtzio nagusiaren aurretik trailerrak eta sarrera pertsonalizatuak erreproduzitzeko gaitasunarekin.", "Channels": "Kanalak", @@ -1547,7 +1547,7 @@ "HeaderBranding": "Itxura pertsonalizatzea", "HeaderBlockItemsWithNoRating": "Baloraziorik gabeko edo ezezagunak diren artikuluak blokeatzea:", "HeaderAutoDiscovery": "Sarearen aurkikuntza", - "HeaderAudioSettings": "Audio-doikuntzak", + "HeaderAudioSettings": "Audio ezarpenak", "HeaderAudioBooks": "Audioliburuak", "HeaderAppearsOn": "Hemen agertzen da", "HeaderApp": "Aplikazioa", @@ -1626,5 +1626,9 @@ "ButtonSpace": "Hutsunea", "EnableRewatchingNextUp": "Gaitu berriz ikustea Nobedadeak atalekoentzat", "EnableRewatchingNextUpHelp": "Gaitu jada ikusitako kapituluak \"Nobedadeak\" atalean agertzea.", - "LabelMaxVideoResolution": "Onartutako bideo-transkodetzearen bereizmen maximoa:" + "LabelMaxVideoResolution": "Onartutako bideo-transkodetzearen bereizmen maximoa:", + "EnableCardLayout": "Erakutsi CardBox ikusgai", + "GoogleCastUnsupported": "Google Cast ez da bateragarria", + "Experimental": "Esperimentala", + "DownloadAll": "Deskargatu dena" } diff --git a/src/strings/fi.json b/src/strings/fi.json index d2051489b4..9d5e968ff0 100644 --- a/src/strings/fi.json +++ b/src/strings/fi.json @@ -343,7 +343,7 @@ "LabelAbortedByServerShutdown": "(Keskeytetty palvelimen sammutuksen takia)", "Identify": "Tunnista", "Horizontal": "Horisontaalinen", - "HideWatchedContentFromLatestMedia": "Piilota toistettu sisältö 'Hiljattain lisätty media' -osiosta", + "HideWatchedContentFromLatestMedia": "Piilota toistettu sisältö 'Viimeksi lisätty media' -osiosta", "HeaderUpcomingOnTV": "Tulossa televisiosta", "HeaderTypeImageFetchers": "Kuvien lataajat ({0}):", "HeaderTranscodingProfile": "Transkoodausprofiili", @@ -411,11 +411,11 @@ "HeaderMediaFolders": "Mediakansiot", "HeaderMedia": "Media", "HeaderLibraryFolders": "Kirjaston kansiot", - "HeaderLatestMedia": "Hiljattain lisätty media", - "HeaderLatestRecordings": "Hiljattain lisätyt tallenteet", - "HeaderLatestMusic": "Hiljattain lisätty musiikki", - "HeaderLatestMovies": "Hiljattain lisätyt elokuvat", - "HeaderLatestEpisodes": "Hiljattain lisätyt jaksot", + "HeaderLatestMedia": "Viimeksi lisätty media", + "HeaderLatestRecordings": "Viimeksi lisätyt tallenteet", + "HeaderLatestMusic": "Viimeksi lisätty musiikki", + "HeaderLatestMovies": "Viimeksi lisätyt elokuvat", + "HeaderLatestEpisodes": "Viimeksi lisätyt jaksot", "HeaderInstall": "Asenna", "HeaderFrequentlyPlayed": "Usein toistetut", "HeaderFetcherSettings": "Lataajan asetukset", @@ -562,7 +562,7 @@ "TabMyPlugins": "Omat lisäosat", "TabMusic": "Musiikki", "TabLogs": "Lokit", - "TabLatest": "Hiljattain lisätyt", + "TabLatest": "Viimeksi lisätyt", "TabDirectPlay": "Muuntamaton toisto", "TabDashboard": "Hallintapaneeli", "TabCatalog": "Luettelo", @@ -1264,7 +1264,7 @@ "LabelMinAudiobookResumeHelp": "Kohteita pidetään toistamattomina, jos toisto keskeytetään ennen tätä aikaa.", "LabelMaxStreamingBitrate": "Suoratoiston enimmäislaatu:", "MoreFromValue": "Lisää kohteesta {0}", - "LatestFromLibrary": "Hiljattain lisätty: '{0}'", + "LatestFromLibrary": "Viimeksi lisätty: {0}", "LabelVideoRange": "Videon alue:", "LabelVaapiDeviceHelp": "Tämä on renderöintinoodi, jota käytetään laitteistokiihdytykseen.", "LabelUserRemoteClientBitrateLimitHelp": "Korvaa globaali arvo asetusten kohdasta Ohjauspaneeli > Toisto > Suoratoisto.", @@ -1634,7 +1634,7 @@ "ButtonSpace": "Välilyönti", "ThemeVideo": "Tunnusvideo", "ThemeSong": "Tunnusmusiikki", - "Clip": "Lyhytfilmi", + "Clip": "Klippi", "Scene": "Kohtaus", "Interview": "Haastattelu", "UnknownAudioStreamInfo": "Äänivirran tiedot ovat tuntemattomia", @@ -1677,7 +1677,7 @@ "MessageNoFavoritesAvailable": "Suosikkeja ei ole tällä hetkellä käytettävissä.", "EnableCardLayout": "Näytä visuaalinen KorttiLaatikko", "Unreleased": "Ei vielä julkaistu", - "MediaInfoDvVersionMajor": "", + "MediaInfoDvVersionMajor": "DV-pääversio", "DownloadAll": "Lataa kaikki", "Experimental": "Kokeellinen", "LabelStereoDownmixAlgorithm": "Stereoäänen alasmiksausalgoritmi:", @@ -1697,5 +1697,22 @@ "ResolutionMatchSource": "Vastaa lähdettä", "PreferEmbeddedExtrasTitlesOverFileNames": "Suosi lisämateriaaleille upotettuja otsikoita tiedostonimien sijaan", "PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Lisämateriaaleilla on usein sama otsikko kuin niiden isännällä. Valitse tämä käyttääksesi silti upotettuja otsikoita.", - "SecondarySubtitles": "Toissijaiset tekstitykset" + "SecondarySubtitles": "Toissijaiset tekstitykset", + "MediaInfoElPresentFlag": "DV EL havaittu", + "SubtitleBlack": "Musta", + "SubtitleCyan": "Syaani", + "SubtitleGray": "Harmaa", + "SubtitleGreen": "Vihreä", + "SubtitleLightGray": "Vaaleanharmaa", + "SubtitleMagenta": "Magenta", + "SubtitleRed": "Punainen", + "SubtitleWhite": "Valkoinen", + "SubtitleYellow": "Keltainen", + "MediaInfoDvBlSignalCompatibilityId": "DV BL -signaaliyhteensopivuuden ID", + "SubtitleBlue": "Sininen", + "Featurette": "Oheisfilmi", + "Short": "Lyhytfilmi", + "MediaInfoBlPresentFlag": "DV BL havaittu", + "MediaInfoRpuPresentFlag": "DV RPU havaittu", + "MediaInfoDvVersionMinor": "DV-väliversio" } diff --git a/src/strings/fr-ca.json b/src/strings/fr-ca.json index 26e2fe3a71..15f1fe017b 100644 --- a/src/strings/fr-ca.json +++ b/src/strings/fr-ca.json @@ -334,7 +334,7 @@ "DownloadsValue": "{0} téléchargements", "DisplayModeHelp": "Sélectionner l'agencement que vous désirez pour l'interface.", "DisplayMissingEpisodesWithinSeasons": "Afficher les épisodes manquants dans les saisons", - "DisplayInOtherHomeScreenSections": "Afficher dans les sections de l’écran d’accueil comme « Ajouts récents » et reprendre le visionnement", + "DisplayInOtherHomeScreenSections": "Afficher dans les sections de l’écran d’accueil comme « Ajouts récents » et «Reprendre le visionnement»", "Directors": "Réalisateurs", "Director": "Réalisateur", "DetectingDevices": "Détection des appareils", @@ -349,7 +349,7 @@ "HeaderCodecProfile": "Profil de codec", "HeaderChapterImages": "Images des chapitres", "HeaderChannelAccess": "Accès aux chaînes", - "LatestFromLibrary": "{0}, ajouts récents", + "LatestFromLibrary": "{0} ajouts récents", "HideWatchedContentFromLatestMedia": "Masquer le contenu déjà vu dans les 'Derniers Médias'", "HeaderLatestRecordings": "Derniers enregistrements", "HeaderLatestMusic": "Dernière musique", @@ -1020,5 +1020,25 @@ "LabelMaxParentalRating": "Classification parentale maximale :", "SpecialFeatures": "Bonus", "Sort": "Trier", - "SortByValue": "Trier par" + "SortByValue": "Trier par", + "LabelMovieCategories": "Catégories de films:", + "LabelNewPassword": "Nouveau mot de passe:", + "LabelOriginalName": "Nom original:", + "LabelPasswordRecoveryPinCode": "Code NIP:", + "LabelOriginalTitle": "Titre original:", + "LabelMaxStreamingBitrate": "Qualité maximale de streaming:", + "LabelNotificationEnabled": "Activer cette notification", + "LabelNewName": "Nouveau nom:", + "LabelNewPasswordConfirm": "Confirmer le nouveau mot de passe:", + "LabelPersonRole": "Rôle:", + "LabelPasswordConfirm": "Confirmer le mot de passe:", + "LabelPersonRoleHelp": "Exemple: Conducteur de camion à crème glacée", + "LabelPleaseRestart": "Les changements prendront effets après avoir rechargé manuellement le client web.", + "LabelMinAudiobookResumeHelp": "Les titres sont considérés comme non joué s'ils sont arrêtés avant cette durée.", + "LabelPassword": "Mot de passe:", + "LabelPath": "Chemin:", + "LabelMetadataPath": "Chemin des métadonnées:", + "LabelDummyChapterDuration": "Intervalle:", + "LabelDummyChapterCount": "Limite:", + "LabelChapterImageResolution": "Résolution:" } diff --git a/src/strings/fr.json b/src/strings/fr.json index 859cbd8640..4c01e2b798 100644 --- a/src/strings/fr.json +++ b/src/strings/fr.json @@ -1614,7 +1614,7 @@ "AudioIsExternal": "Le flux audio est externe", "SelectAll": "Tout sélectionner", "ButtonExitApp": "Quitter l'application", - "Clip": "Court-métrage", + "Clip": "Clip", "ThemeVideo": "Générique", "ThemeSong": "Thème musical", "Sample": "Échantillon", @@ -1704,5 +1704,17 @@ "LabelDummyChapterCountHelp": "Nombre maximal d’images de chapitre à extraire pour chaque fichier multimédia.", "PreferEmbeddedExtrasTitlesOverFileNames": "Préférer les titres intégrés aux médias pour les bonus", "PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Les bonus possèdent souvent un titre intégré identique au média parent, cocher l'option pour utiliser ce titre quoi qu'il en soit.", - "SecondarySubtitles": "Sous-titres secondaires" + "SecondarySubtitles": "Sous-titres secondaires", + "SubtitleBlack": "Noir", + "SubtitleBlue": "Bleu", + "SubtitleCyan": "Cyan", + "SubtitleGray": "Gris", + "SubtitleGreen": "Vert", + "SubtitleLightGray": "Gris clair", + "SubtitleMagenta": "Magenta", + "SubtitleRed": "Rouge", + "SubtitleWhite": "Blanc", + "SubtitleYellow": "Jaune", + "Featurette": "Featurette", + "Short": "Court-métrage" } diff --git a/src/strings/hu.json b/src/strings/hu.json index e83e7516b2..117a166d8a 100644 --- a/src/strings/hu.json +++ b/src/strings/hu.json @@ -107,9 +107,9 @@ "HeaderIdentifyItemHelp": "Adj meg egy vagy több keresési kritériumot. Távolítsd el a kritériumokat a keresési eredmények növelése érdekében.", "HeaderImageSettings": "Kép beállítások", "HeaderInstall": "Telepítés", - "HeaderLatestEpisodes": "Legújabb epizódok", - "HeaderLatestMedia": "Legújabb média", - "HeaderLatestMovies": "Legújabb filmek", + "HeaderLatestEpisodes": "Nemrég hozzáadott epizódok", + "HeaderLatestMedia": "Nemrég hozzáadott tartalmak", + "HeaderLatestMovies": "Nemrég hozzáadott filmek", "HeaderLibraries": "Könyvtárak", "HeaderLibraryAccess": "Könyvtár hozzáférés", "HeaderLibraryFolders": "Médiatár mappák", @@ -156,7 +156,7 @@ "HeaderVideos": "Videók", "HeaderYears": "Év", "Help": "Súgó", - "HideWatchedContentFromLatestMedia": "A megtekintett tartalom elrejtése a \"Legújabb Média\"-ból", + "HideWatchedContentFromLatestMedia": "A megtekintett tartalom elrejtése a \"Nemrég hozzáadott felvételek\"-ből", "Home": "Kezdőlap", "Identify": "Azonosítás", "Images": "Képek", @@ -395,7 +395,7 @@ "TabCodecs": "Kódek", "TabContainers": "Tároló", "TabDashboard": "Vezérlőpult", - "TabLatest": "Legújabb", + "TabLatest": "Nemrég hozzáadott", "TabLogs": "Naplók", "TabMusic": "Zene", "TabMyPlugins": "Telepített bővítmények", @@ -519,7 +519,7 @@ "Disc": "Lemez", "Disconnect": "Bontás", "DisplayInMyMedia": "Megjelenítés a kezdőképernyőn", - "DisplayInOtherHomeScreenSections": "Megjelenítés a kezdőképernyőn, mint például a 'Legújabb Média', és a 'Folyamatban lévő filmek'", + "DisplayInOtherHomeScreenSections": "Megjelenítés a kezdőképernyőn, mint például a 'Nemrég hozzáadott', és a 'Korábban megtekintett'", "DisplayModeHelp": "Válaszd ki a használni kívánt elrendezést.", "DoNotRecord": "Ne rögzítsen", "DownloadsValue": "{0} letöltés", @@ -604,8 +604,8 @@ "HeaderInstantMix": "Azonnali keverés", "HeaderKeepRecording": "Felvétel készítése", "HeaderKodiMetadataHelp": "Az NFO-metaadatok engedélyezéséhez vagy letiltásához szerkesszen egy könyvtárat, és keresse meg a „Metaadat-mentők” részt.", - "HeaderLatestMusic": "Legújabb zenék", - "HeaderLatestRecordings": "Legújabb felvételek", + "HeaderLatestMusic": "Nemrég hozzáadott zenék", + "HeaderLatestRecordings": "Nemrég hozzáadott felvételek", "HeaderLoginFailure": "Bejelentkezési hiba", "HeaderMusicQuality": "Zene minősége", "HeaderNewApiKey": "Új API kulcs", @@ -1510,7 +1510,7 @@ "Framerate": "Képkockasebesség", "DirectPlayHelp": "A forrásfájl teljes mértékben kompatibilis ezzel az klienssel, és a munkamenet módosítások nélkül fogadja a fájlt.", "HeaderContinueReading": "Olvasás folytatása", - "EnableGamepadHelp": "Figyeljen bármilyen csatlakoztatott kontroller bemenetére.", + "EnableGamepadHelp": "Figyeljen bármilyen csatlakoztatott bemenetre. (Szükséges: TV megjelenítési mód)", "LabelEnableGamepad": "Engedélyezze a Gamepad-ot", "Controls": "Vezérlők", "TextSent": "Szöveg elküldve.", @@ -1685,5 +1685,30 @@ "MessageNoItemsAvailable": "Jelenleg nincs elérhető elem.", "MessageNoFavoritesAvailable": "Jelenleg nincs elérhető kedvenc.", "Unreleased": "Még nem jelent meg", - "DownloadAll": "Összes letöltése" + "DownloadAll": "Összes letöltése", + "PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Beágyazott címek használata.", + "SaveRecordingNFO": "A rögzített EPG metaadatok mentése NFO-ba", + "SubtitleBlack": "Fekete", + "SubtitleBlue": "Kék", + "SubtitleCyan": "Cián", + "SubtitleGray": "Szürke", + "SubtitleGreen": "Zöld", + "SubtitleLightGray": "Világos szürke", + "SubtitleMagenta": "Bíborvörös", + "SubtitleRed": "Piros", + "SubtitleWhite": "Fehér", + "SubtitleYellow": "Sárga", + "SecondarySubtitles": "Másodlagos feliratok", + "StereoDownmixAlgorithmHelp": "Többcsatornás hangot sztereóvá keverő algoritmus használata.", + "LabelDummyChapterCountHelp": "Az egyes médiafájlokból kibontható fejezetképek maximális száma.", + "PreferEmbeddedExtrasTitlesOverFileNames": "Részesítse előnyben a beágyazott címeket a fájlnevekkel szemben", + "SaveRecordingImages": "Rögzített EPG-képek mentése", + "Experimental": "Tapasztalati", + "LabelStereoDownmixAlgorithm": "Térhatású csatornák egyesítése", + "HeaderDummyChapter": "Fejezetképek", + "HeaderRecordingMetadataSaving": "Metaadatok rögzítése", + "LabelDummyChapterDuration": "Intervallum:", + "LabelDummyChapterCount": "Határvonal:", + "LabelChapterImageResolution": "Felbontás:", + "LabelChapterImageResolutionHelp": "A kinyert fejezetképek felbontása." } diff --git a/src/strings/is-is.json b/src/strings/is-is.json index b4b01a1fda..4d131ff8ad 100644 --- a/src/strings/is-is.json +++ b/src/strings/is-is.json @@ -447,6 +447,36 @@ "Mute": "Þagga", "MusicArtist": "Tónlistarmaður", "MusicAlbum": "Tónlistaralbúm", - "ButtonCast": "Senda efni út í tæki", - "AddToFavorites": "Bæta við eftirlæti" + "ButtonCast": "Senda út í tæki", + "AddToFavorites": "Bæta við eftirlæti", + "ButtonPlayer": "Spilari", + "AgeValue": "({0} ára)", + "ApiKeysCaption": "Listi yfir núverandi virkum API lyklum", + "ButtonActivate": "Virkja", + "ButtonClose": "Loka", + "ButtonSpace": "Bil", + "Authorize": "Heimila", + "ChangingMetadataImageSettingsNewContent": "Breytingar á niðurhali lýsigögnum eða myndum mun aðeins gilda um ný efni sem bætt hafa verið í safnið. Til að breyta núverandi titla þarftu að endurnýja lýsigögnin handvirkt.", + "ColorTransfer": "Litaflutningur", + "Data": "Gögn", + "ClearQueue": "Hreinsa biðröð", + "DailyAt": "Daglega um {0}", + "DashboardServerName": "Þjónn: {0}", + "DashboardVersionNumber": "Útgáfa: {0}", + "ColorSpace": "Litarými", + "Copied": "Aftritað", + "Copy": "Afrita", + "CopyFailed": "Gat ekki afritað", + "ButtonExitApp": "Fara úr forriti", + "EnableFasterAnimations": "Hraðari hreyfimyndir", + "EnablePlugin": "Virkja", + "Bwdif": "BWDIF", + "DisablePlugin": "Slökkva", + "EnableAutoCast": "Setja sem Sjálfgefið", + "EnableDetailsBanner": "Upplýsingar borði", + "DeleteAll": "Eyða Öllu", + "ButtonBackspace": "Backspace", + "ButtonUseQuickConnect": "Nota hraðtengingu", + "Digital": "Stafrænt", + "DownloadAll": "Sækja allt" } diff --git a/src/strings/it.json b/src/strings/it.json index 1a325f784e..3ccb4247ad 100644 --- a/src/strings/it.json +++ b/src/strings/it.json @@ -137,7 +137,7 @@ "Disconnect": "Disconnetti", "Display": "Schermo", "DisplayInMyMedia": "Visualizza nella schermata di home", - "DisplayInOtherHomeScreenSections": "Mostra le sezioni della schermata home come 'Ultimi media' e 'Continua a guardare'", + "DisplayInOtherHomeScreenSections": "Visualizza nelle sezioni della schermata iniziale come \"Media aggiunti di recente\" e \"Continua a guardare\"", "DisplayMissingEpisodesWithinSeasons": "Visualizza gli episodi mancanti nelle stagioni", "DisplayMissingEpisodesWithinSeasonsHelp": "Questo deve anche essere abilitato per le librerie TV nella configurazione del server.", "DisplayModeHelp": "Seleziona lo stile del layout che vuoi per l'interfaccia.", diff --git a/src/strings/mg.json b/src/strings/mg.json new file mode 100644 index 0000000000..54e2956c06 --- /dev/null +++ b/src/strings/mg.json @@ -0,0 +1,42 @@ +{ + "Add": "Manampy", + "AddToPlaylist": "Ampiana ao amin'ny playlist", + "AddToPlayQueue": "Ampiana ao amin'ny filaharana", + "AirDate": "Daty fandefasana", + "Aired": "Nalefa", + "AlbumArtist": "Artista tompon'ny Album", + "Alerts": "Lakolosy", + "AllChannels": "Fahitalavitra rehetra", + "AllowMediaConversion": "Avela hiasa hanao famadiahana format", + "AllowEmbeddedSubtitlesAllowNoneOption": "Tsy asiana mihitsy", + "AllowEmbeddedSubtitlesAllowTextOption": "Asiana soratra", + "Absolute": "Feno", + "AccessRestrictedTryAgainLater": "Voafetra ny fidirana amin'izao fotoana izao. Andramo indray afaka fotoana fohy.", + "Actor": "Mpilalao", + "AddedOnValue": "Nampiana", + "AdditionalNotificationServices": "Tsidiho ny katalaogin'ny plugin raha hanampy serivisy fampahafantarana fanampiny.", + "AddToCollection": "Ampiana ao amin'ny fitahirizana", + "AddToFavorites": "Ampiana ao amin'ireo ankafihizina", + "AgeValue": "({0} taona)", + "Album": "Album", + "Albums": "Albums", + "All": "Rehetra", + "AllComplexFormats": "Ireo endrika sarotra rehetra (ASS, SSA, VobSub, PGS, SUB, IDX, …)", + "AllEpisodes": "Fizaràna rehetra", + "AllLanguages": "Tenim-pirenena rehetra", + "AllLibraries": "Tahiry rehetra", + "AllowedRemoteAddressesHelp": "Lisitry ny adiresy IP na IP/netmask nosarahan'ny faingo ho an'ny tambajotra izay avela hifandray lavitra. Raha avela ho banga dia afaka miditra avokoa ny IP rehetra.", + "AllowEmbeddedSubtitles": "Atsahatra ny karazana dikanteny ao anatiny", + "AllowEmbeddedSubtitlesAllowAllOption": "Avela malalaka", + "AllowEmbeddedSubtitlesAllowImageOption": "Asiana sary", + "AllowEmbeddedSubtitlesHelp": "Atsahatra ny dikanteny raikitra ao anaty media. Mila fanavaozana ny tahiry rehetra.", + "AllowFfmpegThrottling": "Ahena ny asa famadihana format", + "AllowFfmpegThrottlingHelp": "Rehefa lasa lavitra mihoatra ny playback ny asa famadihana format dia atsahatra kely ny fampiasana CPU. Tena ilaina izany rehefa mijery fotsiny fa tsy mandingana na mamerina matetika. Aza alefa raha mahita olana amin'ny playback.", + "AllowHevcEncoding": "Avela hamadika format HEVC", + "AllowHWTranscodingHelp": "Avela ny tuner hiasa sady mamadika format. Mety hanampy amin'ny fampihenana ny asa takian'ny serveur izany.", + "AllowMediaConversionHelp": "Avela na lavina ny fidirana amin'ny asa famadihana format.", + "AllowOnTheFlySubtitleExtraction": "Avela hamaky dikanteny eny ampandehanana.", + "AllowOnTheFlySubtitleExtractionHelp": "Azo alaina avy amin'ilay video ny dikanteny ao anatiny ary aseho amin'ny soratra tsotra, mba hisorohana ny famadihana video. Mety haharitra ela izany ka hampiato ny famakiana horonan-tsary mandritra ny dingan'ny fitrandrahana. Atsaharo raha avela ho dikanteny miaraka amin'ny famadihana horonan-tsary.", + "AllowRemoteAccess": "Avela hifandray amin'ny hafa ity serveur ity", + "AllowRemoteAccessHelp": "Raha tsy marihina dia ho voasakana ny fifandraisana lavitra rehetra." +} diff --git a/src/strings/nl.json b/src/strings/nl.json index 44d8eab396..7d493b7a6f 100644 --- a/src/strings/nl.json +++ b/src/strings/nl.json @@ -107,7 +107,7 @@ "ConfirmDeletion": "Bevestigen Verwijdering", "ConfirmEndPlayerSession": "Wilt u Jellyfin afsluiten op {0}?", "Connect": "Verbind", - "ContinueWatching": "Verder kijken", + "ContinueWatching": "Verderkijken", "Continuing": "Wordt vervolgd", "CriticRating": "Critici-beoordeling", "CustomDlnaProfilesHelp": "Maak een aangepast profiel om een nieuw apparaat aan te maken of overschrijf een systeemprofiel.", @@ -324,7 +324,7 @@ "HeaderPreferredMetadataLanguage": "Gewenste metadata taal", "HeaderProfileInformation": "Profiel Informatie", "HeaderProfileServerSettingsHelp": "Deze waarden bepalen hoe de server zich zal presenteren aan het apparaat.", - "HeaderRecentlyPlayed": "Recent afgespeeld", + "HeaderRecentlyPlayed": "Onlangs afgespeeld", "HeaderRecordingOptions": "Opname instellingen", "HeaderRecordingPostProcessing": "Nabewerking opname", "HeaderRemoteControl": "Afstandsbediening", @@ -694,7 +694,7 @@ "LabelffmpegPathHelp": "Het pad naar het FFmpeg applicatiebestand, of de folder die FFmpeg bevat.", "LanNetworksHelp": "Komma-gescheiden lijst van IP-adressen of IP/netmask adressen voor netwerken die als lokaal gezien worden wanneer bandbreedtebeperkingen van toepassing zijn. Indien ingesteld, worden alle overige IP-adressen gezien als externe adressen en zullen worden onderworpen aan de bandbreedte-instellingen voor externe adressen. Indien blanco, zal alleen het subnet van de server als lokaal netwerk gezien worden.", "Large": "Groot", - "LatestFromLibrary": "Onlangs toegevoegd in {0}", + "LatestFromLibrary": "Onlangs toegevoegd aan {0}", "LearnHowYouCanContribute": "Lees meer over hoe u kunt bijdragen.", "LibraryAccessHelp": "Selecteer de bibliotheken om met deze gebruiker te delen. Beheerders kunnen alle mappen bewerken via de metadata-beheerder.", "List": "Lijst", @@ -1544,7 +1544,7 @@ "LabelSyncPlaySettingsSkipToSyncHelp": "Synchronisatie correctiemethode die bestaat uit het zoeken naar de geschatte positie. Synchronisatie Correctie moet ingeschakeld zijn.", "MessageSent": "Bericht verzonden.", "Mixer": "Mixer", - "UseEpisodeImagesInNextUpHelp": "'Hierna'- en 'Verder kijken'-secties zullen afleveringsafbeeldingen gebruiken als thumbnails in plaats van de primaire thumbnail van de serie.", + "UseEpisodeImagesInNextUpHelp": "'Hierna'- en 'Verderkijken'-secties zullen afleveringsafbeeldingen gebruiken als thumbnails in plaats van de primaire thumbnail van de serie.", "SetUsingLastTracks": "Ondertitel/Audio-sporen instellen met vorig item", "SetUsingLastTracksHelp": "Probeer de ondertiteling/het audiospoor in te stellen op de video die het meest overeenkomt met de laatste video.", "TextSent": "Tekst verzonden.", @@ -1560,7 +1560,7 @@ "LabelMaxDaysForNextUpHelp": "Zet het maximaal aantal dagen dat een serie in de 'Hierna'-lijst staat zonder het te kijken.", "PreviousChapter": "Vorig hoofdstuk", "Remixer": "Remixer", - "UseEpisodeImagesInNextUp": "Gebruik afleveringscovers in de secties 'Hierna' en 'Verder kijken'", + "UseEpisodeImagesInNextUp": "Gebruik afleveringscovers in de secties 'Hierna' en 'Verderkijken'", "EnableGamepadHelp": "Luister naar input van alle aangesloten controllers. (Vereist weergavemodus 'Tv')", "VideoCodecNotSupported": "De videocodec wordt niet ondersteund", "AudioBitrateNotSupported": "De bitrate van de audio wordt niet ondersteund", @@ -1703,5 +1703,15 @@ "LabelDummyChapterDurationHelp": "De extractieinterval voor hoofdstukafbeeldingen in seconden.", "ResolutionMatchSource": "Gelijk aan bron", "HeaderRecordingMetadataSaving": "Metadata opname", - "SecondarySubtitles": "Secundaire ondertiteling" + "SecondarySubtitles": "Secundaire ondertiteling", + "SubtitleBlack": "Zwart", + "SubtitleBlue": "Blauw", + "SubtitleCyan": "Cyaan", + "SubtitleGray": "Grijs", + "SubtitleGreen": "Groen", + "SubtitleLightGray": "Lichtgrijs", + "SubtitleMagenta": "Magenta", + "SubtitleRed": "Rood", + "SubtitleWhite": "Wit", + "SubtitleYellow": "Geel" } diff --git a/src/strings/pl.json b/src/strings/pl.json index ace38a17b4..d6f9ab9972 100644 --- a/src/strings/pl.json +++ b/src/strings/pl.json @@ -150,7 +150,7 @@ "Disconnect": "Rozłącz", "Display": "Wyświetlanie", "DisplayInMyMedia": "Wyświetlaj na ekranie startowym", - "DisplayInOtherHomeScreenSections": "Wyświetlaj na ekranie startowym sekcje 'Ostatnio dodane' i 'Kontynuuj odtwarzanie'", + "DisplayInOtherHomeScreenSections": "Wyświetlaj w sekcjach ekranu startowego, jak \"Ostatnio dodane media\" i \"Oglądaj dalej\"", "DisplayMissingEpisodesWithinSeasons": "Wyświetlaj w sezonach brakujące odcinki", "DisplayMissingEpisodesWithinSeasonsHelp": "Ta opcja musi zostać dodatkowo aktywowana w bibliotece seriali, w konfiguracji serwera.", "DisplayModeHelp": "Wybierz styl układu interfejsu.", @@ -301,11 +301,11 @@ "HeaderKeepRecording": "Zachowaj nagranie", "HeaderKeepSeries": "Zachowaj nagranie serialu", "HeaderKodiMetadataHelp": "By aktywować lub dezaktywować metadane NFO, edytuj ustawienia biblioteki w sekcji 'dostawcy metadanych'.", - "HeaderLatestEpisodes": "Odcinki ostatnio dodane", - "HeaderLatestMedia": "Media ostatnio dodane", - "HeaderLatestMovies": "Filmy ostatnio dodane", - "HeaderLatestMusic": "Muzyka ostatnio dodana", - "HeaderLatestRecordings": "Nagrania ostatnio dodane", + "HeaderLatestEpisodes": "Ostatnio dodane odcinki", + "HeaderLatestMedia": "Ostatnio dodane media", + "HeaderLatestMovies": "Ostatnio dodane filmy", + "HeaderLatestMusic": "Ostatnio dodana muzyka", + "HeaderLatestRecordings": "Ostatnio dodane nagrania", "HeaderLibraries": "Biblioteki", "HeaderLibraryAccess": "Dostęp do Bibliotek", "HeaderLibraryFolders": "Foldery biblioteki", @@ -1701,5 +1701,18 @@ "SaveRecordingNFO": "Zapisz nagrane metadane EPG w NFO", "SaveRecordingImages": "Zapisywanie obrazów EPG", "SaveRecordingImagesHelp": "Zapisz obrazy od dostawcy list EPG razem z mediami.", - "HeaderRecordingMetadataSaving": "Nagrywanie metadanych" + "HeaderRecordingMetadataSaving": "Nagrywanie metadanych", + "PreferEmbeddedExtrasTitlesOverFileNames": "Preferuj wbudowane tytuły zamiast nazw plików w materiałach dodatkowych", + "PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Materiały dodatkowe często mają tę samą wbudowaną nazwę co rodzic, zaznacz, aby mimo to używać dla nich wbudowanych tytułów.", + "SubtitleBlack": "Czarny", + "SubtitleGreen": "Zielony", + "SubtitleMagenta": "Magenta", + "SubtitleBlue": "Niebieski", + "SubtitleCyan": "Cyjanowy", + "SubtitleGray": "Szary", + "SubtitleLightGray": "Jasnoszary", + "SubtitleRed": "Czerwony", + "SubtitleWhite": "Biały", + "SubtitleYellow": "Żółty", + "SecondarySubtitles": "Napisy drugorzędne" } diff --git a/src/strings/sk.json b/src/strings/sk.json index 282b1f0f8b..64bac51b24 100644 --- a/src/strings/sk.json +++ b/src/strings/sk.json @@ -173,11 +173,11 @@ "HeaderImageSettings": "Nastavenia obrázkov", "HeaderInstall": "Inštalovať", "HeaderInstantMix": "Okamžitý mix", - "HeaderLatestEpisodes": "Najnovšie epizódy", - "HeaderLatestMedia": "Najnovšie médiá", - "HeaderLatestMovies": "Najnovšie filmy", - "HeaderLatestMusic": "Najnovšia hudba", - "HeaderLatestRecordings": "Najnovšie nahrávky", + "HeaderLatestEpisodes": "Najnovšie pridané epizódy", + "HeaderLatestMedia": "Najnovšie pridané médiá", + "HeaderLatestMovies": "Najnovšie pridané filmy", + "HeaderLatestMusic": "Najnovšia pridaná hudba", + "HeaderLatestRecordings": "Najnovšie pridané nahrávky", "HeaderLibraries": "Knižnice", "HeaderLibraryAccess": "Prístup ku knižnici", "HeaderLibraryFolders": "Priečinky knižnice", @@ -659,7 +659,7 @@ "TabCodecs": "Kodeky", "TabContainers": "Kontajnery", "TabDashboard": "Dashboard", - "TabLatest": "Najnovšie", + "TabLatest": "Najnovšie pridané", "TabMusic": "Hudba", "TabMyPlugins": "Moje zásuvné moduly", "TabNetworks": "TV Siete", @@ -723,7 +723,7 @@ "ButtonRevoke": "Odvolať", "ButtonSelectView": "Výber zobrazenia", "CancelRecording": "Zrušiť nahrávanie", - "AirDate": "Dátum vysielania", + "AirDate": "Dátum prvého vysielania", "Aired": "Odvysielané", "Alerts": "Upozornenia", "AllowOnTheFlySubtitleExtraction": "Povoliť extrahovanie titulkov za behu", @@ -968,7 +968,7 @@ "EnableStreamLooping": "Autom. slučka živého vysielania", "EnableExternalVideoPlayersHelp": "Ponuka externého prehrávača sa zobrazí pri spustení prehrávania videa.", "EnableBackdropsHelp": "Zobraziť pozadia pre niektoré stránky pri prechádzaní knižnice.", - "DisplayInOtherHomeScreenSections": "Zobraziť v sekciách domovskej obrazovky, ako sú najnovšie médiá a pokračovať v pozeraní", + "DisplayInOtherHomeScreenSections": "Zobraziť 'Najnovšie pridané médiá' a 'Pokračovať v pozeraní' v sekciách domovskej obrazovky", "DirectStreamHelp1": "Video je kompatibilné zo zariadením, ale zvuková stopa je v nekompatibilnom formáte (DTS, Dolby TrueHD, atď.) alebo má nekompatibilný počet kanálov. Video bude pred odoslaním do zariadenia za behu bezstrátovo prebalené. Iba zvuk bude prekódovaný.", "Depressed": "Stlačený", "DefaultSubtitlesHelp": "Titulky sú načítané v závislosti od predvolených a vynútených nastavení vo vložených metadátach. Jazykové predvoľby sú zobrané do úvahy až vtedy, keď je k dispozícií viacero možností.", @@ -1701,5 +1701,6 @@ "LabelDummyChapterDurationHelp": "Interval extrakcie obrázkov kapitol v sekundách.", "SaveRecordingNFO": "Uložiť metadáta nahrávky zo sprievodcu EPG do NFO", "SaveRecordingImagesHelp": "Uloží obrázky z EPG položiek sprievodcu spolu s médiami.", - "HeaderRecordingMetadataSaving": "Metadáta nahrávok" + "HeaderRecordingMetadataSaving": "Metadáta nahrávok", + "SecondarySubtitles": "Sekundárne titulky" } diff --git a/src/strings/uk.json b/src/strings/uk.json index 2168357faf..7a7527dc02 100644 --- a/src/strings/uk.json +++ b/src/strings/uk.json @@ -351,7 +351,7 @@ "EnablePhotosHelp": "Зображення будуть виявлені та відображені разом з іншими медіафайлами.", "EnableNextVideoInfoOverlayHelp": "Наприкінці відео відображати інформацію про наступне відео у поточному списку відтворення.", "EnableNextVideoInfoOverlay": "Показувати інформацію про наступне відео під час відтворення", - "EnableFasterAnimationsHelp": "Використовуйте швидші анімації та переходи.", + "EnableFasterAnimationsHelp": "Використовувати швидші анімації та переходи.", "EnableFasterAnimations": "Прискорена анімація", "EnableExternalVideoPlayersHelp": "Меню зовнішнього плеєра буде показано під час запуску відтворення відео.", "LabelCollection": "Добірка:", @@ -382,7 +382,7 @@ "LabelAlbumArtMaxHeight": "Максимальна висота зображення альбому:", "LabelAlbum": "Альбом:", "ErrorDeletingItem": "Сталася помилка під час видалення елемента з сервера. Перевірте, що Jellyfin має доступ до папки з медіа, та спробуйте ще раз.", - "ErrorAddingXmlTvFile": "Сталася помилка під час доступу к XMLTV файлу. Переконайтесь, що файл існує, та спробуйте ще раз.", + "ErrorAddingXmlTvFile": "Сталася помилка під час доступу до XMLTV файлу. Переконайтесь, що файл існує, та спробуйте ще раз.", "ErrorAddingTunerDevice": "Сталася помилка під час додавання тюнера. Переконайтесь, що пристрій доступний, та спробуйте ще раз.", "ErrorAddingMediaPathToVirtualFolder": "Сталася помилка під чам додавання шляха медіа. Переконайтесь, що шлях вірний, та Jellyfin має до нього доступ.", "EncoderPresetHelp": "Виберіть більше значення, щоб покращити швидкість, або менше, щоб поліпшити якість.", @@ -467,7 +467,7 @@ "Guide": "Посібник", "FFmpegSavePathNotFound": "Неможливо знайти FFmpeg в відомих системних директоріях. Додаток FFprobe також потрібен для роботи і мусить знаходитись в тій самій директорії. Ці компоненти зазвичай постачаються разом. Будь ласка перевірте шлях до них.", "ErrorStartHourGreaterThanEnd": "Час закінчення має бути більшим за час початку.", - "ErrorGettingTvLineups": "Сталася помилка під час завантаження списку телеканалів. Будь ласка перевірте корректність інформації та спробуйте ще раз.", + "ErrorGettingTvLineups": "Сталася помилка під час завантаження списку телеканалів. Будь ласка перевірте коректність інформації та спробуйте ще раз.", "LabelLogs": "Журнали:", "LabelOptionalNetworkPathHelp": "Якщо ця папка є спільною у вашій мережі, надання шляхів спільного доступу до мережі може дозволити клієнтам на інших пристроях отримувати прямий доступ до медіафайлів. Наприклад, {0} або {1}.", "LabelOptionalNetworkPath": "Спільна мережева тека:", @@ -494,7 +494,7 @@ "LabelMinResumeDurationHelp": "Найкоротша тривалість відео в секундах, з збереженням місця продовження перегляду.", "LabelMinResumeDuration": "Мінімальна відрізок для відновлення:", "LabelMetadataDownloadLanguage": "Бажана мова завантаження:", - "LabelMetadataDownloadersHelp": "Увімкніть та ранжуйте бажані завантажувачі метаданих у порядку їх пріоритетності. Завантажувачі нижчого пріоритету будуть використовуватися лише для заповнення відсутньої інформації.", + "LabelMetadataDownloadersHelp": "Увімкнути та ранжувати бажані завантажувачі метаданих у порядку їх пріоритетності. Завантажувачі нижчого пріоритету будуть використовуватися лише для заповнення відсутньої інформації.", "LabelMetadata": "Метадані:", "LabelMessageTitle": "Назва повідомлення:", "LabelMessageText": "Текст повідомлення:", @@ -514,7 +514,7 @@ "LabelManufacturer": "Виробник:", "LabelLoginDisclaimerHelp": "Повідомлення, яке відображатиметься внизу сторінки для входу.", "LabelLoginDisclaimer": "Відмова від відповідальності:", - "LabelLockItemToPreventChanges": "Заблокуйте цей елемент, щоб запобігти майбутнім змінам", + "LabelLockItemToPreventChanges": "Заблокувати цей елемент, щоб запобігти майбутнім змінам", "LabelLocalHttpServerPortNumberHelp": "Номер порту TCP для сервера HTTP.", "LabelLocalHttpServerPortNumber": "Номер локального порту HTTP:", "LabelLineup": "Склад:", @@ -535,11 +535,11 @@ "LabelKeepUpTo": "Дотримуйтесь до:", "LabelIsForced": "Примусово", "LabelInternetQuality": "Якість в Інтернет:", - "LabelInNetworkSignInWithEasyPasswordHelp": "Використовуйте простий PIN-код для входу з клієнтів у вашій локальній мережі. Ваш звичайний пароль знадобиться лише далеко від дому. Якщо PIN-код залишити порожнім, вам не знадобиться пароль у вашій домашній мережі.", + "LabelInNetworkSignInWithEasyPasswordHelp": "Використовувати простий PIN-код для входу з клієнтів у вашій локальній мережі. Ваш звичайний пароль знадобиться лише далеко від дому. Якщо PIN-код залишити порожнім, вам не знадобиться пароль у вашій домашній мережі.", "LabelInNetworkSignInWithEasyPassword": "Увімкнути вхід у мережу за допомогою мого простого PIN-коду", "LabelImportOnlyFavoriteChannels": "Обмежити до каналів, позначених як обрані", "LabelImageType": "Тип зображення:", - "LabelImageFetchersHelp": "Увімкніть та ранжируйте вибрані програми збору зображень у порядку пріоритету.", + "LabelImageFetchersHelp": "Увімкнути та ранжувати вибрані програми збору зображень у порядку пріоритету.", "LabelIdentificationFieldHelp": "Підрядок або регулярний вираз, що не чутливий до регістру.", "LabelIconMaxWidth": "Максимальна ширина значка:", "LabelCreateHttpPortMap": "Увімкніть автоматичне зіставлення портів для трафіку HTTP, а також HTTPS.", @@ -684,8 +684,8 @@ "LanNetworksHelp": "Список IP -адрес або записів IP/маски мережі, розділених комами, для мереж, які будуть враховуватися у локальній мережі під час застосування обмежень пропускної здатності. Якщо встановлено, усі інші IP -адреси вважатимуться зовнішніми мережами і на них поширюватимуться обмеження зовнішньої пропускної здатності. Якщо залишити це поле пустим, у локальній мережі вважається лише підмережа сервера.", "LabelTranscodingThreadCountHelp": "Виберіть максимальну кількість потоків для використання при перекодуванні. Зменшення кількості потоків знизить використання процесора, але може перетворити його недостатньо швидко для плавного відтворення.", "LabelTonemappingThresholdHelp": "Параметри алгоритму тонального відображення налаштовуються для кожної сцени. Поріг використовується для визначення того, змінилася сцена чи ні. Якщо відстань між поточною середньою яскравістю кадру та поточною середньою частотою перевищує порогове значення, ми б повторно обчислили середню сцену та пікову яскравість. Рекомендовані та стандартні значення - 0,8 та 0,2.", - "LabelTonemappingDesatHelp": "Застосовуйте десатурацію для світлих тонів, які перевищують цей рівень яскравості. Чим вище параметр, тим більше інформації про колір буде збережено. Це налаштування допомагає запобігти неприродним стиранням кольорів для надсвітків, шляхом (плавного) перетворення на білий. Це робить зображення більш природними, за рахунок зменшення інформації про кольори, що виходять за межі діапазону. Рекомендовані та стандартні значення - 0 та 0,5.", - "LabelSelectFolderGroups": "Автоматично групуйте вміст із таких папок у види, такі як Фільми, Музика та ТБ:", + "LabelTonemappingDesatHelp": "Застосувати десатурацію для світлих тонів, які перевищують цей рівень яскравості. Чим вище параметр, тим більше інформації про колір буде збережено. Це налаштування допомагає запобігти неприродним стиранням кольорів для надсвітків, шляхом (плавного) перетворення на білий. Це робить зображення більш природними, за рахунок зменшення інформації про кольори, що виходять за межі діапазону. Рекомендовані та стандартні значення - 0 та 0,5.", + "LabelSelectFolderGroups": "Автоматично групувати вміст із таких папок у види, такі як Фільми, Музика та ТБ:", "LabelSaveLocalMetadataHelp": "Збереження творів мистецтва в папках для медіа помістить їх у місце, де їх можна легко редагувати.", "LabelRequireHttpsHelp": "Якщо цей прапорець встановлено, сервер автоматично перенаправлятиме всі запити через HTTP на HTTPS. Це не впливає, якщо сервер не прослуховує протокол HTTPS.", "LabelRepositoryNameHelp": "Спеціальна назва, щоб відрізняти це сховище від будь -яких інших, доданих на ваш сервер.", @@ -726,7 +726,7 @@ "HeaderLatestRecordings": "Нещодавно додані записи", "HeaderKodiMetadataHelp": "Щоб увімкнути або вимкнути метадані NFO, відкрийте меню редагування медіатеки та знайдіть розділ \"Збереження метаданих\".", "HeaderKeepSeries": "Зберегти серіал", - "HeaderKeepRecording": "Продовжуйте записувати", + "HeaderKeepRecording": "Продовжувати записувати", "HeaderInstantMix": "Миттєвий мікс", "HeaderInstall": "Встановити", "HeaderImageSettings": "Налаштування зображення", @@ -778,7 +778,7 @@ "Large": "Більший", "LabelHDHomerunPortRangeHelp": "Обмежує діапазон портів UDP HD-HomeRun до цього значення. (За замовчуванням 1024 - 65535).", "HeaderSelectServerCachePathHelp": "Перегляньте або введіть шлях для файлів кешу сервера. Тека має бути з дозволом запису.", - "LabelSyncPlaySettingsExtraTimeOffsetHelp": "Вручну налаштуйте зсув часу (в мілісекундах) з вибраним пристроєм для синхронізації часу. Налаштуйте обережно.", + "LabelSyncPlaySettingsExtraTimeOffsetHelp": "Ручне налаштування зсуву часу (в мілісекундах) з вибраним пристроєм для синхронізації часу. Налаштовуйте обережно.", "LabelLocalCustomCss": "Спеціальний CSS-код для стилю, який стосується лише цього клієнта. Ви можете вимкнути користувацький CSS-код сервера.", "LabelMaxAudiobookResumeHelp": "Назви вважаються повністю відтвореними, якщо їх зупинити, коли тривалість, що залишилася, менша за це значення.", "LabelLibraryPageSizeHelp": "Встановлює кількість елементів для показу на сторінці медіатеки. Встановіть 0, щоб вимкнути підкачування сторінок.", @@ -797,7 +797,7 @@ "LabelH265Crf": "H.265, що кодує CRF:", "LabelH264Crf": "H.264, що кодує CRF:", "LabelGroupMoviesIntoCollectionsHelp": "Під час відображення списків фільмів фільми в добірці відображатимуться як один згрупований елемент.", - "LabelGroupMoviesIntoCollections": "Групуйте фільми у добірки", + "LabelGroupMoviesIntoCollections": "Групувати фільми у добірки", "LabelFriendlyName": "Спрощене ім'я:", "LabelFormat": "Формат:", "LabelForgotPasswordUsernameHelp": "Введіть своє ім’я користувача, якщо ви його пам’ятаєте.", @@ -808,7 +808,7 @@ "LabelffmpegPathHelp": "Шлях до файлу або папки програми FFmpeg, що містить FFmpeg.", "LabelffmpegPath": "Шлях FFmpeg:", "LabelFailed": "Не вдалося", - "LabelExtractChaptersDuringLibraryScan": "Витягуйте зображення розділів під час сканування медіатеки", + "LabelExtractChaptersDuringLibraryScan": "Витягувати зображення розділів під час сканування медіатеки", "LabelEvent": "Подія:", "LabelEveryXMinutes": "Кожен:", "LabelEpisodeNumber": "Номер епізоду:", @@ -829,7 +829,7 @@ "LabelEnableHardwareDecodingFor": "Увімкнути апаратне декодування для:", "LabelEnableDlnaServerHelp": "Дозволяє пристроям UPnP у вашій мережі переглядати та відтворювати вміст.", "LabelEnableDlnaServer": "Увімкнути сервер DLNA", - "LabelEnableDlnaPlayToHelp": "Виявляйте пристрої у вашій мережі та пропонуйте можливість керувати ними віддалено.", + "LabelEnableDlnaPlayToHelp": "Виявляти пристрої у вашій мережі та пропонувати можливість керувати ними віддалено.", "LabelEnableDlnaPlayTo": "Увімкніть функцію DLNA \"Play To\"", "LabelEnableDlnaDebugLoggingHelp": "Створює великі файли журналу, і їх слід використовувати лише за потреби для усунення несправностей.", "LabelEnableDlnaDebugLogging": "Увімкнути запис дебагу DLNA", @@ -849,7 +849,7 @@ "LabelDropImageHere": "Перетягніть зображення сюди або натисніть, щоб переглянути.", "LabelDownMixAudioScale": "Підсилення звуку під час мікшування:", "LabelDownloadLanguages": "Завантажити мови:", - "LabelDisplaySpecialsWithinSeasons": "Показуйте спеціальні пропозиції протягом сезонів, в яких вони виходили в етер", + "LabelDisplaySpecialsWithinSeasons": "Показувати спеціальні пропозиції протягом сезонів, в яких вони виходили в етер", "LabelDisplayOrder": "Порядок відображення:", "LabelDisplayName": "Відображуване ім'я:", "LabelDisplayMode": "Режим відображення:", @@ -868,7 +868,7 @@ "LabelDateAdded": "Дата додання:", "LabelDashboardTheme": "Тема інфопанелі сервера:", "LabelCustomRating": "Спеціальний рейтинг:", - "LabelCustomCssHelp": "Застосуйте свій власний код CSS для створення тем/брендингу у веб-інтерфейсі.", + "LabelCustomCssHelp": "Застосувати свій власний код CSS для створення тем/брендингу у веб-інтерфейсі.", "LabelCustomDeviceDisplayNameHelp": "Введіть спеціальне відображуване ім'я або залиште поле порожнім, щоб використовувати ім'я, повідомлене пристроєм.", "LabelCustomCss": "Кастомний CSS-код:", "LabelCustomCertificatePathHelp": "Шлях до файлу PKCS #12, що містить сертифікат та приватний ключ, щоб увімкнути підтримку TLS у користувацькому домені.", @@ -960,7 +960,7 @@ "LabelTonemappingRange": "Діапазон тонального відображення:", "LabelTonemappingPeakHelp": "Замінити сигнал/номінальний/еталонний пік за допомогою цього значення. Корисно, коли вбудована інформація про піки в метаданих дисплея не є надійною або коли тонове відображення з нижчого діапазону в більш високий діапазон. Рекомендовані значення і значення за замовчуванням – 100 і 0.", "LabelTonemappingPeak": "Пік тонального відображення:", - "LabelTonemappingParamHelp": "Налаштуйте алгоритм тонального відображення. Рекомендованим і стандартним значенням є NaN. Зазвичай залиште його порожнім.", + "LabelTonemappingParamHelp": "Налаштувати алгоритм тонального відображення. Рекомендованим і стандартним значенням є NaN. Зазвичай залишайте його порожнім.", "LabelTonemappingParam": "Параметр тонального відображення:", "LabelTonemappingDesat": "Відображення тональних даних:", "LabelTonemappingAlgorithm": "Виберіть алгоритм тонального відображення для використання:", @@ -999,7 +999,7 @@ "LabelSyncPlayNewGroup": "Нова група", "LabelSyncPlayLeaveGroupDescription": "Вимкніть SyncPlay", "LabelSyncPlayLeaveGroup": "Покинути групу", - "LabelSyncPlayHaltPlaybackDescription": "І ігноруйте поточні оновлення списку відтворення", + "LabelSyncPlayHaltPlaybackDescription": "Та ігнорувати поточні оновлення списку відтворення", "LabelSyncPlayHaltPlayback": "Зупинити локальне відтворення", "LabelSyncPlayAccessNone": "Вимкнено для цього користувача", "LabelSyncPlayAccessJoinGroups": "Дозволити користувачам приєднуватися до груп", @@ -1028,7 +1028,7 @@ "LabelSonyAggregationFlagsHelp": "Визначте вміст елемента 'aggregationFlags' в просторі імен 'urn:schemas-sonycom:av'.", "LabelSonyAggregationFlags": "Прапори агрегації Sony:", "LabelSlowResponseTime": "Час у мс, після якого відповідь вважається повільною:", - "LabelSlowResponseEnabled": "Зареєструйте попередження, якщо сервер повільно відповідає", + "LabelSlowResponseEnabled": "Зареєструвати попередження, якщо сервер повільно відповідає", "LabelSkipIfGraphicalSubsPresentHelp": "Збереження текстових версій субтитрів призведе до ефективнішої доставки та зменшить ймовірність перекодування відео.", "LabelSkipIfGraphicalSubsPresent": "Пропустити, якщо відео вже містить вбудовані субтитри", "LabelSkipIfAudioTrackPresentHelp": "Зніміть прапорець, щоб переконатися, що всі відео мають субтитри, незалежно від мови аудіо.", @@ -1077,7 +1077,7 @@ "LabelProfileAudioCodecs": "Аудіокодеки:", "LabelPreferredSubtitleLanguage": "Бажана мова субтитрів:", "LabelPreferredDisplayLanguage": "Бажана мова відображення:", - "LabelPostProcessorArgumentsHelp": "Використовуйте {path} як шлях до файлу запису.", + "LabelPostProcessorArgumentsHelp": "Використовувати {path} як шлях до файлу запису.", "LabelPostProcessorArguments": "Аргументи командного рядка постпроцесора:", "LabelPostProcessor": "Додаток для постобробки:", "LabelPleaseRestart": "Зміни набудуть чинності після ручного перезавантаження веб-клієнта.", @@ -1250,7 +1250,7 @@ "Premiere": "Прем'єра", "PreferEmbeddedTitlesOverFileNamesHelp": "Визначте заголовок відображення, який використовуватиметься, коли немає доступних Інтернет-метаданих або локальних метаданих.", "PreferEmbeddedTitlesOverFileNames": "Віддавайте перевагу вбудованим заголовкам над іменами файлів", - "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Використовуйте інформацію про епізод із вбудованих метаданих, якщо є.", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Використовувати інформацію про епізод із вбудованих метаданих, якщо є.", "PreferEmbeddedEpisodeInfosOverFileNames": "Віддавайте перевагу вбудованій інформації про епізоди над іменами файлів", "PosterCard": "Картка постеру", "Poster": "Постер", @@ -1290,7 +1290,7 @@ "OptionUnairedEpisode": "Невипущені епізоди", "OptionSpecialEpisode": "Спец", "OptionResumable": "Відновлюється", - "OptionRequirePerfectSubtitleMatch": "Завантажуйте лише субтитри, які ідеально підходять для відеофайлів", + "OptionRequirePerfectSubtitleMatch": "Завантажувати лише субтитри, які ідеально підходять для відеофайлів", "OptionReportByteRangeSeekingWhenTranscodingHelp": "Це потрібно для деяких пристроїв, які не дуже швидко шукають.", "OptionReportByteRangeSeekingWhenTranscoding": "Повідомте, що сервер підтримує пошук байтів під час перекодування", "OptionReleaseDate": "Дата випуску", @@ -1339,8 +1339,8 @@ "OptionDisableUserHelp": "Сервер не дозволить жодних з'єднань від цього користувача. Існуючі з’єднання будуть раптово обірвані.", "OptionDisableUser": "Вимкнути цього користувача", "OptionDatePlayed": "Дата відтворення", - "OptionDateAddedImportTime": "Використовуйте дату відсканування в медіатеку", - "OptionDateAddedFileTime": "Використовуйте дату створення файлу", + "OptionDateAddedImportTime": "Використовувати дату відсканування в медіатеку", + "OptionDateAddedFileTime": "Використовувати дату створення файлу", "OptionDateAdded": "Дата додавання", "OptionDaily": "Кожен день", "OptionCustomUsers": "Користувацький", @@ -1439,7 +1439,7 @@ "SubtitleDownloadersHelp": "Увімкніть та оцініть бажані завантажувачі субтитрів у порядку пріоритету.", "SubtitleAppearanceSettingsDisclaimer": "Наведені нижче налаштування не застосовуються до графічних субтитрів, згаданих вище, або субтитрів ASS/SSA, які вбудовують власні стилі.", "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Ці налаштування також застосовуються до будь-якого відтворення Google Cast, запущеного цим пристроєм.", - "Subtitle": "Підзаголовок", + "Subtitle": "Субтитри", "Studios": "Студії", "StopRecording": "Зупинити запис", "StopPlayback": "Зупинити відтворення", @@ -1453,7 +1453,7 @@ "Smaller": "Менший", "SmallCaps": "Маленькі літери", "SkipEpisodesAlreadyInMyLibraryHelp": "Епізоди порівнюватимуться з використанням номерів сезонів та епізодів, якщо вони доступні.", - "SkipEpisodesAlreadyInMyLibrary": "Не записуйте епізоди, які вже є в моїй медіатеці", + "SkipEpisodesAlreadyInMyLibrary": "Не записувати епізоди, які вже є в моїй медіатеці", "SimultaneousConnectionLimitHelp": "Максимальна кількість дозволених одночасних потоків. Введіть 0 без обмежень.", "Shuffle": "Перемішати", "ShowMore": "Показати більше", @@ -1470,7 +1470,7 @@ "SeriesYearToPresent": "{0} – понині", "SeriesSettings": "Параметри серіалу", "SeriesRecordingScheduled": "Планується запис серіалу.", - "SeriesDisplayOrderHelp": "Упорядковуйте епізоди за датою ефіру, порядком DVD або абсолютною нумерацією.", + "SeriesDisplayOrderHelp": "Упорядковувати епізоди за датою ефіру, порядком DVD або абсолютною нумерацією.", "SeriesCancelled": "Серіал скасовано.", "Series": "Серіал", "SendMessage": "Відправити повідомлення", @@ -1518,7 +1518,7 @@ "SubtitleCodecNotSupported": "Кодек субтитрів не підтримується", "ContainerNotSupported": "Контейнер не підтримується", "AudioCodecNotSupported": "Аудіокодек не підтримується", - "EnableGamepadHelp": "Очікуйте вхідних даних від будь-яких підключених контролерів. (Вимагається: режим відображення «TV»)", + "EnableGamepadHelp": "Очікувати вхідних даних від будь-яких підключених контролерів. (Вимагається: режим відображення «TV»)", "LabelEnableGamepad": "Увімкнути геймпад", "Controls": "Елементи керування", "AllowVppTonemappingHelp": "Повне відображення тонів на основі драйверів Intel. Наразі працює лише на певному обладнанні з відео HDR10. Це має вищий пріоритет порівняно з іншою реалізацією OpenCL.", @@ -1583,7 +1583,7 @@ "UserProfilesIntro": "Jellyfin включає підтримку профілів користувачів із детальними налаштуваннями відображення, станом відтворення та батьківським контролем.", "UserAgentHelp": "Надайте спеціальний HTTP-заголовок «User-Agent».", "UseEpisodeImagesInNextUpHelp": "Розділи «Далі» та «Продовжити перегляд» використовуватимуть зображення епізодів як ескізи замість основного ескізу шоу.", - "UseEpisodeImagesInNextUp": "Використовуйте зображення епізодів у розділах «Далі» та «Продовжити перегляд»", + "UseEpisodeImagesInNextUp": "Використовувати зображення епізодів у розділах «Далі» та «Продовжити перегляд»", "UseDoubleRateDeinterlacing": "Подвоїте частоту кадрів під час деінтерлейсу", "Upload": "Завантажити", "Up": "Вгору", @@ -1700,5 +1700,16 @@ "LabelDummyChapterCount": "Ліміт:", "LabelDummyChapterCountHelp": "Максимальна кількість зображень розділів, які буде видобуто для кожного медіафайлу.", "LabelChapterImageResolution": "Роздільна здатність:", - "HeaderRecordingMetadataSaving": "Метадані запису" + "HeaderRecordingMetadataSaving": "Метадані запису", + "SubtitleBlack": "Чорний", + "SubtitleBlue": "Синій", + "SubtitleCyan": "Блакитний", + "SubtitleGray": "Сірий", + "SubtitleLightGray": "Світло-сірий", + "SubtitleMagenta": "Пурпуровий", + "SubtitleRed": "Червоний", + "SubtitleWhite": "Білий", + "SubtitleYellow": "Жовтий", + "SecondarySubtitles": "Додаткові субтитри", + "SubtitleGreen": "Зелений" } diff --git a/src/strings/vi.json b/src/strings/vi.json index ad55122705..c52f73cf88 100644 --- a/src/strings/vi.json +++ b/src/strings/vi.json @@ -14,20 +14,20 @@ "FileNotFound": "Không tìm thấy tệp tin.", "FileReadCancelled": "Tệp đọc đã bị hủy.", "FileReadError": "Có một lỗi xảy ra khi đọc tệp tin này.", - "HeaderCustomDlnaProfiles": "Hồ sơ khách hàng", + "HeaderCustomDlnaProfiles": "Cấu Hình Tùy Chỉnh", "HeaderFeatureAccess": "Truy cập tính năng:", "HeaderFrequentlyPlayed": "Phát thường xuyên", "HeaderLatestEpisodes": "Tập Phim Thêm Gần Đây", "HeaderLatestMovies": "Phim Thêm Gần Đây", "HeaderRecentlyPlayed": "Phát gần đây", "HeaderStatus": "Trạng thái", - "HeaderSystemDlnaProfiles": "Hồ sơ hệ thống", + "HeaderSystemDlnaProfiles": "Cấu Hình Hệ Thống", "HeaderUsers": "Người dùng", "LabelAudioLanguagePreference": "Ngôn ngữ thoại ưa thích:", "LabelCountry": "Quốc gia:", "LabelCurrentPassword": "Mật khẩu hiện tại:", "LabelDay": "Ngày trong tuần:", - "LabelEnableDlnaPlayTo": "Bật tính năng 'Phát tới' DLNA", + "LabelEnableDlnaPlayTo": "Bật tính năng 'Phát Với' DLNA", "LabelEvent": "Sự kiện:", "LabelFinish": "Xong", "LabelLanguage": "Ngôn ngữ:", @@ -175,7 +175,7 @@ "DeathDateValue": "Đã chết: {0}", "DatePlayed": "Ngày phát", "DateAdded": "Ngày thêm", - "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.", + "CustomDlnaProfilesHelp": "Tạo cấu hình tùy chỉnh nhắm đến thiết bị mới hoặc ghi đè cấu hình hệ thống.", "CriticRating": "Đánh giá của nhà phê bình", "CopyStreamURLSuccess": "Đã sao chép URL thành công.", "CopyStreamURL": "Sao chép URL luồng phát", @@ -254,7 +254,7 @@ "HeaderDateIssued": "Ngày Phát Hành", "HeaderContinueWatching": "Xem Tiếp", "HeaderContinueListening": "Tiếp Tục Nghe", - "HeaderCodecProfileHelp": "Hồ sơ mã hóa 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 cấu hình để phát lại trực tiếp.", + "HeaderCodecProfileHelp": "Cấu hình mã hóa cho biết các giới hạn của thiết bị khi phát các bộ mã hóa. Nếu giới hạn được áp dụng thì phương tiện sẽ được chuyển mã, ngay cả khi bộ mã hóa được cấu hình để phát lại trực tiếp.", "HeaderContainerProfileHelp": "Cấu hình vùng chứa cho biết các giới hạn của thiết bị khi phát các định dạng cụ thể. Nếu giới hạn được áp dụng thì phương tiện sẽ được chuyển mã, ngay cả khi định dạng được định cấu hình để phát lại trực tiếp.", "HeaderContainerProfile": "Hồ Sơ Định Dạng", "HeaderConnectionFailure": "Kế Nối Thất Bại", @@ -452,9 +452,9 @@ "HeaderTracks": "Bản ghi", "HeaderThisUserIsCurrentlyDisabled": "Người dùng này hiện tại đang bị khoá", "HeaderTaskTriggers": "Kích Hoạt Tác Vụ", - "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ụ Đề", + "HeaderSubtitleProfilesHelp": "Cấu hình phụ đề chỉ ra những định dạng phụ đề được hỗ trợ bởi thiết bị phát.", + "HeaderSubtitleProfiles": "Cấu Hình Phụ Đề", + "HeaderSubtitleProfile": "Cấu Hình Phụ Đề", "HeaderSubtitleDownloads": "Tải Phụ Đề", "HeaderSubtitleAppearance": "Giao Diện Phụ Đề", "HeaderStopRecording": "Ngừng Ghi Hình/Ghi Âm", @@ -503,7 +503,7 @@ "LabelAlbumArtPN": "Bìa Tuyển Tập PN:", "LabelAlbumArtMaxWidth": "Chiều rộng tối đa bìa tuyển tập:", "LabelAlbumArtMaxHeight": "Chiều cao tối đa bìa tuyển tập:", - "LabelAlbumArtHelp": "PN được sử dụng cho bìa tuyển tập, 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.", + "LabelAlbumArtHelp": "PN được dùng cho ảnh bìa album, trong thuộc tính 'dlna:profileID' trên 'upnp:albumArtURI'. Một số thiết bị yêu cầu một giá trị cụ thể, bất kể kích thước của hình ảnh.", "LabelAlbum": "Tuyển Tập:", "LabelAirsBeforeSeason": "Phát sóng trước phần:", "LabelAirsBeforeEpisode": "Phát sóng trước tập:", @@ -544,8 +544,8 @@ "HeaderTypeImageFetchers": "Trình tìm nạp hình ảnh ({0}):", "HeaderTuners": "Bộ Điều Khiển Thu Phát Sóng", "HeaderTunerDevices": "Thiết Bị Dò", - "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ã", + "HeaderTranscodingProfileHelp": "Thêm cấu hình chuyển mã để cho biết định dạng nào sẽ được sử dụng khi cần chuyển mã.", + "HeaderTranscodingProfile": "Cấu Hình Chuyển Mã", "LabelEnableAutomaticPortMap": "Kích hoạt tính năng tự động kết nối các port", "LabelEmbedAlbumArtDidlHelp": "Một số thiết bị thích cách này hơn để tải ảnh bìa album. Số khác có thể bị lỗi nếu tuỳ chọn này được bật.", "LabelEmbedAlbumArtDidl": "Bìa tuyển tập trong DIDL", @@ -567,7 +567,7 @@ "LabelDidlMode": "Chế độ DIDL:", "LabelDeviceDescription": "Mô tả thiết bị:", "LabelDeinterlaceMethod": "Phương pháp khử xen kẽ:", - "LabelDefaultUserHelp": "Xác định thư viện người dùng sẽ hiển thị trên các thiết bị được kết nối. Tuỳ chọn này có thể được ghi đè trên hồ sơ của từng thiết bị.", + "LabelDefaultUserHelp": "Xác định thư viện người dùng sẽ hiển thị trên các thiết bị được kết nối. Điều này có thể được ghi đè trên cấu hình 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:", @@ -590,10 +590,10 @@ "LabelEnableHttps": "Bật HTTPS", "LabelEnableHardwareDecodingFor": "Bật 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", + "LabelEnableDlnaServer": "Bật máy chủ DLNA", "LabelEnableDlnaPlayToHelp": "Phát hiện các thiết bị trong mạng của bạn và cung cấp khả năng điều khiển chúng từ xa.", "LabelEnableDlnaDebugLoggingHelp": "Tạo tệp nhật ký lớn và chỉ nên dùng khi cần thiết để xử lý sự cố.", - "LabelEnableDlnaDebugLogging": "Bật tính năng gỡ lỗi DLNA", + "LabelEnableDlnaDebugLogging": "Bật ghi nhật ký gỡ lỗi DLNA", "LabelEnableDlnaClientDiscoveryIntervalHelp": "Xác định thời gian tính bằng giây giữa hai lần tìm kiếm SSDP.", "LabelEnableDlnaClientDiscoveryInterval": "Thời gian dò tìm máy khách:", "LabelEnableBlastAliveMessagesHelp": "Bật cái này nếu máy chủ không được các thiết bị UPnP khác trên mạng của bạn tìm ra một cách đáng tin cậy.", @@ -837,7 +837,7 @@ "LabelProfileVideoCodecs": "Mã hóa video:", "LabelProfileContainersHelp": "Phân cách bằng dấu phẩy. Để trống để áp dụng cho tất cả các định dạng.", "LabelProfileContainer": "Định dạng:", - "LabelProfileCodecsHelp": "Phân cách bằng dấu phẩy. Điều này có thể được để trống để áp dụng cho tất cả các mã hóa.", + "LabelProfileCodecsHelp": "Phân cách bằng dấu phẩy. Có thể để trống để dùng tất cả các mã hóa.", "LabelProfileCodecs": "Mã hóa:", "LabelProfileAudioCodecs": "Mã hóa âm thanh:", "LabelPreferredSubtitleLanguage": "Ngôn ngữ phụ đề ưu tiên:", @@ -1271,7 +1271,7 @@ "Transcoding": "Chuyển mã", "Trailers": "Đoạn giới thiệu", "TabAccess": "Truy cập", - "SystemDlnaProfilesHelp": "Cấu hình hệ thống ở chế độ chỉ đọc. Các thay đổi đối với cấu hình hệ thống sẽ được lưu vào cấu hình tùy chỉnh mới.", + "SystemDlnaProfilesHelp": "Cấu hình hệ thống ở chế độ chỉ đọc. Thay đổi cấu hình hệ thống sẽ được lưu vào cấu hình tùy chỉnh mới.", "Sports": "Thể thao", "SpecialFeatures": "Các tính năng đặc biệt", "SortName": "Sắp xếp tên", @@ -1532,7 +1532,7 @@ "InterlacedVideoNotSupported": "Video xen kẽ không được hỗ trợ", "AnamorphicVideoNotSupported": "Video Anamorphic không được hỗ trợ", "AudioSampleRateNotSupported": "Tốc độ mẫu của âm thanh không được hỗ trợ", - "AudioProfileNotSupported": "Cấu hình của mã hóa âm thanh không được hỗ trợ", + "AudioProfileNotSupported": "Cấu hình mã hóa âm thanh không được hỗ trợ", "VideoResolutionNotSupported": "Độ phân giải của video không được hỗ trợ", "AudioChannelsNotSupported": "Số kênh âm thanh không được hỗ trợ", "AudioBitrateNotSupported": "Tốc độ bit của âm thanh không được hỗ trợ", @@ -1695,5 +1695,15 @@ "PreferEmbeddedExtrasTitlesOverFileNames": "Ưu tiên tiêu đề được nhúng hơn tên tệp cho các tính năng bổ sung", "PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Phần bổ sung thường có cùng tên được nhúng với phần gốc, hãy kiểm tra điều này để sử dụng các tiêu đề được nhúng cho chúng.", "HeaderDummyChapter": "Hình Ảnh Phân Đoạn", - "HeaderRecordingMetadataSaving": "Dữ Liệu Mô Tả Bản Ghi" + "HeaderRecordingMetadataSaving": "Dữ Liệu Mô Tả Bản Ghi", + "SubtitleYellow": "Vàng", + "SubtitleBlack": "Đen", + "SubtitleBlue": "Xanh lam", + "SubtitleCyan": "Lục lam", + "SubtitleGray": "Xám", + "SubtitleGreen": "Xanh lá", + "SubtitleLightGray": "Xám Nhạt", + "SubtitleMagenta": "Đỏ Sậm", + "SubtitleRed": "Đỏ", + "SubtitleWhite": "Trắng" } diff --git a/src/strings/zh-cn.json b/src/strings/zh-cn.json index cee2f4d006..f05bb4d988 100644 --- a/src/strings/zh-cn.json +++ b/src/strings/zh-cn.json @@ -1621,7 +1621,7 @@ "DeletedScene": "删减场景", "BehindTheScenes": "幕后花絮", "Trailer": "预告片", - "Clip": "花絮", + "Clip": "片段", "ButtonExitApp": "退出应用", "ShowParentImages": "显示系列图片", "AllowEmbeddedSubtitlesAllowTextOption": "允许文本", @@ -1704,5 +1704,17 @@ "LabelDummyChapterCountHelp": "每个媒体文件的最大章节图像提取数。", "LabelChapterImageResolution": "分辨率:", "LabelChapterImageResolutionHelp": "提取的章节图像的分辨率。", - "SecondarySubtitles": "次字幕" + "SecondarySubtitles": "次字幕", + "SubtitleBlack": "黑色", + "SubtitleGray": "灰色", + "SubtitleLightGray": "淡灰色", + "SubtitleWhite": "白色", + "SubtitleBlue": "蓝色", + "SubtitleCyan": "青色", + "SubtitleGreen": "绿色", + "SubtitleMagenta": "品红色", + "SubtitleRed": "红色", + "SubtitleYellow": "黄色", + "Featurette": "花絮", + "Short": "短片" } diff --git a/src/assets/css/clearbutton.scss b/src/styles/clearbutton.scss similarity index 100% rename from src/assets/css/clearbutton.scss rename to src/styles/clearbutton.scss diff --git a/src/assets/css/dashboard.scss b/src/styles/dashboard.scss similarity index 100% rename from src/assets/css/dashboard.scss rename to src/styles/dashboard.scss diff --git a/src/assets/css/detailtable.scss b/src/styles/detailtable.scss similarity index 100% rename from src/assets/css/detailtable.scss rename to src/styles/detailtable.scss diff --git a/src/assets/css/flexstyles.scss b/src/styles/flexstyles.scss similarity index 100% rename from src/assets/css/flexstyles.scss rename to src/styles/flexstyles.scss diff --git a/src/assets/css/fonts.scss b/src/styles/fonts.scss similarity index 96% rename from src/assets/css/fonts.scss rename to src/styles/fonts.scss index 16142a3aab..681f1abd2c 100644 --- a/src/assets/css/fonts.scss +++ b/src/styles/fonts.scss @@ -1,4 +1,4 @@ -@import "../../styles/noto-sans/index.scss"; +@import "../styles/noto-sans/index.scss"; @mixin font($weight: null, $size: null) { font-weight: $weight; diff --git a/src/assets/css/fonts.sized.scss b/src/styles/fonts.sized.scss similarity index 100% rename from src/assets/css/fonts.sized.scss rename to src/styles/fonts.sized.scss diff --git a/src/assets/css/ios.scss b/src/styles/ios.scss similarity index 100% rename from src/assets/css/ios.scss rename to src/styles/ios.scss diff --git a/src/assets/css/librarybrowser.scss b/src/styles/librarybrowser.scss similarity index 100% rename from src/assets/css/librarybrowser.scss rename to src/styles/librarybrowser.scss diff --git a/src/assets/css/livetv.scss b/src/styles/livetv.scss similarity index 100% rename from src/assets/css/livetv.scss rename to src/styles/livetv.scss diff --git a/src/assets/css/metadataeditor.scss b/src/styles/metadataeditor.scss similarity index 100% rename from src/assets/css/metadataeditor.scss rename to src/styles/metadataeditor.scss diff --git a/src/assets/css/scrollstyles.scss b/src/styles/scrollstyles.scss similarity index 100% rename from src/assets/css/scrollstyles.scss rename to src/styles/scrollstyles.scss diff --git a/src/assets/css/site.scss b/src/styles/site.scss similarity index 100% rename from src/assets/css/site.scss rename to src/styles/site.scss diff --git a/src/assets/css/videoosd.scss b/src/styles/videoosd.scss similarity index 100% rename from src/assets/css/videoosd.scss rename to src/styles/videoosd.scss diff --git a/src/types/plugin.ts b/src/types/plugin.ts new file mode 100644 index 0000000000..4b4b946e90 --- /dev/null +++ b/src/types/plugin.ts @@ -0,0 +1,13 @@ +export enum PluginType { + MediaPlayer = 'mediaplayer', + PreplayIntercept = 'preplayintercept', + Screensaver = 'screensaver', + SyncPlay = 'syncplay' +} + +export interface Plugin { + name: string + id: string + type: PluginType | string + priority: number +} diff --git a/webpack.common.js b/webpack.common.js index eebd423d98..1b5d41fc42 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -158,6 +158,7 @@ const config = { path.resolve(__dirname, 'node_modules/dom7'), path.resolve(__dirname, 'node_modules/epubjs'), path.resolve(__dirname, 'node_modules/flv.js'), + path.resolve(__dirname, 'node_modules/hls.js'), path.resolve(__dirname, 'node_modules/libarchive.js'), path.resolve(__dirname, 'node_modules/marked'), path.resolve(__dirname, 'node_modules/react-router'),