diff --git a/.ci/azure-pipelines-build.yml b/.ci/azure-pipelines-build.yml index 021b6471cd..9c3a51c9fc 100644 --- a/.ci/azure-pipelines-build.yml +++ b/.ci/azure-pipelines-build.yml @@ -26,8 +26,6 @@ jobs: - script: 'npm ci --no-audit' displayName: 'Install Dependencies' - env: - SKIP_PREPARE: 'true' - script: 'npm run build:development' displayName: 'Build Development' 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/.github/workflows/lint.yml b/.github/workflows/lint.yml index 15526f009a..4d8519501c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -24,8 +24,6 @@ jobs: - name: Install Node.js dependencies run: npm ci --no-audit - env: - SKIP_PREPARE: true - name: Run a production build run: npm run build:production @@ -50,8 +48,6 @@ jobs: - name: Install Node.js dependencies run: npm ci --no-audit - env: - SKIP_PREPARE: true - name: Run eslint run: npm run lint @@ -76,8 +72,6 @@ jobs: - name: Install Node.js dependencies run: npm ci --no-audit - env: - SKIP_PREPARE: true - name: Run stylelint run: npm run stylelint:css @@ -102,8 +96,6 @@ jobs: - name: Install Node.js dependencies run: npm ci --no-audit - env: - SKIP_PREPARE: true - name: Run stylelint run: npm run stylelint:scss diff --git a/.github/workflows/tsc.yml b/.github/workflows/tsc.yml index be0f2596aa..2b1b3bf594 100644 --- a/.github/workflows/tsc.yml +++ b/.github/workflows/tsc.yml @@ -24,8 +24,6 @@ jobs: - name: Install Node.js dependencies run: npm ci --no-audit - env: - SKIP_PREPARE: true - name: Run tsc - run: npm run build:check \ No newline at end of file + run: npm run build:check diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c867d81580..f3dbfec6da 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -59,6 +59,7 @@ - [Vankerkom](https://github.com/vankerkom) - [edvwib](https://github.com/edvwib) - [Rob Farraher](https://github.com/farraherbg) + - [Pier-Luc Ducharme](https://github.com/pl-ducharme) # Emby Contributors diff --git a/debian/rules b/debian/rules index d9ba35ed30..091af0db11 100755 --- a/debian/rules +++ b/debian/rules @@ -12,6 +12,7 @@ override_dh_clistrip: override_dh_auto_build: npm ci --no-audit --unsafe-perm + npm run build:production mv $(CURDIR)/dist $(CURDIR)/web override_dh_auto_clean: diff --git a/deployment/Dockerfile.docker b/deployment/Dockerfile.docker index 33cf501665..5605e1150f 100644 --- a/deployment/Dockerfile.docker +++ b/deployment/Dockerfile.docker @@ -8,4 +8,6 @@ RUN apk add autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool ma WORKDIR ${SOURCE_DIR} COPY . . -RUN npm ci --no-audit --unsafe-perm && mv dist ${ARTIFACT_DIR} +RUN npm ci --no-audit --unsafe-perm \ + && npm run build:production \ + && mv dist ${ARTIFACT_DIR} 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/deployment/build.portable b/deployment/build.portable index 18f7a8d1e6..8bf8a0d2af 100755 --- a/deployment/build.portable +++ b/deployment/build.portable @@ -15,6 +15,7 @@ fi # build archives npm ci --no-audit --unsafe-perm +npm run build:production mv dist jellyfin-web_${version} tar -czf jellyfin-web_${version}_portable.tar.gz jellyfin-web_${version} rm -rf dist diff --git a/fedora/jellyfin-web.spec b/fedora/jellyfin-web.spec index 28407b8bcd..595ef33f2c 100644 --- a/fedora/jellyfin-web.spec +++ b/fedora/jellyfin-web.spec @@ -35,6 +35,7 @@ chown root:root -R . %build npm ci --no-audit --unsafe-perm +npm run build:production %install diff --git a/package-lock.json b/package-lock.json index 093d5dc279..964d7ed7c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,14 +22,14 @@ "classnames": "2.3.2", "core-js": "3.29.0", "date-fns": "2.29.3", - "dompurify": "2.4.4", + "dompurify": "3.0.1", "epubjs": "0.4.2", "escape-html": "1.0.3", "fast-text-encoding": "1.0.6", "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", "jassub": "1.5.3", "jellyfin-apiclient": "1.10.0", @@ -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", @@ -68,8 +68,8 @@ "@types/lodash-es": "4.17.6", "@types/react": "17.0.53", "@types/react-dom": "17.0.19", - "@typescript-eslint/eslint-plugin": "5.53.0", - "@typescript-eslint/parser": "5.53.0", + "@typescript-eslint/eslint-plugin": "5.54.1", + "@typescript-eslint/parser": "5.54.1", "@uupaa/dynamic-import-polyfill": "1.0.2", "autoprefixer": "10.4.13", "babel-loader": "9.1.2", @@ -105,7 +105,7 @@ "stylelint": "15.2.0", "stylelint-config-rational-order": "0.1.2", "stylelint-no-browser-hacks": "1.2.1", - "stylelint-order": "6.0.2", + "stylelint-order": "6.0.3", "stylelint-scss": "4.4.0", "ts-loader": "9.4.2", "typescript": "4.9.5", @@ -2606,11 +2606,11 @@ "dev": true }, "node_modules/@jellyfin/sdk": { - "version": "0.0.0-unstable.202302070552", - "resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202302070552.tgz", - "integrity": "sha512-hwrHLLFPTCEcrMywpLWwgGKEDKBjgu3o+ruMV3qCG7uAmKAQq48kuaZ818rJD+LjWBjBIUixnLJq1qUlHsgc+A==", + "version": "0.0.0-unstable.202303130502", + "resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202303130502.tgz", + "integrity": "sha512-j3ntDjTnZlU511J0CpuPVSSSYrx9so4Y3q6qYOVsB6/evH4/2BNkWYRbKgCnUtCULIV90T6KGc2EcS4GGxojCg==", "dependencies": { - "axios": "1.2.6", + "axios": "1.3.4", "compare-versions": "5.0.3" } }, @@ -2762,9 +2762,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" } @@ -3195,14 +3195,14 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", - "integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz", + "integrity": "sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/type-utils": "5.53.0", - "@typescript-eslint/utils": "5.53.0", + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/type-utils": "5.54.1", + "@typescript-eslint/utils": "5.54.1", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -3244,14 +3244,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz", - "integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz", + "integrity": "sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/typescript-estree": "5.54.1", "debug": "^4.3.4" }, "engines": { @@ -3271,13 +3271,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", - "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz", + "integrity": "sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0" + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/visitor-keys": "5.54.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3288,13 +3288,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz", - "integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz", + "integrity": "sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.53.0", - "@typescript-eslint/utils": "5.53.0", + "@typescript-eslint/typescript-estree": "5.54.1", + "@typescript-eslint/utils": "5.54.1", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -3315,9 +3315,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", - "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.1.tgz", + "integrity": "sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3328,13 +3328,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", - "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz", + "integrity": "sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/visitor-keys": "5.54.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -3399,16 +3399,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", - "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.1.tgz", + "integrity": "sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/typescript-estree": "5.54.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -3440,12 +3440,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", - "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz", + "integrity": "sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/types": "5.54.1", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -4106,9 +4106,9 @@ } }, "node_modules/axios": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.6.tgz", - "integrity": "sha512-rC/7F08XxZwjMV4iuWv+JpD3E0Ksqg9nac4IIg6RwNuF0JTeWoCo/mBNG54+tNhhI11G3/VDRbdDQTs9hGp4pQ==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -5960,9 +5960,9 @@ "integrity": "sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww==" }, "node_modules/dompurify": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz", - "integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.1.tgz", + "integrity": "sha512-60tsgvPKwItxZZdfLmamp0MTcecCta3avOhsLgPZ0qcWt96OasFfhkeIRbJ6br5i0fQawT1/RBGB5L58/Jpwuw==" }, "node_modules/domutils": { "version": "1.7.0", @@ -7301,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", @@ -8467,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", @@ -13131,11 +13128,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" @@ -13145,12 +13142,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" @@ -17080,22 +17077,22 @@ } }, "node_modules/stylelint-order": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.2.tgz", - "integrity": "sha512-yuac0BE6toHd27wUPvYVVQicAJthKFIv1HPQFH3Q0dExiO3Z6Uam7geoO0tUd5Z9ddsATYK++1qWNDX4RxMH5Q==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.3.tgz", + "integrity": "sha512-1j1lOb4EU/6w49qZeT2SQVJXm0Ht+Qnq9GMfUa3pMwoyojIWfuA+JUDmoR97Bht1RLn4ei0xtLGy87M7d29B1w==", "dev": true, "dependencies": { "postcss": "^8.4.21", - "postcss-sorting": "^8.0.1" + "postcss-sorting": "^8.0.2" }, "peerDependencies": { "stylelint": "^14.0.0 || ^15.0.0" } }, "node_modules/stylelint-order/node_modules/postcss-sorting": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.1.tgz", - "integrity": "sha512-go9Zoxx7KQH+uLrJ9xa5wRErFeXu01ydA6O8m7koPXkmAN7Ts//eRcIqjo0stBR4+Nir2gMYDOWAOx7O5EPUZA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.2.tgz", + "integrity": "sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==", "dev": true, "peerDependencies": { "postcss": "^8.4.20" @@ -18243,11 +18240,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", @@ -20929,11 +20921,11 @@ "dev": true }, "@jellyfin/sdk": { - "version": "0.0.0-unstable.202302070552", - "resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202302070552.tgz", - "integrity": "sha512-hwrHLLFPTCEcrMywpLWwgGKEDKBjgu3o+ruMV3qCG7uAmKAQq48kuaZ818rJD+LjWBjBIUixnLJq1qUlHsgc+A==", + "version": "0.0.0-unstable.202303130502", + "resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202303130502.tgz", + "integrity": "sha512-j3ntDjTnZlU511J0CpuPVSSSYrx9so4Y3q6qYOVsB6/evH4/2BNkWYRbKgCnUtCULIV90T6KGc2EcS4GGxojCg==", "requires": { - "axios": "1.2.6", + "axios": "1.3.4", "compare-versions": "5.0.3" } }, @@ -21054,9 +21046,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", @@ -21454,14 +21446,14 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", - "integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz", + "integrity": "sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/type-utils": "5.53.0", - "@typescript-eslint/utils": "5.53.0", + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/type-utils": "5.54.1", + "@typescript-eslint/utils": "5.54.1", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -21483,53 +21475,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz", - "integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz", + "integrity": "sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/typescript-estree": "5.54.1", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", - "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz", + "integrity": "sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0" + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/visitor-keys": "5.54.1" } }, "@typescript-eslint/type-utils": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz", - "integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz", + "integrity": "sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.53.0", - "@typescript-eslint/utils": "5.53.0", + "@typescript-eslint/typescript-estree": "5.54.1", + "@typescript-eslint/utils": "5.54.1", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", - "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.1.tgz", + "integrity": "sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", - "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz", + "integrity": "sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/visitor-keys": "5.53.0", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/visitor-keys": "5.54.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -21569,16 +21561,16 @@ } }, "@typescript-eslint/utils": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", - "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.1.tgz", + "integrity": "sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.53.0", - "@typescript-eslint/types": "5.53.0", - "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/typescript-estree": "5.54.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -21596,12 +21588,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.53.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", - "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz", + "integrity": "sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/types": "5.54.1", "eslint-visitor-keys": "^3.3.0" }, "dependencies": { @@ -22105,9 +22097,9 @@ "dev": true }, "axios": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.6.tgz", - "integrity": "sha512-rC/7F08XxZwjMV4iuWv+JpD3E0Ksqg9nac4IIg6RwNuF0JTeWoCo/mBNG54+tNhhI11G3/VDRbdDQTs9hGp4pQ==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -23497,9 +23489,9 @@ "integrity": "sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww==" }, "dompurify": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz", - "integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.1.tgz", + "integrity": "sha512-60tsgvPKwItxZZdfLmamp0MTcecCta3avOhsLgPZ0qcWt96OasFfhkeIRbJ6br5i0fQawT1/RBGB5L58/Jpwuw==" }, "domutils": { "version": "1.7.0", @@ -24506,7 +24498,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", @@ -25419,13 +25412,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", @@ -28744,20 +28733,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": { @@ -31990,19 +31979,19 @@ } }, "stylelint-order": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.2.tgz", - "integrity": "sha512-yuac0BE6toHd27wUPvYVVQicAJthKFIv1HPQFH3Q0dExiO3Z6Uam7geoO0tUd5Z9ddsATYK++1qWNDX4RxMH5Q==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.3.tgz", + "integrity": "sha512-1j1lOb4EU/6w49qZeT2SQVJXm0Ht+Qnq9GMfUa3pMwoyojIWfuA+JUDmoR97Bht1RLn4ei0xtLGy87M7d29B1w==", "dev": true, "requires": { "postcss": "^8.4.21", - "postcss-sorting": "^8.0.1" + "postcss-sorting": "^8.0.2" }, "dependencies": { "postcss-sorting": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.1.tgz", - "integrity": "sha512-go9Zoxx7KQH+uLrJ9xa5wRErFeXu01ydA6O8m7koPXkmAN7Ts//eRcIqjo0stBR4+Nir2gMYDOWAOx7O5EPUZA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.2.tgz", + "integrity": "sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==", "dev": true, "requires": {} } @@ -32726,11 +32715,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", diff --git a/package.json b/package.json index 6c96acd926..4a6817c17c 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "@types/lodash-es": "4.17.6", "@types/react": "17.0.53", "@types/react-dom": "17.0.19", - "@typescript-eslint/eslint-plugin": "5.53.0", - "@typescript-eslint/parser": "5.53.0", + "@typescript-eslint/eslint-plugin": "5.54.1", + "@typescript-eslint/parser": "5.54.1", "@uupaa/dynamic-import-polyfill": "1.0.2", "autoprefixer": "10.4.13", "babel-loader": "9.1.2", @@ -56,7 +56,7 @@ "stylelint": "15.2.0", "stylelint-config-rational-order": "0.1.2", "stylelint-no-browser-hacks": "1.2.1", - "stylelint-order": "6.0.2", + "stylelint-order": "6.0.3", "stylelint-scss": "4.4.0", "ts-loader": "9.4.2", "typescript": "4.9.5", @@ -81,14 +81,14 @@ "classnames": "2.3.2", "core-js": "3.29.0", "date-fns": "2.29.3", - "dompurify": "2.4.4", + "dompurify": "3.0.1", "epubjs": "0.4.2", "escape-html": "1.0.3", "fast-text-encoding": "1.0.6", "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", "jassub": "1.5.3", "jellyfin-apiclient": "1.10.0", @@ -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", @@ -131,7 +131,6 @@ "scripts": { "start": "npm run serve", "serve": "webpack serve --config webpack.dev.js", - "prepare": "node ./scripts/prepare.js", "build:development": "cross-env NODE_OPTIONS=\"--max_old_space_size=6144\" webpack --config webpack.dev.js", "build:production": "cross-env NODE_ENV=\"production\" NODE_OPTIONS=\"--max_old_space_size=6144\" webpack --config webpack.prod.js", "build:check": "tsc --noEmit", diff --git a/scripts/prepare.js b/scripts/prepare.js deleted file mode 100755 index 898b105e2f..0000000000 --- a/scripts/prepare.js +++ /dev/null @@ -1,12 +0,0 @@ -const { execSync } = require('child_process'); - -/** - * The npm `prepare` script needs to run a build to support installing - * a package from git repositories (this is dumb but a limitation of how - * npm behaves). We don't want to run these in CI though because - * building is slow so this script will skip the build when the - * `SKIP_PREPARE` environment variable has been set. - */ -if (!process.env.SKIP_PREPARE) { - execSync('webpack --config webpack.prod.js', { stdio: 'inherit' }); -} 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/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/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index 658837df83..dfbc0a6768 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -18,6 +18,7 @@ import browser from '../../scripts/browser'; import { playbackManager } from '../playback/playbackmanager'; import itemShortcuts from '../shortcuts'; import imageHelper from '../../scripts/imagehelper'; +import { randomInt } from '../../utils/number.ts'; import './card.scss'; import '../../elements/emby-button/paper-icon-button-light'; import '../guide/programs.scss'; @@ -640,16 +641,6 @@ import { appRouter } from '../appRouter'; }; } - /** - * Generates a random integer in a given range. - * @param {number} min - Minimum of the range. - * @param {number} max - Maximum of the range. - * @returns {number} Randomly generated number. - */ - function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; - } - /** * Generates an index used to select the default color of a card based on a string. * @param {?string} [str] - String to use for generating the index. @@ -663,13 +654,13 @@ 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); return (index % numRandomColors) + 1; } else { - return getRandomInt(1, numRandomColors); + return randomInt(1, numRandomColors); } } @@ -773,27 +764,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 +793,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 +823,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 +844,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 +979,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 +1205,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 +1252,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 +1279,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/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/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 e353d562ab..36137af21d 100644 --- a/src/components/guide/guide.js +++ b/src/components/guide/guide.js @@ -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/itemContextMenu.js b/src/components/itemContextMenu.js index e1c1545b69..e0bcb1562b 100644 --- a/src/components/itemContextMenu.js +++ b/src/components/itemContextMenu.js @@ -15,7 +15,6 @@ import toast from './toast/toast'; const user = options.user; const canPlay = playbackManager.canPlay(item); - const restrictOptions = (browser.operaTv || browser.web0s) && !user.Policy.IsAdministrator; const commands = []; @@ -99,8 +98,8 @@ import toast from './toast/toast'; }); } - if (!restrictOptions) { - if (itemHelper.supportsAddingToCollection(item)) { + if (!browser.tv) { + if (itemHelper.supportsAddingToCollection(item) && options.EnableCollectionManagement) { commands.push({ name: globalize.translate('AddToCollection'), id: 'addtocollection', @@ -272,7 +271,7 @@ import toast from './toast/toast'; }); } - if (!restrictOptions && options.share === true && itemHelper.canShare(item, user)) { + if (!browser.tv && options.share === true && itemHelper.canShare(item, user)) { commands.push({ name: globalize.translate('Share'), id: 'share', diff --git a/src/components/itemMediaInfo/itemMediaInfo.js b/src/components/itemMediaInfo/itemMediaInfo.js index 1a05d40849..ac04df4815 100644 --- a/src/components/itemMediaInfo/itemMediaInfo.js +++ b/src/components/itemMediaInfo/itemMediaInfo.js @@ -138,7 +138,7 @@ const attributeDelimiterHtml = layoutManager.tv ? '' : ': { diff --git a/src/components/mediaLibraryEditor/mediaLibraryEditor.js b/src/components/mediaLibraryEditor/mediaLibraryEditor.js index ed54675016..c90acc0078 100644 --- a/src/components/mediaLibraryEditor/mediaLibraryEditor.js +++ b/src/components/mediaLibraryEditor/mediaLibraryEditor.js @@ -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 6456c5721d..fb69d20aa2 100644 --- a/src/components/metadataEditor/metadataEditor.js +++ b/src/components/metadataEditor/metadataEditor.js @@ -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..65f20a71a2 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -1,4 +1,4 @@ -/*eslint prefer-const: "error"*/ +import { randomInt } from '../../utils/number.ts'; let currentId = 0; function addUniquePlaylistItemId(item) { @@ -58,7 +58,7 @@ class PlayQueueManager { const currentPlaylistItem = this._playlist.splice(this.getCurrentPlaylistIndex(), 1)[0]; for (let i = this._playlist.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * i); + const j = randomInt(0, i - 1); const temp = this._playlist[i]; this._playlist[i] = this._playlist[j]; this._playlist[j] = temp; diff --git a/src/components/recordingcreator/recordingfields.js b/src/components/recordingcreator/recordingfields.js index a666a49562..742a77df4b 100644 --- a/src/components/recordingcreator/recordingfields.js +++ b/src/components/recordingcreator/recordingfields.js @@ -12,8 +12,6 @@ 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 590c600e74..79d35b9fe1 100644 --- a/src/components/recordingcreator/seriesrecordingeditor.js +++ b/src/components/recordingcreator/seriesrecordingeditor.js @@ -17,8 +17,6 @@ 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/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/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/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/general.html b/src/controllers/dashboard/general.html index f099a81c5b..62f92d097c 100644 --- a/src/controllers/dashboard/general.html +++ b/src/controllers/dashboard/general.html @@ -80,6 +80,13 @@ +
+

${HeaderPerformance}

+
+ +
${LabelParallelImageEncodingLimitHelp}
+
+