mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'master' into jassub
This commit is contained in:
commit
8848b75be0
101 changed files with 793 additions and 560 deletions
|
@ -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'
|
||||
|
|
|
@ -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',
|
||||
|
|
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
|
@ -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
|
||||
|
|
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
|
@ -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
|
||||
|
|
4
.github/workflows/tsc.yml
vendored
4
.github/workflows/tsc.yml
vendored
|
@ -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
|
||||
run: npm run build:check
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
1
debian/rules
vendored
1
debian/rules
vendored
|
@ -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:
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM fedora:37
|
||||
FROM fedora:38
|
||||
|
||||
# Docker build arguments
|
||||
ARG SOURCE_DIR=/jellyfin
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -35,6 +35,7 @@ chown root:root -R .
|
|||
|
||||
%build
|
||||
npm ci --no-audit --unsafe-perm
|
||||
npm run build:production
|
||||
|
||||
|
||||
%install
|
||||
|
|
324
package-lock.json
generated
324
package-lock.json
generated
|
@ -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",
|
||||
|
|
13
package.json
13
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",
|
||||
|
|
|
@ -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' });
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 += '<div class="lazy cardFooterLogo" data-src="' + logoUrl + '"></div>';
|
||||
if (urls.logoUrl) {
|
||||
html += '<div class="lazy cardFooterLogo" data-src="' + urls.logoUrl + '"></div>';
|
||||
}
|
||||
|
||||
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 += `<button is="paper-icon-button-light" class="itemAction btnCardOptions cardText-secondary" data-action="menu" title="${globalize.translate('ButtonMore')}"><span class="material-icons more_vert" aria-hidden="true"></span></button>`;
|
||||
}
|
||||
|
||||
|
@ -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 = '<div class="' + footerClass + '">' + 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 += '<div class="innerCardFooter fullInnerCardFooter innerCardFooterClear">';
|
||||
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 += '<div class="' + cssClass + '"';
|
||||
}
|
||||
|
||||
html += ' data-id="' + currentItem.Id + '" data-serverid="' + apiClient.serverId() + '" data-index="' + index + '" data-numimages="' + numImages + '" data-imagetype="' + image.ImageType + '" data-providers="' + imageProviders.length + '"';
|
||||
html += ' data-id="' + currentItem.Id + '" data-serverid="' + apiClient.serverId() + '" data-index="' + options.index + '" data-numimages="' + options.numImages + '" data-imagetype="' + image.ImageType + '" data-providers="' + options.imageProviders.length + '"';
|
||||
|
||||
html += '>';
|
||||
|
||||
|
@ -132,7 +132,7 @@ import template from './imageeditor.template.html';
|
|||
|
||||
html += '<div class="cardContent">';
|
||||
|
||||
const imageUrl = getImageUrl(currentItem, apiClient, image.ImageType, image.ImageIndex, { maxWidth: imageSize });
|
||||
const imageUrl = getImageUrl(currentItem, apiClient, image.ImageType, image.ImageIndex, { maxWidth: options.imageSize });
|
||||
|
||||
html += '<div class="cardImageContainer" style="background-image:url(\'' + imageUrl + '\');background-position:center center;background-size:contain;"></div>';
|
||||
|
||||
|
@ -151,23 +151,23 @@ import template from './imageeditor.template.html';
|
|||
}
|
||||
html += '</div>';
|
||||
|
||||
if (enableFooterButtons) {
|
||||
if (options.enableFooterButtons) {
|
||||
html += '<div class="cardText cardTextCentered">';
|
||||
|
||||
if (image.ImageType === 'Backdrop') {
|
||||
if (index > 0) {
|
||||
if (options.index > 0) {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnMoveImage autoSize" data-imagetype="' + image.ImageType + '" data-index="' + image.ImageIndex + '" data-newindex="' + (image.ImageIndex - 1) + '" title="' + globalize.translate('MoveLeft') + '"><span class="material-icons chevron_left"></span></button>';
|
||||
} else {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="autoSize" disabled title="' + globalize.translate('MoveLeft') + '"><span class="material-icons chevron_left" aria-hidden="true"></span></button>';
|
||||
}
|
||||
|
||||
if (index < numImages - 1) {
|
||||
if (options.index < options.numImages - 1) {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnMoveImage autoSize" data-imagetype="' + image.ImageType + '" data-index="' + image.ImageIndex + '" data-newindex="' + (image.ImageIndex + 1) + '" title="' + globalize.translate('MoveRight') + '"><span class="material-icons chevron_right" aria-hidden="true"></span></button>';
|
||||
} else {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="autoSize" disabled title="' + globalize.translate('MoveRight') + '"><span class="material-icons chevron_right" aria-hidden="true"></span></button>';
|
||||
}
|
||||
} else {
|
||||
if (imageProviders.length) {
|
||||
if (options.imageProviders.length) {
|
||||
html += '<button type="button" is="paper-icon-button-light" data-imagetype="' + image.ImageType + '" class="btnSearchImages autoSize" title="' + globalize.translate('Search') + '"><span class="material-icons search" aria-hidden="true"></span></button>';
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ import template from './imageeditor.template.html';
|
|||
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</' + tagName + '>';
|
||||
html += '</' + options.tagName + '>';
|
||||
|
||||
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);
|
||||
});
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -138,7 +138,7 @@ const attributeDelimiterHtml = layoutManager.tv ? '' : '<span class="hide">: </s
|
|||
attributes.push(createAttribute(globalize.translate('MediaInfoChannels'), `${stream.Channels} ch`));
|
||||
}
|
||||
if (stream.BitRate) {
|
||||
attributes.push(createAttribute(globalize.translate('MediaInfoBitrate'), `${parseInt(stream.BitRate / 1000)} kbps`));
|
||||
attributes.push(createAttribute(globalize.translate('MediaInfoBitrate'), `${parseInt(stream.BitRate / 1000, 10)} kbps`));
|
||||
}
|
||||
if (stream.SampleRate) {
|
||||
attributes.push(createAttribute(globalize.translate('MediaInfoSampleRate'), `${stream.SampleRate} Hz`));
|
||||
|
|
|
@ -52,7 +52,7 @@ import datetime from '../../scripts/datetime';
|
|||
|
||||
if (value) {
|
||||
if (identifyField[i].type === 'number') {
|
||||
value = parseInt(value);
|
||||
value = parseInt(value, 10);
|
||||
}
|
||||
|
||||
lookupInfo[identifyField[i].getAttribute('data-lookup')] = value;
|
||||
|
@ -123,7 +123,7 @@ import datetime from '../../scripts/datetime';
|
|||
elem.innerHTML = html;
|
||||
|
||||
function onSearchImageClick() {
|
||||
const index = parseInt(this.getAttribute('data-index'));
|
||||
const index = parseInt(this.getAttribute('data-index'), 10);
|
||||
|
||||
const currentResult = results[index];
|
||||
|
||||
|
|
|
@ -531,7 +531,7 @@ import template from './libraryoptionseditor.template.html';
|
|||
PreferredMetadataLanguage: parent.querySelector('#selectLanguage').value,
|
||||
MetadataCountryCode: parent.querySelector('#selectCountry').value,
|
||||
SeasonZeroDisplayName: parent.querySelector('#txtSeasonZeroName').value,
|
||||
AutomaticRefreshIntervalDays: parseInt(parent.querySelector('#selectAutoRefreshInterval').value),
|
||||
AutomaticRefreshIntervalDays: parseInt(parent.querySelector('#selectAutoRefreshInterval').value, 10),
|
||||
EnableEmbeddedTitles: parent.querySelector('#chkEnableEmbeddedTitles').checked,
|
||||
EnableEmbeddedExtrasTitles: parent.querySelector('#chkEnableEmbeddedExtrasTitles').checked,
|
||||
EnableEmbeddedEpisodeInfos: parent.querySelector('#chkEnableEmbeddedEpisodeInfos').checked,
|
||||
|
|
|
@ -168,7 +168,7 @@ import template from './mediaLibraryCreator.template.html';
|
|||
|
||||
function onRemoveClick(e) {
|
||||
const button = dom.parentWithClass(e.target, 'btnRemovePath');
|
||||
const index = parseInt(button.getAttribute('data-index'));
|
||||
const index = parseInt(button.getAttribute('data-index'), 10);
|
||||
const location = pathInfos[index].Path;
|
||||
const locationLower = location.toLowerCase();
|
||||
pathInfos = pathInfos.filter(p => {
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -13,8 +13,6 @@ import '../formdialog.scss';
|
|||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
|
||||
/*eslint prefer-const: "error"*/
|
||||
|
||||
function getEditorHtml() {
|
||||
let html = '';
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
|
||||
.upNextDialog-countdownText {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.upNextDialog-nextVideoText,
|
||||
.upNextDialog-title {
|
||||
width: 25.5em;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 += '<div class="cardBox visualCardBox">';
|
||||
deviceHtml += '<div class="cardScalable">';
|
||||
deviceHtml += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
deviceHtml += `<a is="emby-linkbutton" href="${canEdit ? '#/device.html?id=' + device.Id : '#'}" class="cardContent cardImageContainer ${cardBuilder.getDefaultBackgroundClass()}">`;
|
||||
deviceHtml += `<a is="emby-linkbutton" href="#/device.html?id=${device.Id}" class="cardContent cardImageContainer ${cardBuilder.getDefaultBackgroundClass()}">`;
|
||||
const iconUrl = imageHelper.getDeviceIcon(device);
|
||||
|
||||
if (iconUrl) {
|
||||
|
@ -114,7 +110,7 @@ import confirm from '../../../components/confirm/confirm';
|
|||
deviceHtml += '</div>';
|
||||
deviceHtml += '<div class="cardFooter">';
|
||||
|
||||
if (canEdit || canDelete(device.Id)) {
|
||||
if (canDelete(device.Id)) {
|
||||
if (globalize.getIsRTL())
|
||||
deviceHtml += '<div style="text-align:left; float:left;padding-top:5px;">';
|
||||
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');
|
||||
|
|
|
@ -100,7 +100,7 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
}).join('') + '</div>';
|
||||
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('') + '</div>';
|
||||
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('') + '</div>';
|
||||
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]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -80,6 +80,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="verticalSection">
|
||||
<h2>${HeaderPerformance}</h2>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" id="txtParallelImageEncodingLimit" label="${LabelParallelImageEncodingLimit}" type="number" pattern="[0-9]*" min="0" step="1" />
|
||||
<div class="fieldDescription">${LabelParallelImageEncodingLimitHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
|
|
|
@ -21,6 +21,7 @@ import alert from '../../components/alert';
|
|||
$('#selectLocalizationLanguage', page).html(languageOptions.map(function (language) {
|
||||
return '<option value="' + language.Value + '">' + language.Name + '</option>';
|
||||
})).val(config.UICulture);
|
||||
page.querySelector('#txtParallelImageEncodingLimit').value = config.ParallelImageEncodingLimit || '';
|
||||
|
||||
loading.hide();
|
||||
}
|
||||
|
@ -36,6 +37,7 @@ import alert from '../../components/alert';
|
|||
config.MetadataPath = $('#txtMetadataPath', form).val();
|
||||
config.MetadataNetworkPath = $('#txtMetadataNetworkPath', form).val();
|
||||
config.QuickConnectAvailable = form.querySelector('#chkQuickConnectAvailable').checked;
|
||||
config.ParallelImageEncodingLimit = parseInt(form.querySelector('#txtParallelImageEncodingLimit').value || '0', 10);
|
||||
|
||||
ApiClient.updateServerConfiguration(config).then(function() {
|
||||
ApiClient.getNamedConfiguration(brandingConfigKey).then(function(brandingConfig) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -68,7 +68,7 @@ function renderPackage(pkg, installedPlugins, page) {
|
|||
|
||||
if (installedPlugin) {
|
||||
const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '<strong>' + installedPlugin.Version + '</strong>');
|
||||
$('#pCurrentVersion', page).show().text(currentVersionText);
|
||||
$('#pCurrentVersion', page).show().html(currentVersionText);
|
||||
} else {
|
||||
$('#pCurrentVersion', page).hide().text('');
|
||||
}
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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 '<h1 class="sliderBubbleText">' + getDisplayTimeWithoutAmPm(new Date(parseInt(ms)), true) + '</h1>';
|
||||
return '<h1 class="sliderBubbleText">' + getDisplayTimeWithoutAmPm(new Date(parseInt(ms, 10)), true) + '</h1>';
|
||||
}
|
||||
|
||||
return '--:--';
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -91,7 +91,7 @@ const EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype);
|
|||
return 0;
|
||||
}
|
||||
|
||||
value = parseInt(value);
|
||||
value = parseInt(value, 10);
|
||||
if (isNaN(value)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ const Scroller: FC<ScrollerProps> = ({
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(value))) {
|
||||
if (isNaN(parseInt(value, 10))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,11 +75,11 @@ import '../../styles/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 '../../styles/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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { PluginType } from '../../types/plugin.ts';
|
||||
import { randomInt } from '../../utils/number.ts';
|
||||
|
||||
export default function () {
|
||||
const self = this;
|
||||
|
||||
self.name = 'Logo ScreenSaver';
|
||||
self.type = 'screensaver';
|
||||
self.type = PluginType.Screensaver;
|
||||
self.id = 'logoscreensaver';
|
||||
self.supportsAnonymous = true;
|
||||
|
||||
|
@ -23,16 +26,12 @@ export default function () {
|
|||
const elem = document.querySelector('.logoScreenSaverImage');
|
||||
|
||||
if (elem && elem.animate) {
|
||||
const random = getRandomInt(0, animations.length - 1);
|
||||
const random = randomInt(0, animations.length - 1);
|
||||
|
||||
animations[random](elem, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function getRandomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function bounceInLeft(elem, iterations) {
|
||||
const keyframes = [
|
||||
{ transform: 'translate3d(-3000px, 0, 0)', opacity: '0', offset: 0 },
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<HTMLDivElement>(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);
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -159,6 +159,7 @@ const UserEdit: FunctionComponent = () => {
|
|||
(page.querySelector('.chkIsAdmin') as HTMLInputElement).checked = user.Policy.IsAdministrator;
|
||||
(page.querySelector('.chkDisabled') as HTMLInputElement).checked = user.Policy.IsDisabled;
|
||||
(page.querySelector('.chkIsHidden') as HTMLInputElement).checked = user.Policy.IsHidden;
|
||||
(page.querySelector('.chkEnableCollectionManagement') as HTMLInputElement).checked = user.Policy.EnableCollectionManagement;
|
||||
(page.querySelector('.chkRemoteControlSharedDevices') as HTMLInputElement).checked = user.Policy.EnableSharedDeviceControl;
|
||||
(page.querySelector('.chkEnableRemoteControlOtherUsers') as HTMLInputElement).checked = user.Policy.EnableRemoteControlOfOtherUsers;
|
||||
(page.querySelector('.chkEnableDownloading') as HTMLInputElement).checked = user.Policy.EnableContentDownloading;
|
||||
|
@ -224,12 +225,13 @@ const UserEdit: FunctionComponent = () => {
|
|||
user.Policy.EnableAudioPlaybackTranscoding = (page.querySelector('.chkEnableAudioPlaybackTranscoding') as HTMLInputElement).checked;
|
||||
user.Policy.EnableVideoPlaybackTranscoding = (page.querySelector('.chkEnableVideoPlaybackTranscoding') as HTMLInputElement).checked;
|
||||
user.Policy.EnablePlaybackRemuxing = (page.querySelector('.chkEnableVideoPlaybackRemuxing') as HTMLInputElement).checked;
|
||||
user.Policy.EnableCollectionManagement = (page.querySelector('.chkEnableCollectionManagement') as HTMLInputElement).checked;
|
||||
user.Policy.ForceRemoteSourceTranscoding = (page.querySelector('.chkForceRemoteSourceTranscoding') as HTMLInputElement).checked;
|
||||
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;
|
||||
|
@ -375,6 +377,11 @@ const UserEdit: FunctionComponent = () => {
|
|||
className='chkIsAdmin'
|
||||
title='OptionAllowUserToManageServer'
|
||||
/>
|
||||
<CheckBoxElement
|
||||
labelClassName='checkboxContainer'
|
||||
className='chkEnableCollectionManagement'
|
||||
title='AllowCollectionManagement'
|
||||
/>
|
||||
<div id='featureAccessFields' className='verticalSection'>
|
||||
<h2 className='paperListLabel'>
|
||||
{globalize.translate('HeaderFeatureAccess')}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -16,6 +16,7 @@ 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';
|
||||
|
||||
|
@ -159,7 +160,7 @@ import '../styles/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'
|
||||
) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -348,7 +348,7 @@ export class UserSettings {
|
|||
return this.set('skipBackLength', val.toString());
|
||||
}
|
||||
|
||||
return parseInt(this.get('skipBackLength') || '10000');
|
||||
return parseInt(this.get('skipBackLength') || '10000', 10);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -361,7 +361,7 @@ export class UserSettings {
|
|||
return this.set('skipForwardLength', val.toString());
|
||||
}
|
||||
|
||||
return parseInt(this.get('skipForwardLength') || '30000');
|
||||
return parseInt(this.get('skipForwardLength') || '30000', 10);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1380,7 +1380,7 @@
|
|||
"UnknownAudioStreamInfo": "Інфармацыя аб аўдыяплыні невядомая",
|
||||
"DirectPlayError": "Узнікла памылка пры запуску прамога прайгравання",
|
||||
"SelectAll": "Абраць усё",
|
||||
"Clip": "Художнік",
|
||||
"Clip": "Кліп",
|
||||
"Sample": "Прыклад",
|
||||
"LabelVppTonemappingBrightness": "Узмацненне яркасці танальнага адлюстравання VPP:",
|
||||
"LabelVppTonemappingBrightnessHelp": "Прымяніць узмацненне яркасці ў танальным адлюстраванні VPP. І рэкамендаванае, і стандартнае значэнне роўна 0.",
|
||||
|
@ -1700,5 +1700,7 @@
|
|||
"SubtitleMagenta": "Пурпурны",
|
||||
"SubtitleRed": "Чырвоны",
|
||||
"SubtitleWhite": "Белы",
|
||||
"SubtitleYellow": "Жоўты"
|
||||
"SubtitleYellow": "Жоўты",
|
||||
"Featurette": "Кароткаметражка",
|
||||
"Short": "Кароткаметражка"
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
@ -1714,5 +1714,10 @@
|
|||
"SubtitleMagenta": "Fialová",
|
||||
"SubtitleRed": "Červená",
|
||||
"SubtitleWhite": "Bílá",
|
||||
"SubtitleYellow": "Žlutá"
|
||||
"SubtitleYellow": "Žlutá",
|
||||
"Featurette": "Středně dlouhý film",
|
||||
"Short": "Krátký film",
|
||||
"HeaderPerformance": "Výkon",
|
||||
"LabelParallelImageEncodingLimit": "Počet paralelních kódování obrázků",
|
||||
"LabelParallelImageEncodingLimitHelp": "Maximální počet kódování obrázků, které mohou běžet zároveň. Nastavením na 0 bude limit nastaven dle parametrů systému."
|
||||
}
|
||||
|
|
|
@ -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,17 @@
|
|||
"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",
|
||||
"Featurette": "Featurette",
|
||||
"Short": "Kurzfilm"
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
@ -1714,5 +1714,7 @@
|
|||
"SubtitleMagenta": "Magenta",
|
||||
"SubtitleRed": "Red",
|
||||
"SubtitleWhite": "White",
|
||||
"SubtitleYellow": "Yellow"
|
||||
"SubtitleYellow": "Yellow",
|
||||
"Featurette": "Featurette",
|
||||
"Short": "Short"
|
||||
}
|
||||
|
|
|
@ -420,6 +420,7 @@
|
|||
"HeaderPassword": "Password",
|
||||
"HeaderPasswordReset": "Password Reset",
|
||||
"HeaderPaths": "Paths",
|
||||
"HeaderPerformance": "Performance",
|
||||
"HeaderPhotoAlbums": "Photo Albums",
|
||||
"HeaderPinCodeReset": "Reset Easy PIN Code",
|
||||
"HeaderPlayAll": "Play All",
|
||||
|
@ -791,6 +792,8 @@
|
|||
"LabelOriginalName": "Original name:",
|
||||
"LabelOriginalTitle": "Original title:",
|
||||
"LabelOverview": "Overview:",
|
||||
"LabelParallelImageEncodingLimit": "Parallel image encoding limit",
|
||||
"LabelParallelImageEncodingLimitHelp": "Maximum amount of image encodings that are allowed to run in parallel. Setting this to 0 will choose a limit based on your system specs.",
|
||||
"LabelParentalRating": "Parental rating:",
|
||||
"LabelParentNumber": "Parent number:",
|
||||
"LabelPassword": "Password:",
|
||||
|
@ -1671,7 +1674,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",
|
||||
|
|
|
@ -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»:"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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:"
|
||||
}
|
||||
|
|
|
@ -234,7 +234,7 @@
|
|||
"HeaderApiKeys": "Clés API",
|
||||
"HeaderApiKeysHelp": "Les applications externes ont besoin d'une clé d'API pour communiquer avec le serveur. Les clés sont distribuées lors d'une connexion avec un compte normal ou en accordant manuellement une clé à une application.",
|
||||
"HeaderApp": "Application",
|
||||
"HeaderAppearsOn": "Apparait dans",
|
||||
"HeaderAppearsOn": "Apparaît dans",
|
||||
"HeaderAudioBooks": "Livres audio",
|
||||
"HeaderAudioSettings": "Réglages audio",
|
||||
"HeaderBlockItemsWithNoRating": "Bloquer les éléments avec des informations de classification inconnues ou n'en disposant pas :",
|
||||
|
@ -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",
|
||||
|
@ -1714,5 +1714,10 @@
|
|||
"SubtitleMagenta": "Magenta",
|
||||
"SubtitleRed": "Rouge",
|
||||
"SubtitleWhite": "Blanc",
|
||||
"SubtitleYellow": "Jaune"
|
||||
"SubtitleYellow": "Jaune",
|
||||
"Featurette": "Featurette",
|
||||
"Short": "Court-métrage",
|
||||
"HeaderPerformance": "Performance",
|
||||
"LabelParallelImageEncodingLimit": "Limite de parallélisation de l'encodage d'image",
|
||||
"LabelParallelImageEncodingLimitHelp": "Nombre maximal d’encodages d’image autorisés à s’exécuter en parallèle. Si vous définissez cette valeur sur 0, vous choisirez une limite en fonction des spécifications de votre système."
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
"Connect": "Verbind",
|
||||
"ContinueWatching": "Verderkijken",
|
||||
"Continuing": "Wordt vervolgd",
|
||||
"CriticRating": "Critici-beoordeling",
|
||||
"CriticRating": "Beoordeling critici",
|
||||
"CustomDlnaProfilesHelp": "Maak een aangepast profiel om een nieuw apparaat aan te maken of overschrijf een systeemprofiel.",
|
||||
"DateAdded": "Datum toegevoegd",
|
||||
"DatePlayed": "Datum afgespeeld",
|
||||
|
@ -787,7 +787,7 @@
|
|||
"NewEpisodesOnly": "Alleen nieuwe afleveringen",
|
||||
"News": "Nieuws",
|
||||
"Next": "Volgende",
|
||||
"NextUp": "Hierna",
|
||||
"NextUp": "Volgende",
|
||||
"No": "Nee",
|
||||
"NoNewDevicesFound": "Er zijn geen nieuwe apparaten gevonden. Sluit deze melding en voer handmatig de apparaat gegevens in om een nieuwe tuner toe te voegen.",
|
||||
"MessageNoNextUpItems": "Niets gevonden. Start met kijken!",
|
||||
|
@ -822,7 +822,7 @@
|
|||
"OptionAutomaticallyGroupSeriesHelp": "Series die verspreid zijn over meerdere mappen binnen deze bibliotheek worden automatisch samengevoegd tot één serie.",
|
||||
"OptionBluray": "BD",
|
||||
"OptionCommunityRating": "Beoordeling gemeenschap",
|
||||
"OptionCriticRating": "Beoordeling door critici",
|
||||
"OptionCriticRating": "Beoordeling critici",
|
||||
"OptionCustomUsers": "Aangepast",
|
||||
"OptionDaily": "Dagelijks",
|
||||
"OptionDateAdded": "Datum toegevoegd",
|
||||
|
@ -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 'Verderkijken'-secties zullen afleveringsafbeeldingen gebruiken als thumbnails in plaats van de primaire thumbnail van de serie.",
|
||||
"UseEpisodeImagesInNextUpHelp": "Secties 'Volgende' en 'Verderkijken' zullen afleveringsafbeeldingen gebruiken als miniaturen in plaats van de primaire miniatuur 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.",
|
||||
|
@ -1556,11 +1556,11 @@
|
|||
"VideoProfileNotSupported": "Het profiel van de videocodec wordt niet ondersteund",
|
||||
"Lyricist": "Tekstschrijver",
|
||||
"NextChapter": "Volgend hoofdstuk",
|
||||
"LabelMaxDaysForNextUp": "Maximaal dagen in 'Hierna':",
|
||||
"LabelMaxDaysForNextUpHelp": "Zet het maximaal aantal dagen dat een serie in de 'Hierna'-lijst staat zonder het te kijken.",
|
||||
"LabelMaxDaysForNextUp": "Maximaal dagen in 'Volgende':",
|
||||
"LabelMaxDaysForNextUpHelp": "Stel het maximaal aantal dagen in dat een serie in de 'Volgende'-lijst staat zonder het te kijken.",
|
||||
"PreviousChapter": "Vorig hoofdstuk",
|
||||
"Remixer": "Remixer",
|
||||
"UseEpisodeImagesInNextUp": "Gebruik afleveringscovers in de secties 'Hierna' en 'Verderkijken'",
|
||||
"UseEpisodeImagesInNextUp": "Gebruik afleveringscovers in de secties 'Volgende' 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",
|
||||
|
@ -1625,11 +1625,11 @@
|
|||
"ButtonBackspace": "Backspace",
|
||||
"StoryArc": "Verhaallijn",
|
||||
"ItemDetails": "Itemdetails",
|
||||
"EnableRewatchingNextUp": "Opnieuw kijken inschakelen in Hierna",
|
||||
"EnableRewatchingNextUp": "Opnieuw kijken inschakelen in Volgende",
|
||||
"Bold": "Vetgedrukt",
|
||||
"LabelTextWeight": "Tekstdikte:",
|
||||
"HomeVideosPhotos": "Homevideo's en foto's",
|
||||
"EnableRewatchingNextUpHelp": "Laat reeds gekeken afleveringen zien in 'Hierna'-secties.",
|
||||
"EnableRewatchingNextUpHelp": "Laat reeds gekeken afleveringen zien in sectie 'Volgende'.",
|
||||
"ContainerBitrateExceedsLimit": "De bitrate van de video overschrijdt de limiet",
|
||||
"LabelMaxVideoResolution": "Maximaal toegestane resolutie voor transcoderingen",
|
||||
"UnknownAudioStreamInfo": "De audio stream info is onbekend",
|
||||
|
@ -1674,7 +1674,7 @@
|
|||
"DeletedScene": "Verwijderde scene",
|
||||
"BehindTheScenes": "Achter de scenes",
|
||||
"Trailer": "Trailer",
|
||||
"Clip": "Korte film",
|
||||
"Clip": "Clip",
|
||||
"SelectAll": "Selecteer alles",
|
||||
"DirectPlayError": "Er is een fout opgetreden tijdens het starten van direct afspelen",
|
||||
"OptionDateShowAdded": "Datum Serie Toegevoegd",
|
||||
|
@ -1713,5 +1713,7 @@
|
|||
"SubtitleMagenta": "Magenta",
|
||||
"SubtitleRed": "Rood",
|
||||
"SubtitleWhite": "Wit",
|
||||
"SubtitleYellow": "Geel"
|
||||
"SubtitleYellow": "Geel",
|
||||
"Featurette": "Featurette",
|
||||
"Short": "Korte film"
|
||||
}
|
||||
|
|
|
@ -1439,7 +1439,7 @@
|
|||
"SubtitleDownloadersHelp": "Увімкніть та оцініть бажані завантажувачі субтитрів у порядку пріоритету.",
|
||||
"SubtitleAppearanceSettingsDisclaimer": "Наведені нижче налаштування не застосовуються до графічних субтитрів, згаданих вище, або субтитрів ASS/SSA, які вбудовують власні стилі.",
|
||||
"SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Ці налаштування також застосовуються до будь-якого відтворення Google Cast, запущеного цим пристроєм.",
|
||||
"Subtitle": "Підзаголовок",
|
||||
"Subtitle": "Субтитри",
|
||||
"Studios": "Студії",
|
||||
"StopRecording": "Зупинити запис",
|
||||
"StopPlayback": "Зупинити відтворення",
|
||||
|
|
|
@ -1621,7 +1621,7 @@
|
|||
"DeletedScene": "删减场景",
|
||||
"BehindTheScenes": "幕后花絮",
|
||||
"Trailer": "预告片",
|
||||
"Clip": "花絮",
|
||||
"Clip": "片段",
|
||||
"ButtonExitApp": "退出应用",
|
||||
"ShowParentImages": "显示系列图片",
|
||||
"AllowEmbeddedSubtitlesAllowTextOption": "允许文本",
|
||||
|
@ -1714,5 +1714,7 @@
|
|||
"SubtitleGreen": "绿色",
|
||||
"SubtitleMagenta": "品红色",
|
||||
"SubtitleRed": "红色",
|
||||
"SubtitleYellow": "黄色"
|
||||
"SubtitleYellow": "黄色",
|
||||
"Featurette": "花絮",
|
||||
"Short": "短片"
|
||||
}
|
||||
|
|
|
@ -650,7 +650,7 @@
|
|||
"LabelEmbedAlbumArtDidl": "於 DIDL 中嵌入專輯封面",
|
||||
"LabelEasyPinCode": "簡易PIN代碼:",
|
||||
"LabelDynamicExternalId": "{0} Id:",
|
||||
"LabelDropSubtitleHere": "將字幕檔丟到這裡,或點擊瀏覽。",
|
||||
"LabelDropSubtitleHere": "將字幕檔拖動到這裡,或點擊瀏覽。",
|
||||
"LabelDropShadow": "陰影:",
|
||||
"LabelDroppedFrames": "丟棄的幀:",
|
||||
"LabelDropImageHere": "拖移圖片到這裡,或是點擊來選取。",
|
||||
|
@ -1096,5 +1096,11 @@
|
|||
"LabelSyncPlaySettingsSyncCorrection": "同步校正",
|
||||
"HomeVideosPhotos": "家庭影片及相片",
|
||||
"MessageConfirmRevokeApiKey": "你是否確定要廢除此 API Key?有關程序將無法再連接此伺服器。",
|
||||
"LabelSyncPlaySettingsExtraTimeOffsetHelp": "為目標裝置調較 SyncPlay 延遲時間(毫秒)以改善不同步問題,請小心調較。"
|
||||
"LabelSyncPlaySettingsExtraTimeOffsetHelp": "為目標裝置調較 SyncPlay 延遲時間(毫秒)以改善不同步問題,請小心調較。",
|
||||
"Experimental": "試驗性",
|
||||
"HeaderDummyChapter": "章節圖片",
|
||||
"IgnoreDtsHelp": "禁用此選項或可解決部份問題,如無法正常播放外部音訊。",
|
||||
"DownloadAll": "全部下載",
|
||||
"LabelDummyChapterDuration": "間距:",
|
||||
"LabelDummyChapterDurationHelp": "章節圖片擷取間距(秒)"
|
||||
}
|
||||
|
|
13
src/types/plugin.ts
Normal file
13
src/types/plugin.ts
Normal file
|
@ -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
|
||||
}
|
|
@ -2,6 +2,16 @@ function toLocaleStringSupportsOptions() {
|
|||
return !!(typeof Intl === 'object' && Intl && typeof Intl.NumberFormat === 'function');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export function randomInt(min: number, max: number): number {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a number formatted as a perentage.
|
||||
* @param {number} value The value as a number.
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue