1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge branch 'master' into relax-hevc-safari

This commit is contained in:
Nyanmisaka 2024-03-07 16:13:44 +08:00 committed by GitHub
commit 4a7fc0662a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
85 changed files with 1567 additions and 2394 deletions

View file

@ -261,7 +261,11 @@ module.exports = {
'ServerNotifications': 'writable',
'TaskButton': 'writable',
'UserParentalControlPage': 'writable',
'Windows': 'readonly'
'Windows': 'readonly',
// Build time definitions
__JF_BUILD_VERSION__: 'readonly',
__USE_SYSTEM_FONTS__: 'readonly',
__WEBPACK_SERVE__: 'readonly'
},
rules: {
'@typescript-eslint/prefer-string-starts-ends-with': ['error']

View file

@ -31,6 +31,8 @@ jobs:
run: npm ci --no-audit
- name: Run a production build
env:
JELLYFIN_VERSION: ${{ github.event.pull_request.head.sha || github.sha }}
run: npm run build:production
- name: Update config.json for testing
@ -56,7 +58,7 @@ jobs:
- name: Save PR context
env:
PR_NUMBER: ${{ github.event.number }}
PR_SHA: ${{ github.sha }}
PR_SHA: ${{ github.event.pull_request.head.sha }}
run: |
echo $PR_NUMBER > PR_number
echo $PR_SHA > PR_sha

52
.github/workflows/update-sdk.yml vendored Normal file
View file

@ -0,0 +1,52 @@
name: Update the Jellyfin SDK
on:
schedule:
- cron: '0 7 * * *'
workflow_dispatch:
concurrency:
group: unstable-sdk-pr
cancel-in-progress: true
jobs:
update:
runs-on: ubuntu-latest
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
steps:
- name: Check out Git repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: master
token: ${{ secrets.JF_BOT_TOKEN }}
- name: Set up Node.js
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
check-latest: true
cache: npm
- name: Install latest unstable SDK
run: |
npm i --save @jellyfin/sdk@unstable
VERSION=$(jq -r '.dependencies["@jellyfin/sdk"]' package.json)
echo "JF_SDK_VERSION=${VERSION}" >> $GITHUB_ENV
- name: Open a pull request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.JF_BOT_TOKEN }}
commit-message: Update @jellyfin/sdk to ${{env.JF_SDK_VERSION}}
committer: jellyfin-bot <team@jellyfin.org>
author: jellyfin-bot <team@jellyfin.org>
branch: update-jf-sdk
delete-branch: true
title: Update @jellyfin/sdk to ${{env.JF_SDK_VERSION}}
body: |
**Changes**
Updates to the latest unstable @jellyfin/sdk build
labels: |
dependencies
npm

View file

@ -79,6 +79,7 @@
- [Kevin Tan (Valius)](https://github.com/valius)
- [Rasmus Krämer](https://github.com/rasmuslos)
- [ntarelix](https://github.com/ntarelix)
- [András Maróy](https://github.com/andrasmaroy)
## Emby Contributors

255
package-lock.json generated
View file

@ -17,7 +17,7 @@
"@fontsource/noto-sans-kr": "5.0.17",
"@fontsource/noto-sans-sc": "5.0.17",
"@fontsource/noto-sans-tc": "5.0.17",
"@jellyfin/sdk": "unstable",
"@jellyfin/sdk": "0.0.0-unstable.202403040506",
"@loadable/component": "5.16.3",
"@mui/icons-material": "5.15.5",
"@mui/material": "5.15.5",
@ -58,6 +58,7 @@
"screenfull": "6.0.2",
"sortablejs": "1.15.2",
"swiper": "11.0.5",
"usehooks-ts": "2.14.0",
"webcomponents.js": "0.7.24",
"whatwg-fetch": "3.6.20"
},
@ -118,7 +119,7 @@
"stylelint-scss": "5.3.2",
"ts-loader": "9.5.1",
"typescript": "5.3.3",
"vitest": "1.2.1",
"vitest": "1.3.0",
"webpack": "5.89.0",
"webpack-bundle-analyzer": "4.10.1",
"webpack-cli": "5.1.4",
@ -3637,9 +3638,9 @@
"dev": true
},
"node_modules/@jellyfin/sdk": {
"version": "0.0.0-unstable.202401060501",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202401060501.tgz",
"integrity": "sha512-6+mTkcr62rUqF8BoZS8K2h87fV/JjMYPqZ45faytqecJIv3GMo2cJTtBKR1LrmPuAdKhC+/1ic5E7bxIK+P9gA==",
"version": "0.0.0-unstable.202403040506",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202403040506.tgz",
"integrity": "sha512-B+i1Mpn10AC0Ks+PIVXCRcpuy7E50bsPy9AYmEt2rgfOpnAJ9qjGqyFligGI8l391NMtv/p0XJd7NUsApHU1Lg==",
"peerDependencies": {
"axios": "^1.3.4"
}
@ -5075,13 +5076,13 @@
"dev": true
},
"node_modules/@vitest/expect": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.2.1.tgz",
"integrity": "sha512-/bqGXcHfyKgFWYwIgFr1QYDaR9e64pRKxgBNWNXPefPFRhgm+K3+a/dS0cUGEreWngets3dlr8w8SBRw2fCfFQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.0.tgz",
"integrity": "sha512-7bWt0vBTZj08B+Ikv70AnLRicohYwFgzNjFqo9SxxqHHxSlUJGSXmCRORhOnRMisiUryKMdvsi1n27Bc6jL9DQ==",
"dev": true,
"dependencies": {
"@vitest/spy": "1.2.1",
"@vitest/utils": "1.2.1",
"@vitest/spy": "1.3.0",
"@vitest/utils": "1.3.0",
"chai": "^4.3.10"
},
"funding": {
@ -5089,12 +5090,12 @@
}
},
"node_modules/@vitest/runner": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.2.1.tgz",
"integrity": "sha512-zc2dP5LQpzNzbpaBt7OeYAvmIsRS1KpZQw4G3WM/yqSV1cQKNKwLGmnm79GyZZjMhQGlRcSFMImLjZaUQvNVZQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.0.tgz",
"integrity": "sha512-1Jb15Vo/Oy7mwZ5bXi7zbgszsdIBNjc4IqP8Jpr/8RdBC4nF1CTzIAn2dxYvpF1nGSseeL39lfLQ2uvs5u1Y9A==",
"dev": true,
"dependencies": {
"@vitest/utils": "1.2.1",
"@vitest/utils": "1.3.0",
"p-limit": "^5.0.0",
"pathe": "^1.1.1"
},
@ -5130,9 +5131,9 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.1.tgz",
"integrity": "sha512-Tmp/IcYEemKaqAYCS08sh0vORLJkMr0NRV76Gl8sHGxXT5151cITJCET20063wk0Yr/1koQ6dnmP6eEqezmd/Q==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.0.tgz",
"integrity": "sha512-swmktcviVVPYx9U4SEQXLV6AEY51Y6bZ14jA2yo6TgMxQ3h+ZYiO0YhAHGJNp0ohCFbPAis1R9kK0cvN6lDPQA==",
"dev": true,
"dependencies": {
"magic-string": "^0.30.5",
@ -5144,9 +5145,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.2.1.tgz",
"integrity": "sha512-vG3a/b7INKH7L49Lbp0IWrG6sw9j4waWAucwnksPB1r1FTJgV7nkBByd9ufzu6VWya/QTvQW4V9FShZbZIB2UQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.0.tgz",
"integrity": "sha512-AkCU0ThZunMvblDpPKgjIi025UxR8V7MZ/g/EwmAGpjIujLVV2X6rGYGmxE2D4FJbAy0/ijdROHMWa2M/6JVMw==",
"dev": true,
"dependencies": {
"tinyspy": "^2.2.0"
@ -5156,9 +5157,9 @@
}
},
"node_modules/@vitest/utils": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.2.1.tgz",
"integrity": "sha512-bsH6WVZYe/J2v3+81M5LDU8kW76xWObKIURpPrOXm2pjBniBu2MERI/XP60GpS4PHU3jyK50LUutOwrx4CyHUg==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.0.tgz",
"integrity": "sha512-/LibEY/fkaXQufi4GDlQZhikQsPO2entBKtfuyIpr1jV4DpaeasqkeHjhdOhU24vSHshcSuEyVlWdzvv2XmYCw==",
"dev": true,
"dependencies": {
"diff-sequences": "^29.6.3",
@ -12671,8 +12672,7 @@
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"dev": true
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
@ -18122,28 +18122,22 @@
}
},
"node_modules/strip-literal": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz",
"integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz",
"integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==",
"dev": true,
"dependencies": {
"acorn": "^8.10.0"
"js-tokens": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/strip-literal/node_modules/acorn": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
"node_modules/strip-literal/node_modules/js-tokens": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz",
"integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==",
"dev": true
},
"node_modules/style-loader": {
"version": "3.3.4",
@ -21118,18 +21112,18 @@
"dev": true
},
"node_modules/tinypool": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.1.tgz",
"integrity": "sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==",
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz",
"integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==",
"dev": true,
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tinyspy": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz",
"integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz",
"integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==",
"dev": true,
"engines": {
"node": ">=14.0.0"
@ -21900,6 +21894,20 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/usehooks-ts": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.14.0.tgz",
"integrity": "sha512-jnhrjTRJoJS7cFxz63tRYc5mzTKf/h+Ii8P0PDHymT9qDe4ZA2/gzDRmDR4WGausg5X8wMIdghwi3BBCN9JKow==",
"dependencies": {
"lodash.debounce": "^4.0.8"
},
"engines": {
"node": ">=16.15.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -22028,9 +22036,9 @@
}
},
"node_modules/vite-node": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.2.1.tgz",
"integrity": "sha512-fNzHmQUSOY+y30naohBvSW7pPn/xn3Ib/uqm+5wAJQJiqQsU0NBR78XdRJb04l4bOFKjpTWld0XAfkKlrDbySg==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.0.tgz",
"integrity": "sha512-D/oiDVBw75XMnjAXne/4feCkCEwcbr2SU1bjAhCcfI5Bq3VoOHji8/wCPAfUkDIeohJ5nSZ39fNxM3dNZ6OBOA==",
"dev": true,
"dependencies": {
"cac": "^6.7.14",
@ -22050,18 +22058,17 @@
}
},
"node_modules/vitest": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.2.1.tgz",
"integrity": "sha512-TRph8N8rnSDa5M2wKWJCMnztCZS9cDcgVTQ6tsTFTG/odHJ4l5yNVqvbeDJYJRZ6is3uxaEpFs8LL6QM+YFSdA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.0.tgz",
"integrity": "sha512-V9qb276J1jjSx9xb75T2VoYXdO1UKi+qfflY7V7w93jzX7oA/+RtYE6TcifxksxsZvygSSMwu2Uw6di7yqDMwg==",
"dev": true,
"dependencies": {
"@vitest/expect": "1.2.1",
"@vitest/runner": "1.2.1",
"@vitest/snapshot": "1.2.1",
"@vitest/spy": "1.2.1",
"@vitest/utils": "1.2.1",
"@vitest/expect": "1.3.0",
"@vitest/runner": "1.3.0",
"@vitest/snapshot": "1.3.0",
"@vitest/spy": "1.3.0",
"@vitest/utils": "1.3.0",
"acorn-walk": "^8.3.2",
"cac": "^6.7.14",
"chai": "^4.3.10",
"debug": "^4.3.4",
"execa": "^8.0.1",
@ -22070,11 +22077,11 @@
"pathe": "^1.1.1",
"picocolors": "^1.0.0",
"std-env": "^3.5.0",
"strip-literal": "^1.3.0",
"strip-literal": "^2.0.0",
"tinybench": "^2.5.1",
"tinypool": "^0.8.1",
"tinypool": "^0.8.2",
"vite": "^5.0.0",
"vite-node": "1.2.1",
"vite-node": "1.3.0",
"why-is-node-running": "^2.2.2"
},
"bin": {
@ -22089,8 +22096,8 @@
"peerDependencies": {
"@edge-runtime/vm": "*",
"@types/node": "^18.0.0 || >=20.0.0",
"@vitest/browser": "^1.0.0",
"@vitest/ui": "^1.0.0",
"@vitest/browser": "1.3.0",
"@vitest/ui": "1.3.0",
"happy-dom": "*",
"jsdom": "*"
},
@ -22184,9 +22191,9 @@
}
},
"node_modules/vitest/node_modules/npm-run-path": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz",
"integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
"dev": true,
"dependencies": {
"path-key": "^4.0.0"
@ -25218,9 +25225,9 @@
"dev": true
},
"@jellyfin/sdk": {
"version": "0.0.0-unstable.202401060501",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202401060501.tgz",
"integrity": "sha512-6+mTkcr62rUqF8BoZS8K2h87fV/JjMYPqZ45faytqecJIv3GMo2cJTtBKR1LrmPuAdKhC+/1ic5E7bxIK+P9gA==",
"version": "0.0.0-unstable.202403040506",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202403040506.tgz",
"integrity": "sha512-B+i1Mpn10AC0Ks+PIVXCRcpuy7E50bsPy9AYmEt2rgfOpnAJ9qjGqyFligGI8l391NMtv/p0XJd7NUsApHU1Lg==",
"requires": {}
},
"@jest/schemas": {
@ -26202,23 +26209,23 @@
"dev": true
},
"@vitest/expect": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.2.1.tgz",
"integrity": "sha512-/bqGXcHfyKgFWYwIgFr1QYDaR9e64pRKxgBNWNXPefPFRhgm+K3+a/dS0cUGEreWngets3dlr8w8SBRw2fCfFQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.0.tgz",
"integrity": "sha512-7bWt0vBTZj08B+Ikv70AnLRicohYwFgzNjFqo9SxxqHHxSlUJGSXmCRORhOnRMisiUryKMdvsi1n27Bc6jL9DQ==",
"dev": true,
"requires": {
"@vitest/spy": "1.2.1",
"@vitest/utils": "1.2.1",
"@vitest/spy": "1.3.0",
"@vitest/utils": "1.3.0",
"chai": "^4.3.10"
}
},
"@vitest/runner": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.2.1.tgz",
"integrity": "sha512-zc2dP5LQpzNzbpaBt7OeYAvmIsRS1KpZQw4G3WM/yqSV1cQKNKwLGmnm79GyZZjMhQGlRcSFMImLjZaUQvNVZQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.0.tgz",
"integrity": "sha512-1Jb15Vo/Oy7mwZ5bXi7zbgszsdIBNjc4IqP8Jpr/8RdBC4nF1CTzIAn2dxYvpF1nGSseeL39lfLQ2uvs5u1Y9A==",
"dev": true,
"requires": {
"@vitest/utils": "1.2.1",
"@vitest/utils": "1.3.0",
"p-limit": "^5.0.0",
"pathe": "^1.1.1"
},
@ -26241,9 +26248,9 @@
}
},
"@vitest/snapshot": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.1.tgz",
"integrity": "sha512-Tmp/IcYEemKaqAYCS08sh0vORLJkMr0NRV76Gl8sHGxXT5151cITJCET20063wk0Yr/1koQ6dnmP6eEqezmd/Q==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.0.tgz",
"integrity": "sha512-swmktcviVVPYx9U4SEQXLV6AEY51Y6bZ14jA2yo6TgMxQ3h+ZYiO0YhAHGJNp0ohCFbPAis1R9kK0cvN6lDPQA==",
"dev": true,
"requires": {
"magic-string": "^0.30.5",
@ -26252,18 +26259,18 @@
}
},
"@vitest/spy": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.2.1.tgz",
"integrity": "sha512-vG3a/b7INKH7L49Lbp0IWrG6sw9j4waWAucwnksPB1r1FTJgV7nkBByd9ufzu6VWya/QTvQW4V9FShZbZIB2UQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.0.tgz",
"integrity": "sha512-AkCU0ThZunMvblDpPKgjIi025UxR8V7MZ/g/EwmAGpjIujLVV2X6rGYGmxE2D4FJbAy0/ijdROHMWa2M/6JVMw==",
"dev": true,
"requires": {
"tinyspy": "^2.2.0"
}
},
"@vitest/utils": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.2.1.tgz",
"integrity": "sha512-bsH6WVZYe/J2v3+81M5LDU8kW76xWObKIURpPrOXm2pjBniBu2MERI/XP60GpS4PHU3jyK50LUutOwrx4CyHUg==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.0.tgz",
"integrity": "sha512-/LibEY/fkaXQufi4GDlQZhikQsPO2entBKtfuyIpr1jV4DpaeasqkeHjhdOhU24vSHshcSuEyVlWdzvv2XmYCw==",
"dev": true,
"requires": {
"diff-sequences": "^29.6.3",
@ -31866,8 +31873,7 @@
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"dev": true
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"lodash.memoize": {
"version": "4.1.2",
@ -35769,18 +35775,18 @@
"dev": true
},
"strip-literal": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz",
"integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz",
"integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==",
"dev": true,
"requires": {
"acorn": "^8.10.0"
"js-tokens": "^8.0.2"
},
"dependencies": {
"acorn": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
"js-tokens": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz",
"integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==",
"dev": true
}
}
@ -38086,15 +38092,15 @@
"dev": true
},
"tinypool": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.1.tgz",
"integrity": "sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==",
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz",
"integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==",
"dev": true
},
"tinyspy": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz",
"integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz",
"integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==",
"dev": true
},
"to-fast-properties": {
@ -38668,6 +38674,14 @@
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"requires": {}
},
"usehooks-ts": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.14.0.tgz",
"integrity": "sha512-jnhrjTRJoJS7cFxz63tRYc5mzTKf/h+Ii8P0PDHymT9qDe4ZA2/gzDRmDR4WGausg5X8wMIdghwi3BBCN9JKow==",
"requires": {
"lodash.debounce": "^4.0.8"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -38736,9 +38750,9 @@
}
},
"vite-node": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.2.1.tgz",
"integrity": "sha512-fNzHmQUSOY+y30naohBvSW7pPn/xn3Ib/uqm+5wAJQJiqQsU0NBR78XdRJb04l4bOFKjpTWld0XAfkKlrDbySg==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.0.tgz",
"integrity": "sha512-D/oiDVBw75XMnjAXne/4feCkCEwcbr2SU1bjAhCcfI5Bq3VoOHji8/wCPAfUkDIeohJ5nSZ39fNxM3dNZ6OBOA==",
"dev": true,
"requires": {
"cac": "^6.7.14",
@ -38749,18 +38763,17 @@
}
},
"vitest": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.2.1.tgz",
"integrity": "sha512-TRph8N8rnSDa5M2wKWJCMnztCZS9cDcgVTQ6tsTFTG/odHJ4l5yNVqvbeDJYJRZ6is3uxaEpFs8LL6QM+YFSdA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.0.tgz",
"integrity": "sha512-V9qb276J1jjSx9xb75T2VoYXdO1UKi+qfflY7V7w93jzX7oA/+RtYE6TcifxksxsZvygSSMwu2Uw6di7yqDMwg==",
"dev": true,
"requires": {
"@vitest/expect": "1.2.1",
"@vitest/runner": "1.2.1",
"@vitest/snapshot": "1.2.1",
"@vitest/spy": "1.2.1",
"@vitest/utils": "1.2.1",
"@vitest/expect": "1.3.0",
"@vitest/runner": "1.3.0",
"@vitest/snapshot": "1.3.0",
"@vitest/spy": "1.3.0",
"@vitest/utils": "1.3.0",
"acorn-walk": "^8.3.2",
"cac": "^6.7.14",
"chai": "^4.3.10",
"debug": "^4.3.4",
"execa": "^8.0.1",
@ -38769,11 +38782,11 @@
"pathe": "^1.1.1",
"picocolors": "^1.0.0",
"std-env": "^3.5.0",
"strip-literal": "^1.3.0",
"strip-literal": "^2.0.0",
"tinybench": "^2.5.1",
"tinypool": "^0.8.1",
"tinypool": "^0.8.2",
"vite": "^5.0.0",
"vite-node": "1.2.1",
"vite-node": "1.3.0",
"why-is-node-running": "^2.2.2"
},
"dependencies": {
@ -38819,9 +38832,9 @@
"dev": true
},
"npm-run-path": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz",
"integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
"dev": true,
"requires": {
"path-key": "^4.0.0"

View file

@ -61,7 +61,7 @@
"stylelint-scss": "5.3.2",
"ts-loader": "9.5.1",
"typescript": "5.3.3",
"vitest": "1.2.1",
"vitest": "1.3.0",
"webpack": "5.89.0",
"webpack-bundle-analyzer": "4.10.1",
"webpack-cli": "5.1.4",
@ -78,7 +78,7 @@
"@fontsource/noto-sans-kr": "5.0.17",
"@fontsource/noto-sans-sc": "5.0.17",
"@fontsource/noto-sans-tc": "5.0.17",
"@jellyfin/sdk": "unstable",
"@jellyfin/sdk": "0.0.0-unstable.202403040506",
"@loadable/component": "5.16.3",
"@mui/icons-material": "5.15.5",
"@mui/material": "5.15.5",
@ -119,6 +119,7 @@
"screenfull": "6.0.2",
"sortablejs": "1.15.2",
"swiper": "11.0.5",
"usehooks-ts": "2.14.0",
"webcomponents.js": "0.7.24",
"whatwg-fetch": "3.6.20"
},

View file

@ -1,26 +1,15 @@
import { Devices, Analytics, Input, ExpandLess, ExpandMore } from '@mui/icons-material';
import Collapse from '@mui/material/Collapse';
import { Devices, Analytics, Input } from '@mui/icons-material';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
import React from 'react';
import { useLocation } from 'react-router-dom';
import ListItemLink from 'components/ListItemLink';
import globalize from 'scripts/globalize';
const DLNA_PATHS = [
'/dashboard/dlna',
'/dashboard/dlna/profiles'
];
const DevicesDrawerSection = () => {
const location = useLocation();
const isDlnaSectionOpen = DLNA_PATHS.includes(location.pathname);
return (
<List
aria-labelledby='devices-subheader'
@ -47,24 +36,13 @@ const DevicesDrawerSection = () => {
</ListItemLink>
</ListItem>
<ListItem disablePadding>
<ListItemLink to='/dashboard/dlna' selected={false}>
<ListItemLink to='/dashboard/dlna'>
<ListItemIcon>
<Input />
</ListItemIcon>
<ListItemText primary={'DLNA'} />
{isDlnaSectionOpen ? <ExpandLess /> : <ExpandMore />}
</ListItemLink>
</ListItem>
<Collapse in={isDlnaSectionOpen} timeout='auto' unmountOnExit>
<List component='div' disablePadding>
<ListItemLink to='/dashboard/dlna' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('Settings')} />
</ListItemLink>
<ListItemLink to='/dashboard/dlna/profiles' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('TabProfiles')} />
</ListItemLink>
</List>
</Collapse>
</List>
);
};

View file

@ -2,6 +2,7 @@ import { AsyncRouteType, type AsyncRoute } from 'components/router/AsyncRoute';
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
{ path: 'activity', type: AsyncRouteType.Dashboard },
{ path: 'dlna', type: AsyncRouteType.Dashboard },
{ path: 'notifications', type: AsyncRouteType.Dashboard },
{ path: 'users', type: AsyncRouteType.Dashboard },
{ path: 'users/access', type: AsyncRouteType.Dashboard },

View file

@ -31,24 +31,6 @@ export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
controller: 'dashboard/devices/device',
view: 'dashboard/devices/device.html'
}
}, {
path: 'dlna/profiles/edit',
pageProps: {
controller: 'dashboard/dlna/profile',
view: 'dashboard/dlna/profile.html'
}
}, {
path: 'dlna/profiles',
pageProps: {
controller: 'dashboard/dlna/profiles',
view: 'dashboard/dlna/profiles.html'
}
}, {
path: 'dlna',
pageProps: {
controller: 'dashboard/dlna/settings',
view: 'dashboard/dlna/settings.html'
}
}, {
path: 'plugins/add',
pageProps: {

View file

@ -8,8 +8,8 @@ export const REDIRECTS: Redirect[] = [
{ from: 'dashboardgeneral.html', to: '/dashboard/settings' },
{ from: 'device.html', to: '/dashboard/devices/edit' },
{ from: 'devices.html', to: '/dashboard/devices' },
{ from: 'dlnaprofile.html', to: '/dashboard/dlna/profiles/edit' },
{ from: 'dlnaprofiles.html', to: '/dashboard/dlna/profiles' },
{ from: 'dlnaprofile.html', to: '/dashboard/dlna' },
{ from: 'dlnaprofiles.html', to: '/dashboard/dlna' },
{ from: 'dlnasettings.html', to: '/dashboard/dlna' },
{ from: 'edititemmetadata.html', to: '/metadata' },
{ from: 'encodingsettings.html', to: '/dashboard/playback/transcoding' },

View file

@ -0,0 +1,33 @@
import Alert from '@mui/material/Alert/Alert';
import Box from '@mui/material/Box/Box';
import Button from '@mui/material/Button/Button';
import React from 'react';
import { Link } from 'react-router-dom';
import Page from 'components/Page';
import globalize from 'scripts/globalize';
const DlnaPage = () => (
<Page
id='dlnaSettingsPage'
title='DLNA'
className='mainAnimatedPage type-interior'
>
<div className='content-primary'>
<h2>DLNA</h2>
<Alert severity='info'>
<Box sx={{ marginBottom: 2 }}>
{globalize.translate('DlnaMovedMessage')}
</Box>
<Button
component={Link}
to='/dashboard/plugins/add?name=DLNA&guid=33eba9cd7da14720967fdd7dae7b74a1'
>
{globalize.translate('GetThePlugin')}
</Button>
</Alert>
</div>
</Page>
);
export default DlnaPage;

View file

@ -1,23 +1,13 @@
import Alert from '@mui/material/Alert/Alert';
import Box from '@mui/material/Box/Box';
import Button from '@mui/material/Button/Button';
import React from 'react';
import { Link } from 'react-router-dom';
import Page from 'components/Page';
import globalize from 'scripts/globalize';
const PluginLink = () => (
<div
dangerouslySetInnerHTML={{
__html: `<a
is='emby-linkbutton'
class='button-link'
href='#/dashboard/plugins/add?name=Webhook&guid=71552a5a5c5c4350a2aeebe451a30173'
>
${globalize.translate('GetThePlugin')}
</a>`
}}
/>
);
const Notifications = () => (
const NotificationsPage = () => (
<Page
id='notificationSettingPage'
title={globalize.translate('Notifications')}
@ -25,12 +15,20 @@ const Notifications = () => (
>
<div className='content-primary'>
<h2>{globalize.translate('Notifications')}</h2>
<p>
<Alert severity='info'>
<Box sx={{ marginBottom: 2 }}>
{globalize.translate('NotificationsMovedMessage')}
</p>
<PluginLink />
</Box>
<Button
component={Link}
to='/dashboard/plugins/add?name=Webhook&guid=71552a5a5c5c4350a2aeebe451a30173'
>
{globalize.translate('GetThePlugin')}
</Button>
</Alert>
</div>
</Page>
);
export default Notifications;
export default NotificationsPage;

View file

@ -58,7 +58,7 @@ const UserProfiles: FunctionComponent = () => {
const menuItems: MenuEntry[] = [];
menuItems.push({
name: globalize.translate('ButtonOpen'),
name: globalize.translate('ButtonEditUser'),
id: 'open',
icon: 'mode_edit'
});

View file

@ -3,15 +3,13 @@ import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import React from 'react';
import { useApi } from 'hooks/useApi';
import { useSystemInfo } from 'hooks/useSystemInfo';
import ListItemLink from 'components/ListItemLink';
import appIcon from 'assets/img/icon-transparent.png';
const DrawerHeaderLink = () => {
const { api } = useApi();
const { data: systemInfo } = useSystemInfo(api);
const { data: systemInfo } = useSystemInfo();
return (
<ListItemLink to='/'>

View file

@ -1,16 +1,46 @@
import React, { FunctionComponent, useState } from 'react';
import React, { type FC, useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import Page from '../../../components/Page';
import SearchFields from '../../../components/search/SearchFields';
import SearchResults from '../../../components/search/SearchResults';
import SearchSuggestions from '../../../components/search/SearchSuggestions';
import LiveTVSearchResults from '../../../components/search/LiveTVSearchResults';
import globalize from '../../../scripts/globalize';
import Page from 'components/Page';
import SearchFields from 'components/search/SearchFields';
import SearchResults from 'components/search/SearchResults';
import SearchSuggestions from 'components/search/SearchSuggestions';
import LiveTVSearchResults from 'components/search/LiveTVSearchResults';
import { usePrevious } from 'hooks/usePrevious';
import globalize from 'scripts/globalize';
const Search: FunctionComponent = () => {
const [ query, setQuery ] = useState<string>();
const [ searchParams ] = useSearchParams();
const COLLECTION_TYPE_PARAM = 'collectionType';
const PARENT_ID_PARAM = 'parentId';
const QUERY_PARAM = 'query';
const SERVER_ID_PARAM = 'serverId';
const Search: FC = () => {
const [ searchParams, setSearchParams ] = useSearchParams();
const urlQuery = searchParams.get(QUERY_PARAM) || '';
const [ query, setQuery ] = useState(urlQuery);
const prevQuery = usePrevious(query, '');
useEffect(() => {
if (query !== prevQuery) {
if (query === '' && urlQuery !== '') {
// The query input has been cleared; remove the url param
searchParams.delete(QUERY_PARAM);
setSearchParams(searchParams, { replace: true });
} else if (query !== urlQuery) {
// Update the query url param value
searchParams.set(QUERY_PARAM, query);
setSearchParams(searchParams, { replace: true });
}
} else if (query !== urlQuery) {
// Update the query if the query url param has changed
if (!urlQuery) {
searchParams.delete(QUERY_PARAM);
setSearchParams(searchParams, { replace: true });
}
setQuery(urlQuery);
}
}, [query, prevQuery, searchParams, setSearchParams, urlQuery]);
return (
<Page
@ -18,22 +48,22 @@ const Search: FunctionComponent = () => {
title={globalize.translate('Search')}
className='mainAnimatedPage libraryPage allLibraryPage noSecondaryNavPage'
>
<SearchFields onSearch={setQuery} />
<SearchFields query={query} onSearch={setQuery} />
{!query
&& <SearchSuggestions
parentId={searchParams.get('parentId')}
parentId={searchParams.get(PARENT_ID_PARAM)}
/>
}
<SearchResults
serverId={searchParams.get('serverId') || window.ApiClient.serverId()}
parentId={searchParams.get('parentId')}
collectionType={searchParams.get('collectionType')}
serverId={searchParams.get(SERVER_ID_PARAM) || window.ApiClient.serverId()}
parentId={searchParams.get(PARENT_ID_PARAM)}
collectionType={searchParams.get(COLLECTION_TYPE_PARAM)}
query={query}
/>
<LiveTVSearchResults
serverId={searchParams.get('serverId') || window.ApiClient.serverId()}
parentId={searchParams.get('parentId')}
collectionType={searchParams.get('collectionType')}
serverId={searchParams.get(SERVER_ID_PARAM) || window.ApiClient.serverId()}
parentId={searchParams.get(PARENT_ID_PARAM)}
collectionType={searchParams.get(COLLECTION_TYPE_PARAM)}
query={query}
/>
</Page>

View file

@ -9,8 +9,64 @@ import 'material-design-icons-iconfont';
import '../../styles/scrollstyles.scss';
import '../../components/listview/listview.scss';
function getOffsets(elems) {
const results = [];
interface OptionItem {
asideText?: string;
divider?: boolean;
icon?: string;
id?: string;
innerText?: string;
name?: string;
secondaryText?: string;
selected?: boolean;
textContent?: string;
value?: string;
}
interface Options {
items: OptionItem[];
border?: boolean;
callback?: (id: string) => void;
dialogClass?: string;
enableHistory?: boolean;
entryAnimationDuration?: number;
entryAnimation?: string;
exitAnimationDuration?: number;
exitAnimation?: string;
menuItemClass?: string;
offsetLeft?: number;
offsetTop?: number;
positionTo?: Element | null;
positionY?: string;
resolveOnClick?: boolean | (string | null)[];
shaded?: boolean;
showCancel?: boolean;
text?: string;
timeout?: number;
title?: string;
}
interface Offset {
top: number;
left: number;
width: number;
height: number;
}
interface DialogOptions {
autoFocus?: boolean;
enableHistory?: boolean;
entryAnimationDuration?: number;
entryAnimation?: string;
exitAnimationDuration?: number;
exitAnimation?: string;
modal?: boolean;
removeOnClose?: boolean;
scrollY?: boolean;
size?: string;
}
function getOffsets(elems: Element[]): Offset[] {
const results: Offset[] = [];
if (!document) {
return results;
@ -30,12 +86,12 @@ function getOffsets(elems) {
return results;
}
function getPosition(options, dlg) {
function getPosition(positionTo: Element, options: Options, dlg: HTMLElement) {
const windowSize = dom.getWindowSize();
const windowHeight = windowSize.innerHeight;
const windowWidth = windowSize.innerWidth;
const pos = getOffsets([options.positionTo])[0];
const pos = getOffsets([positionTo])[0];
if (options.positionY !== 'top') {
pos.top += (pos.height || 0) / 2;
@ -71,19 +127,22 @@ function getPosition(options, dlg) {
return pos;
}
function centerFocus(elem, horiz, on) {
function centerFocus(elem: Element, horiz: boolean, on: boolean) {
import('../../scripts/scrollHelper').then((scrollHelper) => {
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
}).catch(e => {
console.warn('Error in centerFocus', e);
});
}
export function show(options) {
/* eslint-disable-next-line sonarjs/cognitive-complexity */
export function show(options: Options) {
// items
// positionTo
// showCancel
// title
const dialogOptions = {
const dialogOptions: DialogOptions = {
removeOnClose: true,
enableHistory: options.enableHistory,
scrollY: false
@ -239,7 +298,10 @@ export function show(options) {
dlg.innerHTML = html;
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.actionSheetScroller'), false, true);
const scroller = dlg.querySelector('.actionSheetScroller');
if (scroller) {
centerFocus(scroller, false, true);
}
}
const btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet');
@ -249,9 +311,9 @@ export function show(options) {
});
}
let selectedId;
let selectedId: string | null = null;
let timeout;
let timeout: ReturnType<typeof setTimeout> | undefined;
if (options.timeout) {
timeout = setTimeout(function () {
dialogHelper.close(dlg);
@ -259,16 +321,16 @@ export function show(options) {
}
return new Promise(function (resolve, reject) {
let isResolved;
let isResolved = false;
dlg.addEventListener('click', function (e) {
const actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem');
const actionSheetMenuItem = dom.parentWithClass(e.target as HTMLElement, 'actionSheetMenuItem');
if (actionSheetMenuItem) {
selectedId = actionSheetMenuItem.getAttribute('data-id');
if (options.resolveOnClick) {
if (options.resolveOnClick.indexOf) {
if (Array.isArray(options.resolveOnClick)) {
if (options.resolveOnClick.indexOf(selectedId) !== -1) {
resolve(selectedId);
isResolved = true;
@ -285,12 +347,15 @@ export function show(options) {
dlg.addEventListener('close', function () {
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.actionSheetScroller'), false, false);
const scroller = dlg.querySelector('.actionSheetScroller');
if (scroller) {
centerFocus(scroller, false, false);
}
}
if (timeout) {
clearTimeout(timeout);
timeout = null;
timeout = undefined;
}
if (!isResolved) {
@ -306,13 +371,15 @@ export function show(options) {
}
});
dialogHelper.open(dlg);
dialogHelper.open(dlg).catch(e => {
console.warn('DialogHelper.open error', e);
});
const pos = options.positionTo && dialogOptions.size !== 'fullscreen' ? getPosition(options, dlg) : null;
const pos = options.positionTo && dialogOptions.size !== 'fullscreen' ? getPosition(options.positionTo, options, dlg) : null;
if (pos) {
dlg.style.position = 'fixed';
dlg.style.margin = 0;
dlg.style.margin = '0';
dlg.style.left = pos.left + 'px';
dlg.style.top = pos.top + 'px';
}

View file

@ -339,7 +339,8 @@ function executeCommand(item, id, options) {
break;
case 'addtoplaylist':
import('./playlisteditor/playlisteditor').then(({ default: PlaylistEditor }) => {
new PlaylistEditor({
const playlistEditor = new PlaylistEditor();
playlistEditor.show({
items: [itemId],
serverId: serverId
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));

View file

@ -153,6 +153,7 @@ function onSubmit(e) {
DateCreated: getDateValue(form, '#txtDateAdded', 'DateCreated'),
EndDate: getDateValue(form, '#txtEndDate', 'EndDate'),
ProductionYear: form.querySelector('#txtProductionYear').value,
Height: form.querySelector('#selectHeight').value,
AspectRatio: form.querySelector('#txtOriginalAspectRatio').value,
Video3DFormat: form.querySelector('#select3dFormat').value,
@ -650,6 +651,12 @@ function setFieldVisibilities(context, item) {
hideElement('#fldPlaceOfBirth');
}
if (item.MediaType === 'Video' && item.Type === 'TvChannel') {
showElement('#fldHeight');
} else {
hideElement('#fldHeight');
}
if (item.MediaType === 'Video' && item.Type !== 'TvChannel') {
showElement('#fldOriginalAspectRatio');
} else {
@ -828,6 +835,8 @@ function fillItemInfo(context, item, parentalRatingOptions) {
const placeofBirth = item.ProductionLocations?.length ? item.ProductionLocations[0] : '';
context.querySelector('#txtPlaceOfBirth').value = placeofBirth;
context.querySelector('#selectHeight').value = item.Height || '';
context.querySelector('#txtOriginalAspectRatio').value = item.AspectRatio || '';
context.querySelector('#selectLanguage').value = item.PreferredMetadataLanguage || '';

View file

@ -142,6 +142,16 @@
<select is="emby-select" id="selectCustomRating" label="${LabelCustomRating}"></select>
</div>
</div>
<div id="fldHeight" class="selectContainer hide">
<select is="emby-select" id="selectHeight" label="${MediaInfoResolution}">
<option value="0"></option>
<option value="480">${ChannelResolutionSD}</option>
<option value="576">${ChannelResolutionSDPAL}</option>
<option value="720">${ChannelResolutionHD}</option>
<option value="1080">${ChannelResolutionFullHD}</option>
<option value="2160">${ChannelResolutionUHD4K}</option>
</select>
</div>
<div class="inlineForm">
<div id="fldOriginalAspectRatio" class="inputContainer hide">
<input is="emby-input" id="txtOriginalAspectRatio" type="text" label="${LabelOriginalAspectRatio}" />

View file

@ -6,7 +6,6 @@ import dom from '../../scripts/dom';
import './multiSelect.scss';
import ServerConnections from '../ServerConnections';
import alert from '../alert';
import PlaylistEditor from '../playlisteditor/playlisteditor';
import confirm from '../confirm/confirm';
import itemHelper from '../itemHelper';
import datetime from '../../scripts/datetime';
@ -269,9 +268,16 @@ function showMenuForSelectedItems(e) {
dispatchNeedsRefresh();
break;
case 'playlist':
new PlaylistEditor({
import('../playlisteditor/playlisteditor').then(({ default: PlaylistEditor }) => {
const playlistEditor = new PlaylistEditor();
playlistEditor.show({
items: items,
serverId: serverId
}).catch(() => {
// Dialog closed
});
}).catch(err => {
console.error('[AddToPlaylist] failed to load playlist editor', err);
});
hideSelections();
dispatchNeedsRefresh();

View file

@ -222,7 +222,7 @@ function centerFocus(elem, horiz, on) {
}
export class PlaylistEditor {
constructor(options) {
show(options) {
const items = options.items || {};
currentServerId = options.serverId;

View file

@ -704,15 +704,20 @@ export default function () {
import('../playlisteditor/playlisteditor').then(({ default: PlaylistEditor }) => {
getSaveablePlaylistItems().then(function (items) {
const serverId = items.length ? items[0].ServerId : ApiClient.serverId();
new PlaylistEditor({
const playlistEditor = new PlaylistEditor();
playlistEditor.show({
items: items.map(function (i) {
return i.Id;
}),
serverId: serverId,
enableAddToPlayQueue: false,
defaultValue: 'new'
}).catch(() => {
// Dialog closed
});
});
}).catch(err => {
console.error('[savePlaylist] failed to load playlist editor', err);
});
}

View file

@ -2,7 +2,8 @@ import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
import type { ApiClient } from 'jellyfin-apiclient';
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
import classNames from 'classnames';
import React, { FunctionComponent, useEffect, useState } from 'react';
import React, { type FC, useCallback, useEffect, useState } from 'react';
import { useDebounceValue } from 'usehooks-ts';
import globalize from '../../scripts/globalize';
import ServerConnections from '../ServerConnections';
@ -30,7 +31,7 @@ type LiveTVSearchResultsProps = {
/*
* React component to display search result rows for live tv library search
*/
const LiveTVSearchResults: FunctionComponent<LiveTVSearchResultsProps> = ({ serverId = window.ApiClient.serverId(), parentId, collectionType, query }: LiveTVSearchResultsProps) => {
const LiveTVSearchResults: FC<LiveTVSearchResultsProps> = ({ serverId = window.ApiClient.serverId(), parentId, collectionType, query }: LiveTVSearchResultsProps) => {
const [ movies, setMovies ] = useState<BaseItemDto[]>([]);
const [ episodes, setEpisodes ] = useState<BaseItemDto[]>([]);
const [ sports, setSports ] = useState<BaseItemDto[]>([]);
@ -38,11 +39,11 @@ const LiveTVSearchResults: FunctionComponent<LiveTVSearchResultsProps> = ({ serv
const [ news, setNews ] = useState<BaseItemDto[]>([]);
const [ programs, setPrograms ] = useState<BaseItemDto[]>([]);
const [ channels, setChannels ] = useState<BaseItemDto[]>([]);
const [ debouncedQuery ] = useDebounceValue(query, 500);
useEffect(() => {
const getDefaultParameters = () => ({
const getDefaultParameters = useCallback(() => ({
ParentId: parentId,
searchTerm: query,
searchTerm: debouncedQuery,
Limit: 24,
Fields: 'PrimaryImageAspectRatio,CanDelete,MediaSourceCount',
Recursive: true,
@ -53,8 +54,9 @@ const LiveTVSearchResults: FunctionComponent<LiveTVSearchResultsProps> = ({ serv
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false
});
}), [ parentId, debouncedQuery ]);
useEffect(() => {
const fetchItems = (apiClient: ApiClient, params = {}) => apiClient?.getItems(
apiClient?.getCurrentUserId(),
{
@ -73,7 +75,10 @@ const LiveTVSearchResults: FunctionComponent<LiveTVSearchResultsProps> = ({ serv
setPrograms([]);
setChannels([]);
if (query && collectionType === CollectionType.Livetv) {
if (!debouncedQuery || collectionType !== CollectionType.Livetv) {
return;
}
const apiClient = ServerConnections.getApiClient(serverId);
// Movies row
@ -130,8 +135,7 @@ const LiveTVSearchResults: FunctionComponent<LiveTVSearchResultsProps> = ({ serv
fetchItems(apiClient, { IncludeItemTypes: 'TvChannel' })
.then(result => setChannels(result.Items || []))
.catch(() => setChannels([]));
}
}, [collectionType, parentId, query, serverId]);
}, [collectionType, debouncedQuery, getDefaultParameters, parentId, serverId]);
return (
<div
@ -139,7 +143,7 @@ const LiveTVSearchResults: FunctionComponent<LiveTVSearchResultsProps> = ({ serv
'searchResults',
'padded-bottom-page',
'padded-top',
{ 'hide': !query || collectionType !== CollectionType.Livetv }
{ 'hide': !debouncedQuery || collectionType !== CollectionType.Livetv }
)}
>
<SearchResultsRow

View file

@ -1,90 +1,62 @@
import debounce from 'lodash-es/debounce';
import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react';
import React, { type ChangeEvent, type FC, useCallback } from 'react';
import AlphaPicker from '../alphaPicker/AlphaPickerComponent';
import Input from 'elements/emby-input/Input';
import globalize from '../../scripts/globalize';
import 'material-design-icons-iconfont';
import '../../elements/emby-input/emby-input';
import '../../styles/flexstyles.scss';
import './searchfields.scss';
import layoutManager from '../layoutManager';
import browser from '../../scripts/browser';
// There seems to be some compatibility issues here between
// React and our legacy web components, so we need to inject
// them as an html string for now =/
const createInputElement = () => ({
__html: `<input
is="emby-input"
class="searchfields-txtSearch"
type="text"
data-keyboard="true"
placeholder="${globalize.translate('Search')}"
autocomplete="off"
maxlength="40"
autofocus
/>`
});
import 'material-design-icons-iconfont';
const normalizeInput = (value = '') => value.trim();
import '../../styles/flexstyles.scss';
import './searchfields.scss';
type SearchFieldsProps = {
query: string,
onSearch?: (query: string) => void
};
// eslint-disable-next-line @typescript-eslint/no-empty-function
const SearchFields: FunctionComponent<SearchFieldsProps> = ({ onSearch = () => {} }: SearchFieldsProps) => {
const element = useRef<HTMLDivElement>(null);
const getSearchInput = () => element?.current?.querySelector<HTMLInputElement>('.searchfields-txtSearch');
const debouncedOnSearch = useMemo(() => debounce(onSearch, 400), [onSearch]);
useEffect(() => {
getSearchInput()?.addEventListener('input', e => {
debouncedOnSearch(normalizeInput((e.target as HTMLInputElement).value));
});
getSearchInput()?.focus();
return () => {
debouncedOnSearch.cancel();
};
}, [debouncedOnSearch]);
const SearchFields: FC<SearchFieldsProps> = ({
onSearch = () => { /* no-op */ },
query
}: SearchFieldsProps) => {
const onAlphaPicked = useCallback((e: Event) => {
const value = (e as CustomEvent).detail.value;
const searchInput = getSearchInput();
if (!searchInput) {
console.error('Unexpected null reference');
return;
}
if (value === 'backspace') {
const currentValue = searchInput.value;
searchInput.value = currentValue.length ? currentValue.substring(0, currentValue.length - 1) : '';
onSearch(query.length ? query.substring(0, query.length - 1) : '');
} else {
searchInput.value += value;
onSearch(query + value);
}
}, [ onSearch, query ]);
searchInput.dispatchEvent(new CustomEvent('input', { bubbles: true }));
}, []);
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
onSearch(e.target.value);
}, [ onSearch ]);
return (
<div
className='padded-left padded-right searchFields'
ref={element}
>
<div className='padded-left padded-right searchFields'>
<div className='searchFieldsInner flex align-items-center justify-content-center'>
<span className='searchfields-icon material-icons search' aria-hidden='true' />
<div
className='inputContainer flex-grow'
style={{ marginBottom: 0 }}
dangerouslySetInnerHTML={createInputElement()}
>
<Input
id='searchTextInput'
className='searchfields-txtSearch'
type='text'
data-keyboard='true'
placeholder={globalize.translate('Search')}
autoComplete='off'
maxLength={40}
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
value={query}
onChange={onChange}
/>
</div>
</div>
{layoutManager.tv && !browser.tv
&& <AlphaPicker onAlphaPicked={onAlphaPicked} />
}

View file

@ -1,8 +1,9 @@
import type { BaseItemDto, BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
import type { ApiClient } from 'jellyfin-apiclient';
import classNames from 'classnames';
import React, { FunctionComponent, useCallback, useEffect, useState } from 'react';
import React, { type FC, useCallback, useEffect, useState } from 'react';
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
import { useDebounceValue } from 'usehooks-ts';
import globalize from '../../scripts/globalize';
import ServerConnections from '../ServerConnections';
@ -30,7 +31,7 @@ const isTVShows = (collectionType: string) => collectionType === CollectionType.
/*
* React component to display search result rows for global search and non-live tv library search
*/
const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = window.ApiClient.serverId(), parentId, collectionType, query }: SearchResultsProps) => {
const SearchResults: FC<SearchResultsProps> = ({ serverId = window.ApiClient.serverId(), parentId, collectionType, query }: SearchResultsProps) => {
const [ movies, setMovies ] = useState<BaseItemDto[]>([]);
const [ shows, setShows ] = useState<BaseItemDto[]>([]);
const [ episodes, setEpisodes ] = useState<BaseItemDto[]>([]);
@ -47,11 +48,12 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
const [ books, setBooks ] = useState<BaseItemDto[]>([]);
const [ people, setPeople ] = useState<BaseItemDto[]>([]);
const [ collections, setCollections ] = useState<BaseItemDto[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [ isLoading, setIsLoading ] = useState(false);
const [ debouncedQuery ] = useDebounceValue(query, 500);
const getDefaultParameters = useCallback(() => ({
ParentId: parentId,
searchTerm: query,
searchTerm: debouncedQuery,
Limit: 100,
Fields: 'PrimaryImageAspectRatio,CanDelete,MediaSourceCount',
Recursive: true,
@ -62,7 +64,7 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false
}), [parentId, query]);
}), [ parentId, debouncedQuery ]);
const fetchArtists = useCallback((apiClient: ApiClient, params = {}) => (
apiClient?.getArtists(
@ -97,6 +99,10 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
).then(ensureNonNullItems)
), [getDefaultParameters]);
useEffect(() => {
if (query) setIsLoading(true);
}, [ query ]);
useEffect(() => {
// Reset state
setMovies([]);
@ -116,13 +122,11 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
setPeople([]);
setCollections([]);
if (!query) {
if (!debouncedQuery) {
setIsLoading(false);
return;
}
setIsLoading(true);
const apiClient = ServerConnections.getApiClient(serverId);
const fetchPromises = [];
@ -230,7 +234,7 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
console.error('An error occurred while fetching data:', error);
setIsLoading(false); // Set loading to false even if an error occurs
});
}, [collectionType, fetchArtists, fetchItems, fetchPeople, query, serverId]);
}, [collectionType, fetchArtists, fetchItems, fetchPeople, debouncedQuery, serverId]);
const allEmpty = [movies, shows, episodes, videos, programs, channels, playlists, artists, albums, songs, photoAlbums, photos, audioBooks, books, people, collections].every(arr => arr.length === 0);
@ -240,7 +244,7 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
'searchResults',
'padded-bottom-page',
'padded-top',
{ 'hide': !query || collectionType === CollectionType.Livetv }
{ 'hide': !debouncedQuery || collectionType === CollectionType.Livetv }
)}
>
{isLoading ? (
@ -335,8 +339,10 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({ serverId = windo
cardOptions={{ coverImage: true }}
/>
{allEmpty && query && !isLoading && (
<div className='sorry-text'>{globalize.translate('SearchResultsEmpty', query)}</div>
{allEmpty && debouncedQuery && !isLoading && (
<div className='noItemsMessage centerMessage'>
{globalize.translate('SearchResultsEmpty', debouncedQuery)}
</div>
)}
</>
)}

View file

@ -9,14 +9,3 @@
font-size: 2em;
align-self: flex-end;
}
.sorry-text {
font-size: 2em;
text-align: center;
font-family: inherit;
width: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

View file

@ -282,11 +282,15 @@ function executeAction(card, target, action) {
function addToPlaylist(item) {
import('./playlisteditor/playlisteditor').then(({ default: PlaylistEditor }) => {
new PlaylistEditor().show({
const playlistEditor = new PlaylistEditor();
playlistEditor.show({
items: [item.Id],
serverId: item.ServerId
}).catch(() => {
// Dialog closed
});
}).catch(err => {
console.error('[addToPlaylist] failed to load playlist editor', err);
});
}

View file

@ -8,16 +8,22 @@
<span class="material-icons chevron_right" aria-hidden="true"></span>
</a>
<div class="paperList" style="padding: 1em;">
<p id="serverName"></p>
<p id="versionNumber"></p>
<div class="serverInfo paperList">
<div>${LabelServerName}</div>
<div id="serverName"></div>
<div>${LabelServerVersion}</div>
<div id="versionNumber"></div>
<div>${LabelWebVersion}</div>
<div id="webVersion"></div>
<div>${LabelBuildVersion}</div>
<div id="buildVersion"></div>
</div>
<div class="dashboardActionsContainer">
<button is="emby-button" type="button" class="raised btnRefresh">
<span>${ButtonScanAllLibraries}</span>
</button>
<button is="emby-button" type="button" id="btnRestartServer" class="raised hide" onclick="DashboardPage.restart(this);">
<button is="emby-button" type="button" id="btnRestartServer" class="raised" onclick="DashboardPage.restart(this);">
<span>${Restart}</span>
</button>
<button is="emby-button" type="button" id="btnShutdown" class="raised" onclick="DashboardPage.shutdown(this);">

View file

@ -1,4 +1,5 @@
import escapeHtml from 'escape-html';
import datetime from '../../scripts/datetime';
import Events from '../../utils/events.ts';
import itemHelper from '../../components/itemHelper';
@ -14,10 +15,6 @@ import imageLoader from '../../components/images/imageLoader';
import ActivityLog from '../../components/activitylog';
import imageHelper from '../../utils/image';
import indicators from '../../components/indicators/indicators';
import '../../components/listview/listview.scss';
import '../../elements/emby-button/emby-button';
import '../../styles/flexstyles.scss';
import '../../elements/emby-itemscontainer/emby-itemscontainer';
import taskButton from '../../scripts/taskbutton';
import Dashboard from '../../utils/dashboard';
import ServerConnections from '../../components/ServerConnections';
@ -25,6 +22,19 @@ import alert from '../../components/alert';
import confirm from '../../components/confirm/confirm';
import { getDefaultBackgroundClass } from '../../components/cardbuilder/cardBuilderUtils';
import { getSystemInfoQuery } from 'hooks/useSystemInfo';
import { toApi } from 'utils/jellyfin-apiclient/compat';
import { queryClient } from 'utils/query/queryClient';
import { version as WEB_VERSION } from '../../../package.json';
import '../../elements/emby-button/emby-button';
import '../../elements/emby-itemscontainer/emby-itemscontainer';
import '../../components/listview/listview.scss';
import '../../styles/flexstyles.scss';
import './dashboard.scss';
function showPlaybackInfo(btn, session) {
let title;
const text = [];
@ -199,15 +209,14 @@ function refreshActiveRecordings(view, apiClient) {
}
function reloadSystemInfo(view, apiClient) {
apiClient.getSystemInfo().then(function (systemInfo) {
view.querySelector('#serverName').innerText = globalize.translate('DashboardServerName', systemInfo.ServerName);
view.querySelector('#versionNumber').innerText = globalize.translate('DashboardVersionNumber', systemInfo.Version);
view.querySelector('#buildVersion').innerText = __JF_BUILD_VERSION__;
view.querySelector('#webVersion').innerText = WEB_VERSION;
if (systemInfo.CanSelfRestart) {
view.querySelector('#btnRestartServer').classList.remove('hide');
} else {
view.querySelector('#btnRestartServer').classList.add('hide');
}
queryClient
.fetchQuery(getSystemInfoQuery(toApi(apiClient)))
.then(systemInfo => {
view.querySelector('#serverName').innerText = systemInfo.ServerName;
view.querySelector('#versionNumber').innerText = systemInfo.Version;
view.querySelector('#cachePath').innerText = systemInfo.CachePath;
view.querySelector('#logPath').innerText = systemInfo.LogPath;

View file

@ -0,0 +1,17 @@
.serverInfo {
display: flex;
flex-wrap: wrap;
gap: 1em;
padding: 1em;
> *:nth-child(odd) {
flex: 1 0 20%;
min-width: 7.5em;
font-weight: bold;
}
> *:nth-child(even) {
flex: 1 0 70%;
}
}

View file

@ -1,584 +0,0 @@
<div id="dlnaProfilePage" data-role="page" class="page type-interior dlnaPage withTabs">
<div data-role="content">
<div class="content-primary">
<form class="dlnaProfileForm" style="max-width: 650px;">
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle">${HeaderProfileInformation}</h2>
</div>
</div>
<div data-role="controlgroup" data-type="horizontal" data-mini="true">
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioInfo" data-value="tabInfo">${ButtonInfo}</a>
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioDirectPlay" data-value="tabDirectPlayProfiles">${TabDirectPlay}</a>
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioTranscoding" data-value="tabTranscodingProfiles">${Transcoding}</a>
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioContainers" data-value="tabContainerProfiles">${TabContainers}</a>
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioCodecs" data-value="tabCodecProfiles">${TabCodecs}</a>
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioMediaProfiles" data-value="tabMediaProfiles">${TabResponses}</a>
</div>
<br />
<div class="tabContent tabInfo">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtName" required="required" label="${LabelName}" />
</div>
<div class="selectContainer">
<select is="emby-select" id="selectUser" label="${LabelUserLibrary}"></select>
<div class="fieldDescription">${LabelUserLibraryHelp}</div>
</div>
<div>
<h3 class="checkboxListLabel">${LabelSupportedMediaTypes}</h3>
<div class="checkboxList paperList checkboxList-paperList">
<label>
<input is="emby-checkbox" type="checkbox" id="chkAudio" data-value="Audio" class="chkMediaType" />
<span>${Audio}</span>
</label>
<label>
<input is="emby-checkbox" type="checkbox" id="chkPhoto" data-value="Photo" class="chkMediaType" />
<span>${Photo}</span>
</label>
<label>
<input is="emby-checkbox" type="checkbox" id="chkVideo" data-value="Video" class="chkMediaType" />
<span>${Video}</span>
</label>
</div>
</div>
<br />
<div class="inputContainer">
<input is="emby-input" type="number" id="txtMaxAllowedBitrate" pattern="[0-9]*" min="1" label="${LabelMaxStreamingBitrate}" />
<div class="fieldDescription">${LabelMaxStreamingBitrateHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="number" id="txtMusicStreamingTranscodingBitrate" pattern="[0-9]*" min="1" label="${LabelMusicStreamingTranscodingBitrate}" />
<div class="fieldDescription">${LabelMusicStreamingTranscodingBitrateHelp}</div>
</div>
<div style="display:none;">
<label for="chkIgnoreTranscodeByteRangeRequests">${OptionIgnoreTranscodeByteRangeRequests}</label>
<input type="checkbox" id="chkIgnoreTranscodeByteRangeRequests" data-mini="true" />
<div class="fieldDescription">${OptionIgnoreTranscodeByteRangeRequestsHelp}</div>
</div>
<div is="emby-collapse" title="${HeaderIdentification}">
<div class="collapseContent">
<h3>${HeaderIdentificationCriteriaHelp}</h3>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdFriendlyName" label="${LabelFriendlyName}" />
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdManufacturer" label="${LabelManufacturer}" />
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdManufacturerUrl" label="${LabelManufacturerUrl}" />
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdModelName" label="${LabelModelName}" />
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdModelNumber" label="${LabelModelNumber}" />
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdModelDesription" label="${LabelModelDescription}" />
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdModelUrl" label="${LabelModelUrl}" />
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdSerialNumber" label="${LabelSerialNumber}" />
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdDeviceDescription" label="${LabelDeviceDescription}" />
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
</div>
<div>
<h2 style="vertical-align:middle;display:inline-block;">${HeaderHttpHeaders}</h2>
<button is="emby-button" type="button" class="fab btnAddIdentificationHttpHeader submit sectionTitleButton" style="margin-left:1em;" title="${Add}">
<span class="material-icons add" aria-hidden="true"></span>
</button>
</div>
<div class="httpHeaderIdentificationList"></div>
</div>
</div>
<div is="emby-collapse" title="${Display}">
<div class="collapseContent">
<br />
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="chkRequiresPlainFolders" />
<span>${OptionPlainStorageFolders}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${OptionPlainStorageFoldersHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="chkRequiresPlainVideoItems" />
<span>${OptionPlainVideoItems}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${OptionPlainVideoItemsHelp}</div>
</div>
</div>
</div>
<div is="emby-collapse" title="${HeaderImageSettings}">
<div class="collapseContent">
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="chkEnableAlbumArtInDidl" data-mini="true" />
<span>${LabelEmbedAlbumArtDidl}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LabelEmbedAlbumArtDidlHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="chkEnableSingleImageLimit" data-mini="true" />
<span>${LabelEnableSingleImageInDidlLimit}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LabelEnableSingleImageInDidlLimitHelp}</div>
</div>
<div class="inputContainer">
<input type="text" is="emby-input" id="txtAlbumArtPn" label="${LabelAlbumArtPN}" />
<div class="fieldDescription">${LabelAlbumArtHelp}</div>
</div>
<div class="inputContainer">
<input type="number" is="emby-input" id="txtAlbumArtMaxWidth" pattern="[0-9]*" min="1" label="${LabelAlbumArtMaxWidth}" />
<div class="fieldDescription">${LabelAlbumArtMaxResHelp}</div>
</div>
<div class="inputContainer">
<input type="number" is="emby-input" id="txtAlbumArtMaxHeight" pattern="[0-9]*" min="1" label="${LabelAlbumArtMaxHeight}" />
<div class="fieldDescription">${LabelAlbumArtMaxResHelp}</div>
</div>
<div class="inputContainer">
<input type="number" is="emby-input" id="txtIconMaxWidth" pattern="[0-9]*" min="1" label="${LabelIconMaxWidth}" />
<div class="fieldDescription">${LabelIconMaxResHelp}</div>
</div>
<div class="inputContainer">
<input type="number" is="emby-input" id="txtIconMaxHeight" pattern="[0-9]*" min="1" label="${LabelIconMaxHeight}" />
<div class="fieldDescription">${LabelIconMaxResHelp}</div>
</div>
</div>
</div>
<div is="emby-collapse" title="${HeaderServerSettings}">
<div class="collapseContent">
<p>${HeaderProfileServerSettingsHelp}</p>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtInfoFriendlyName" label="${LabelFriendlyName}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtInfoManufacturer" label="${LabelManufacturer}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtInfoManufacturerUrl" label="${LabelManufacturerUrl}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtInfoModelName" label="${LabelModelName}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtInfoModelNumber" label="${LabelModelNumber}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtInfoModelDesription" label="${LabelModelDescription}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtInfoModelUrl" label="${LabelModelUrl}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtInfoSerialNumber" label="${LabelSerialNumber}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtProtocolInfo" label="${LabelProtocolInfo}" />
<div class="fieldDescription">${LabelProtocolInfoHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtXDlnaCap" label="${LabelXDlnaCap}" />
<div class="fieldDescription">${LabelXDlnaCapHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtXDlnaDoc" label="${LabelXDlnaDoc}" />
<div class="fieldDescription">${LabelXDlnaDocHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtSonyAggregationFlags" label="${LabelSonyAggregationFlags}" />
<div class="fieldDescription">${LabelSonyAggregationFlagsHelp}</div>
</div>
</div>
</div>
<div is="emby-collapse" title="${HeaderSubtitleProfiles}">
<div class="collapseContent">
<p>${HeaderSubtitleProfilesHelp}</p>
<button is="emby-button" type="button" class="raised submit block btnAddSubtitleProfile">
<span>${Add}</span>
</button>
<div class="subtitleProfileList"></div>
<br />
</div>
</div>
<div is="emby-collapse" title="${HeaderXmlSettings}">
<div class="collapseContent">
<div>
<h2 style="vertical-align:middle;display:inline-block;">${HeaderXmlDocumentAttributes}</h2>
<button is="emby-button" type="button" class="fab btnAddXmlDocumentAttribute submit sectionTitleButton" style="margin-left:1em;" title="${Add}">
<span class="material-icons add" aria-hidden="true"></span>
</button>
</div>
<div class="xmlDocumentAttributeList"></div>
<div class="fieldDescription">${XmlDocumentAttributeListHelp}</div>
<br />
</div>
</div>
</div>
<div class="tabContent tabDirectPlayProfiles">
<p>${HeaderDirectPlayProfileHelp}</p>
<button is="emby-button" class="raised submit block btnAddDirectPlayProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
<br />
<div class="directPlayProfiles"></div>
</div>
<div class="tabContent tabTranscodingProfiles">
<p>${HeaderTranscodingProfileHelp}</p>
<button is="emby-button" class="raised submit block btnAddTranscodingProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
<br />
<div class="transcodingProfiles"></div>
</div>
<div class="tabContent tabContainerProfiles">
<p>${HeaderContainerProfileHelp}</p>
<button is="emby-button" class="raised submit block btnAddContainerProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
<br />
<div class="containerProfiles"></div>
</div>
<div class="tabContent tabCodecProfiles">
<p>${HeaderCodecProfileHelp}</p>
<button is="emby-button" class="raised submit block btnAddCodecProfile" type="button" data-icon="plus">${New}</button>
<br />
<div class="codecProfiles"></div>
</div>
<div class="tabContent tabMediaProfiles">
<p>${HeaderResponseProfileHelp}</p>
<button is="emby-button" class="raised submit block btnAddResponseProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
<br />
<div class="mediaProfiles"></div>
</div>
<br />
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
<button is="emby-button" type="button" class="button-cancel raised block" onclick="Dashboard.navigate('dashboard/dlna/profiles');">
<span>${ButtonCancel}</span>
</button>
</div>
</form>
</div>
</div>
<div data-role="popup" id="popupEditDirectPlayProfile" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
<form class="editDirectPlayProfileForm" style="padding:1em;">
<div class="ui-bar-a">
<h3 class="sectionTitle">${HeaderDirectPlayProfile}</h3>
</div>
<div data-role="content">
<div class="selectContainer">
<select id="selectDirectPlayProfileType" name="selectDirectPlayProfileType" is="emby-select" label="${LabelType}">
<option value="Audio">${Audio}</option>
<option value="Photo">${Photo}</option>
<option value="Video">${Video}</option>
</select>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtDirectPlayContainer" label="${LabelProfileContainer}" />
<div class="fieldDescription">${LabelProfileContainersHelp}</div>
</div>
<div id="fldDirectPlayVideoCodec" style="margin: 1em 0;">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtDirectPlayVideoCodec" label="${LabelProfileVideoCodecs}" />
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
</div>
</div>
<div id="fldDirectPlayAudioCodec" style="margin: 1em 0 2em;">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtDirectPlayAudioCodec" label="${LabelProfileAudioCodecs}" />
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
</div>
</div>
<p>
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
<span>${ButtonOk}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
<span>${ButtonCancel}</span>
</button>
</p>
</div>
</form>
</div>
<div data-role="popup" id="transcodingProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
<form class="transcodingProfileForm" style="padding:1em;">
<div class="ui-bar-a">
<h3 class="sectionTitle">${HeaderTranscodingProfile}</h3>
</div>
<div data-role="content">
<div data-role="controlgroup" data-type="horizontal" data-mini="true">
<input type="radio" name="radioTranscodingTab" class="radioTabButton" id="radioTranscodingBasics" value="tabTranscodingBasics">
<label for="radioTranscodingBasics">${ButtonInfo}</label>
<input type="radio" name="radioTranscodingTab" class="radioTabButton" id="radioTranscodingAdvanced" value="tabTranscodingAdvanced">
<label for="radioTranscodingAdvanced">${TabAdvanced}</label>
</div>
<br />
<div class="tabContent tabTranscodingBasics" style="display: none;">
<div class="selectContainer">
<select id="selectTranscodingProfileType" name="selectTranscodingProfileType" is="emby-select" label="${LabelType}">
<option value="Audio">${Audio}</option>
<option value="Photo">${Photo}</option>
<option value="Video">${Video}</option>
</select>
</div>
<div id="fldTranscodingProtocol" style="margin: 1em 0;">
<div class="selectContainer">
<select id="selectTranscodingProtocol" name="selectTranscodingProtocol" is="emby-select" label="${LabelProtocol}">
<option value="Http">${OptionProtocolHttp}</option>
<option value="Hls">${OptionProtocolHls}</option>
</select>
</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtTranscodingContainer" label="${LabelProfileContainer}"; required="required" />
</div>
<div id="fldTranscodingVideoCodec" style="margin: 1em 0;">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtTranscodingVideoCodec" label="${LabelVideoCodec}" />
</div>
</div>
<div id="fldTranscodingAudioCodec" style="margin: 1em 0;">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtTranscodingAudioCodec" label="${LabelAudioCodec}" />
</div>
</div>
</div>
<div class="tabContent tabTranscodingAdvanced" style="display: none;">
<div id="fldEnableMpegtsM2TsMode" style="margin: 1em 0;">
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="chkEnableMpegtsM2TsMode" />
<span>${OptionEnableM2tsMode}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${OptionEnableM2tsModeHelp}</div>
</div>
</div>
<div id="fldEstimateContentLength" style="margin: 1em 0;">
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="chkEstimateContentLength" />
<span>${OptionEstimateContentLength}</span>
</label>
</div>
</div>
<div id="fldReportByteRangeRequests" style="margin: 1em 0;">
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="chkReportByteRangeRequests" />
<span>${OptionReportByteRangeSeekingWhenTranscoding}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${OptionReportByteRangeSeekingWhenTranscodingHelp}</div>
</div>
</div>
</div>
<p>
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
<span>${ButtonOk}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
<span>${ButtonCancel}</span>
</button>
</p>
</div>
</form>
</div>
<div data-role="popup" id="containerProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
<form class="containerProfileForm" style="padding:1em;">
<div class="ui-bar-a">
<h3 class="sectionTitle">${HeaderContainerProfile}</h3>
</div>
<div data-role="content">
<p>${HeaderContainerProfileHelp}</p>
<div class="tabContent tabContainerBasics">
<div class="selectContainer">
<select id="selectContainerProfileType" name="selectContainerProfileType" is="emby-select" label="${LabelType}">
<option value="Audio">${Audio}</option>
<option value="Photo">${Photo}</option>
<option value="Video">${Video}</option>
</select>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtContainerProfileContainer" label="${LabelProfileContainer}" />
<div class="fieldDescription">${LabelProfileContainersHelp}</div>
</div>
</div>
<div class="tabContent tabContainerConditions" style="display: none;"></div>
<p>
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
<span>${ButtonOk}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
<span>${ButtonCancel}</span>
</button>
</p>
</div>
</form>
</div>
<div data-role="popup" id="codecProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
<form class="codecProfileForm" style="padding:1em;">
<div class="ui-bar-a">
<h3 class="sectionTitle">${HeaderCodecProfile}</h3>
</div>
<div data-role="content">
<p>${HeaderCodecProfileHelp}</p>
<div class="selectContainer">
<select id="selectCodecProfileType" name="selectCodecProfileType" is="emby-select" label="${LabelType}">
<option value="Video">${Video}</option>
<option value="VideoAudio">${VideoAudio}</option>
<option value="Audio">${Audio}</option>
</select>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtCodecProfileCodec" label="${LabelProfileCodecs}" />
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
</div>
<p>
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
<span>${ButtonOk}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
<span>${ButtonCancel}</span>
</button>
</p>
</div>
</form>
</div>
<div data-role="popup" id="responseProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
<form class="editResponseProfileForm" style="padding:1em;">
<div class="ui-bar-a">
<h3 class="sectionTitle">${HeaderResponseProfile}</h3>
</div>
<div data-role="content">
<div class="selectContainer">
<select id="selectResponseProfileType" name="selectResponseProfileType" is="emby-select" label="${LabelType}">
<option value="Audio">${Audio}</option>
<option value="Photo">${Photo}</option>
<option value="Video">${Video}</option>
</select>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtResponseProfileContainer" label="${LabelProfileContainer}" />
<div class="fieldDescription">${LabelProfileContainersHelp}</div>
</div>
<div id="fldResponseProfileVideoCodec" style="margin: 1em 0;">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtResponseProfileVideoCodec" label="${LabelProfileVideoCodecs}" />
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
</div>
</div>
<div id="fldResponseProfileAudioCodec" style="margin: 1em 0 2em;">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtResponseProfileAudioCodec" label="${LabelProfileAudioCodecs}" />
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
</div>
</div>
<p>
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
<span>${ButtonOk}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
<span>${ButtonCancel}</span>
</button>
</p>
</div>
</form>
</div>
<div data-role="popup" id="identificationHeaderPopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
<form class="identificationHeaderForm" style="padding:1em;">
<div class="ui-bar-a">
<h3 class="sectionTitle">${HeaderIdentificationHeader}</h3>
</div>
<div data-role="content">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdentificationHeaderName" label="${LabelName}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtIdentificationHeaderValue" label="${LabelValue}" />
</div>
<div class="selectContainer">
<select id="selectMatchType" name="selectMatchType" is="emby-select" label="${LabelMatchType}">
<option value="Equals">${OptionEquals}</option>
<option value="Regex">${OptionRegex}</option>
<option value="Substring">${OptionSubstring}</option>
</select>
</div>
<p>
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
<span>${ButtonOk}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
<span>${ButtonCancel}</span>
</button>
</p>
</div>
</form>
</div>
<div data-role="popup" id="xmlAttributePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
<form class="xmlAttributeForm" style="padding:1em;">
<div class="ui-bar-a">
<h3 class="sectionTitle">${HeaderXmlDocumentAttribute}</h3>
</div>
<div data-role="content">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtXmlAttributeName" label="${LabelName}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtXmlAttributeValue" label="${LabelValue}" />
</div>
<p>
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
<span>${ButtonOk}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
<span>${ButtonCancel}</span>
</button>
</p>
</div>
</form>
</div>
<div data-role="popup" id="subtitleProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
<form class="subtitleProfileForm" style="padding:1em;">
<div class="ui-bar-a">
<h3 class="sectionTitle">${HeaderSubtitleProfile}</h3>
</div>
<div data-role="content">
<div class="inputContainer">
<input is="emby-input" type="text" id="txtSubtitleProfileFormat" label="${LabelFormat}" />
<div class="fieldDescription">${LabelSubtitleFormatHelp}</div>
</div>
<div class="selectContainer">
<select id="selectSubtitleProfileMethod" name="selectSubtitleProfileMethod" is="emby-select" label="${LabelMethod}">
<option value="Embed">${OptionEmbedSubtitles}</option>
<option value="External">${OptionExternallyDownloaded}</option>
<option value="Hls">${OptionHlsSegmentedSubtitles}</option>
</select>
</div>
<div class="selectContainer">
<select id="selectSubtitleProfileDidlMode" name="selectSubtitleProfileDidlMode" is="emby-select" label="${LabelDidlMode}">
<option value="">${OptionResElement}</option>
<option value="CaptionInfoEx">${OptionCaptionInfoExSamsung}</option>
</select>
</div>
<p>
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
<span>${ButtonOk}</span>
</button>
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
<span>${ButtonCancel}</span>
</button>
</p>
</div>
</form>
</div>
</div>

View file

@ -1,830 +0,0 @@
import escapeHtml from 'escape-html';
import 'jquery';
import loading from '../../../components/loading/loading';
import globalize from '../../../scripts/globalize';
import '../../../elements/emby-select/emby-select';
import '../../../elements/emby-button/emby-button';
import '../../../elements/emby-input/emby-input';
import '../../../elements/emby-checkbox/emby-checkbox';
import '../../../components/listview/listview.scss';
import Dashboard from '../../../utils/dashboard';
import toast from '../../../components/toast/toast';
import { getParameterByName } from '../../../utils/url.ts';
function loadProfile(page) {
loading.show();
const promise1 = getProfile();
const promise2 = ApiClient.getUsers();
Promise.all([promise1, promise2]).then(function (responses) {
currentProfile = responses[0];
renderProfile(page, currentProfile, responses[1]);
loading.hide();
});
}
function getProfile() {
const id = getParameterByName('id');
const url = id ? 'Dlna/Profiles/' + id : 'Dlna/Profiles/Default';
return ApiClient.getJSON(ApiClient.getUrl(url));
}
function renderProfile(page, profile, users) {
$('#txtName', page).val(profile.Name);
$('.chkMediaType', page).each(function () {
this.checked = (profile.SupportedMediaTypes || '').split(',').indexOf(this.getAttribute('data-value')) != -1;
});
$('#chkEnableAlbumArtInDidl', page).prop('checked', profile.EnableAlbumArtInDidl);
$('#chkEnableSingleImageLimit', page).prop('checked', profile.EnableSingleAlbumArtLimit);
renderXmlDocumentAttributes(page, profile.XmlRootAttributes || []);
const idInfo = profile.Identification || {};
renderIdentificationHeaders(page, idInfo.Headers || []);
renderSubtitleProfiles(page, profile.SubtitleProfiles || []);
$('#txtInfoFriendlyName', page).val(profile.FriendlyName || '');
$('#txtInfoModelName', page).val(profile.ModelName || '');
$('#txtInfoModelNumber', page).val(profile.ModelNumber || '');
$('#txtInfoModelDescription', page).val(profile.ModelDescription || '');
$('#txtInfoModelUrl', page).val(profile.ModelUrl || '');
$('#txtInfoManufacturer', page).val(profile.Manufacturer || '');
$('#txtInfoManufacturerUrl', page).val(profile.ManufacturerUrl || '');
$('#txtInfoSerialNumber', page).val(profile.SerialNumber || '');
$('#txtIdFriendlyName', page).val(idInfo.FriendlyName || '');
$('#txtIdModelName', page).val(idInfo.ModelName || '');
$('#txtIdModelNumber', page).val(idInfo.ModelNumber || '');
$('#txtIdModelDescription', page).val(idInfo.ModelDescription || '');
$('#txtIdModelUrl', page).val(idInfo.ModelUrl || '');
$('#txtIdManufacturer', page).val(idInfo.Manufacturer || '');
$('#txtIdManufacturerUrl', page).val(idInfo.ManufacturerUrl || '');
$('#txtIdSerialNumber', page).val(idInfo.SerialNumber || '');
$('#txtIdDeviceDescription', page).val(idInfo.DeviceDescription || '');
$('#txtAlbumArtPn', page).val(profile.AlbumArtPn || '');
$('#txtAlbumArtMaxWidth', page).val(profile.MaxAlbumArtWidth || '');
$('#txtAlbumArtMaxHeight', page).val(profile.MaxAlbumArtHeight || '');
$('#txtIconMaxWidth', page).val(profile.MaxIconWidth || '');
$('#txtIconMaxHeight', page).val(profile.MaxIconHeight || '');
$('#chkIgnoreTranscodeByteRangeRequests', page).prop('checked', profile.IgnoreTranscodeByteRangeRequests);
$('#txtMaxAllowedBitrate', page).val(profile.MaxStreamingBitrate || '');
$('#txtMusicStreamingTranscodingBitrate', page).val(profile.MusicStreamingTranscodingBitrate || '');
$('#chkRequiresPlainFolders', page).prop('checked', profile.RequiresPlainFolders);
$('#chkRequiresPlainVideoItems', page).prop('checked', profile.RequiresPlainVideoItems);
$('#txtProtocolInfo', page).val(profile.ProtocolInfo || '');
$('#txtXDlnaCap', page).val(profile.XDlnaCap || '');
$('#txtXDlnaDoc', page).val(profile.XDlnaDoc || '');
$('#txtSonyAggregationFlags', page).val(profile.SonyAggregationFlags || '');
profile.DirectPlayProfiles = profile.DirectPlayProfiles || [];
profile.TranscodingProfiles = profile.TranscodingProfiles || [];
profile.ContainerProfiles = profile.ContainerProfiles || [];
profile.CodecProfiles = profile.CodecProfiles || [];
profile.ResponseProfiles = profile.ResponseProfiles || [];
const usersHtml = '<option></option>' + users.map(function (u) {
return '<option value="' + u.Id + '">' + escapeHtml(u.Name) + '</option>';
}).join('');
$('#selectUser', page).html(usersHtml).val(profile.UserId || '');
renderSubProfiles(page, profile);
}
function renderIdentificationHeaders(page, headers) {
let index = 0;
const html = '<div class="paperList">' + headers.map(function (h) {
let li = '<div class="listItem">';
li += '<span class="material-icons listItemIcon info" aria-hidden="true"></span>';
li += '<div class="listItemBody">';
li += '<h3 class="listItemBodyText">' + escapeHtml(h.Name + ': ' + (h.Value || '')) + '</h3>';
li += '<div class="listItemBodyText secondary">' + escapeHtml(h.Match || '') + '</div>';
li += '</div>';
li += '<button type="button" is="paper-icon-button-light" class="btnDeleteIdentificationHeader listItemButton" data-index="' + index + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
li += '</div>';
index++;
return li;
}).join('') + '</div>';
const elem = $('.httpHeaderIdentificationList', page).html(html).trigger('create');
$('.btnDeleteIdentificationHeader', elem).on('click', function () {
const itemIndex = parseInt(this.getAttribute('data-index'), 10);
currentProfile.Identification.Headers.splice(itemIndex, 1);
renderIdentificationHeaders(page, currentProfile.Identification.Headers);
});
}
function openPopup(elem) {
elem.classList.remove('hide');
}
function closePopup(elem) {
elem.classList.add('hide');
}
function editIdentificationHeader(page, header) {
isSubProfileNew = header == null;
header = header || {};
currentSubProfile = header;
const popup = $('#identificationHeaderPopup', page);
$('#txtIdentificationHeaderName', popup).val(header.Name || '');
$('#txtIdentificationHeaderValue', popup).val(header.Value || '');
$('#selectMatchType', popup).val(header.Match || 'Equals');
openPopup(popup[0]);
}
function saveIdentificationHeader(page) {
currentSubProfile.Name = $('#txtIdentificationHeaderName', page).val();
currentSubProfile.Value = $('#txtIdentificationHeaderValue', page).val();
currentSubProfile.Match = $('#selectMatchType', page).val();
if (isSubProfileNew) {
currentProfile.Identification = currentProfile.Identification || {};
currentProfile.Identification.Headers = currentProfile.Identification.Headers || [];
currentProfile.Identification.Headers.push(currentSubProfile);
}
renderIdentificationHeaders(page, currentProfile.Identification.Headers);
currentSubProfile = null;
closePopup($('#identificationHeaderPopup', page)[0]);
}
function renderXmlDocumentAttributes(page, attribute) {
const html = '<div class="paperList">' + attribute.map(function (h) {
let li = '<div class="listItem">';
li += '<span class="material-icons listItemIcon info" aria-hidden="true"></span>';
li += '<div class="listItemBody">';
li += '<h3 class="listItemBodyText">' + escapeHtml(h.Name + ' = ' + (h.Value || '')) + '</h3>';
li += '</div>';
li += '<button type="button" is="paper-icon-button-light" class="btnDeleteXmlAttribute listItemButton" data-index="0"><span class="material-icons delete" aria-hidden="true"></span></button>';
li += '</div>';
return li;
}).join('') + '</div>';
const elem = $('.xmlDocumentAttributeList', page).html(html).trigger('create');
$('.btnDeleteXmlAttribute', elem).on('click', function () {
const itemIndex = parseInt(this.getAttribute('data-index'), 10);
currentProfile.XmlRootAttributes.splice(itemIndex, 1);
renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes);
});
}
function editXmlDocumentAttribute(page, attribute) {
isSubProfileNew = attribute == null;
attribute = attribute || {};
currentSubProfile = attribute;
const popup = $('#xmlAttributePopup', page);
$('#txtXmlAttributeName', popup).val(attribute.Name || '');
$('#txtXmlAttributeValue', popup).val(attribute.Value || '');
openPopup(popup[0]);
}
function saveXmlDocumentAttribute(page) {
currentSubProfile.Name = $('#txtXmlAttributeName', page).val();
currentSubProfile.Value = $('#txtXmlAttributeValue', page).val();
if (isSubProfileNew) {
currentProfile.XmlRootAttributes.push(currentSubProfile);
}
renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes);
currentSubProfile = null;
closePopup($('#xmlAttributePopup', page)[0]);
}
function renderSubtitleProfiles(page, profiles) {
let index = 0;
const html = '<div class="paperList">' + profiles.map(function (h) {
let li = '<div class="listItem lnkEditSubProfile" data-index="' + index + '">';
li += '<span class="material-icons listItemIcon info" aria-hidden="true"></span>';
li += '<div class="listItemBody">';
li += '<h3 class="listItemBodyText">' + escapeHtml(h.Format || '') + '</h3>';
li += '</div>';
li += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-index="' + index + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
li += '</div>';
index++;
return li;
}).join('') + '</div>';
const elem = $('.subtitleProfileList', page).html(html).trigger('create');
$('.btnDeleteProfile', elem).on('click', function () {
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'), 10);
editSubtitleProfile(page, currentProfile.SubtitleProfiles[itemIndex]);
});
}
function editSubtitleProfile(page, profile) {
isSubProfileNew = profile == null;
profile = profile || {};
currentSubProfile = profile;
const popup = $('#subtitleProfilePopup', page);
$('#txtSubtitleProfileFormat', popup).val(profile.Format || '');
$('#selectSubtitleProfileMethod', popup).val(profile.Method || '');
$('#selectSubtitleProfileDidlMode', popup).val(profile.DidlMode || '');
openPopup(popup[0]);
}
function saveSubtitleProfile(page) {
currentSubProfile.Format = $('#txtSubtitleProfileFormat', page).val();
currentSubProfile.Method = $('#selectSubtitleProfileMethod', page).val();
currentSubProfile.DidlMode = $('#selectSubtitleProfileDidlMode', page).val();
if (isSubProfileNew) {
currentProfile.SubtitleProfiles.push(currentSubProfile);
}
renderSubtitleProfiles(page, currentProfile.SubtitleProfiles);
currentSubProfile = null;
closePopup($('#subtitleProfilePopup', page)[0]);
}
function renderSubProfiles(page, profile) {
renderDirectPlayProfiles(page, profile.DirectPlayProfiles);
renderTranscodingProfiles(page, profile.TranscodingProfiles);
renderContainerProfiles(page, profile.ContainerProfiles);
renderCodecProfiles(page, profile.CodecProfiles);
renderResponseProfiles(page, profile.ResponseProfiles);
}
function saveDirectPlayProfile(page) {
currentSubProfile.Type = $('#selectDirectPlayProfileType', page).val();
currentSubProfile.Container = $('#txtDirectPlayContainer', page).val();
currentSubProfile.AudioCodec = $('#txtDirectPlayAudioCodec', page).val();
currentSubProfile.VideoCodec = $('#txtDirectPlayVideoCodec', page).val();
if (isSubProfileNew) {
currentProfile.DirectPlayProfiles.push(currentSubProfile);
}
renderSubProfiles(page, currentProfile);
currentSubProfile = null;
closePopup($('#popupEditDirectPlayProfile', page)[0]);
}
function renderDirectPlayProfiles(page, profiles) {
let html = '';
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
let currentType;
for (const [index, profile] of profiles.entries()) {
if (profile.Type !== currentType) {
html += '<li data-role="list-divider">' + profile.Type + '</li>';
currentType = profile.Type;
}
html += '<div>';
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + index + '">';
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
if (profile.Type == 'Video') {
html += '<p>' + globalize.translate('ValueVideoCodec', profile.VideoCodec || allText) + '</p>';
html += '<p>' + globalize.translate('ValueAudioCodec', profile.AudioCodec || allText) + '</p>';
} else if (profile.Type == 'Audio') {
html += '<p>' + globalize.translate('ValueCodec', profile.AudioCodec || allText) + '</p>';
}
html += '</a>';
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + index + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
html += '</div>';
}
html += '</ul>';
const elem = $('.directPlayProfiles', page).html(html).trigger('create');
$('.btnDeleteProfile', elem).on('click', function () {
const index = this.getAttribute('data-profileindex');
deleteDirectPlayProfile(page, index);
});
$('.lnkEditSubProfile', elem).on('click', function () {
const index = parseInt(this.getAttribute('data-profileindex'), 10);
editDirectPlayProfile(page, currentProfile.DirectPlayProfiles[index]);
});
}
function deleteDirectPlayProfile(page, index) {
currentProfile.DirectPlayProfiles.splice(index, 1);
renderDirectPlayProfiles(page, currentProfile.DirectPlayProfiles);
}
function editDirectPlayProfile(page, directPlayProfile) {
isSubProfileNew = directPlayProfile == null;
directPlayProfile = directPlayProfile || {};
currentSubProfile = directPlayProfile;
const popup = $('#popupEditDirectPlayProfile', page);
$('#selectDirectPlayProfileType', popup).val(directPlayProfile.Type || 'Video').trigger('change');
$('#txtDirectPlayContainer', popup).val(directPlayProfile.Container || '');
$('#txtDirectPlayAudioCodec', popup).val(directPlayProfile.AudioCodec || '');
$('#txtDirectPlayVideoCodec', popup).val(directPlayProfile.VideoCodec || '');
openPopup(popup[0]);
}
function renderTranscodingProfiles(page, profiles) {
let html = '';
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
let currentType;
for (let i = 0, length = profiles.length; i < length; i++) {
const profile = profiles[i];
if (profile.Type !== currentType) {
html += '<li data-role="list-divider">' + profile.Type + '</li>';
currentType = profile.Type;
}
html += '<div>';
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
html += '<p>Protocol: ' + (profile.Protocol || 'Http') + '</p>';
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
if (profile.Type == 'Video') {
html += '<p>' + globalize.translate('ValueVideoCodec', profile.VideoCodec || allText) + '</p>';
html += '<p>' + globalize.translate('ValueAudioCodec', profile.AudioCodec || allText) + '</p>';
} else if (profile.Type == 'Audio') {
html += '<p>' + globalize.translate('ValueCodec', profile.AudioCodec || allText) + '</p>';
}
html += '</a>';
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + i + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
html += '</div>';
}
html += '</ul>';
const elem = $('.transcodingProfiles', page).html(html).trigger('create');
$('.btnDeleteProfile', elem).on('click', function () {
const index = this.getAttribute('data-profileindex');
deleteTranscodingProfile(page, index);
});
$('.lnkEditSubProfile', elem).on('click', function () {
const index = parseInt(this.getAttribute('data-profileindex'), 10);
editTranscodingProfile(page, currentProfile.TranscodingProfiles[index]);
});
}
function editTranscodingProfile(page, transcodingProfile) {
isSubProfileNew = transcodingProfile == null;
transcodingProfile = transcodingProfile || {};
currentSubProfile = transcodingProfile;
const popup = $('#transcodingProfilePopup', page);
$('#selectTranscodingProfileType', popup).val(transcodingProfile.Type || 'Video').trigger('change');
$('#txtTranscodingContainer', popup).val(transcodingProfile.Container || '');
$('#txtTranscodingAudioCodec', popup).val(transcodingProfile.AudioCodec || '');
$('#txtTranscodingVideoCodec', popup).val(transcodingProfile.VideoCodec || '');
$('#selectTranscodingProtocol', popup).val(transcodingProfile.Protocol || 'Http');
$('#chkEnableMpegtsM2TsMode', popup).prop('checked', transcodingProfile.EnableMpegtsM2TsMode || false);
$('#chkEstimateContentLength', popup).prop('checked', transcodingProfile.EstimateContentLength || false);
$('#chkReportByteRangeRequests', popup).prop('checked', transcodingProfile.TranscodeSeekInfo == 'Bytes');
$('.radioTabButton:first', popup).trigger('click');
openPopup(popup[0]);
}
function deleteTranscodingProfile(page, index) {
currentProfile.TranscodingProfiles.splice(index, 1);
renderTranscodingProfiles(page, currentProfile.TranscodingProfiles);
}
function saveTranscodingProfile(page) {
currentSubProfile.Type = $('#selectTranscodingProfileType', page).val();
currentSubProfile.Container = $('#txtTranscodingContainer', page).val();
currentSubProfile.AudioCodec = $('#txtTranscodingAudioCodec', page).val();
currentSubProfile.VideoCodec = $('#txtTranscodingVideoCodec', page).val();
currentSubProfile.Protocol = $('#selectTranscodingProtocol', page).val();
currentSubProfile.Context = 'Streaming';
currentSubProfile.EnableMpegtsM2TsMode = $('#chkEnableMpegtsM2TsMode', page).is(':checked');
currentSubProfile.EstimateContentLength = $('#chkEstimateContentLength', page).is(':checked');
currentSubProfile.TranscodeSeekInfo = $('#chkReportByteRangeRequests', page).is(':checked') ? 'Bytes' : 'Auto';
if (isSubProfileNew) {
currentProfile.TranscodingProfiles.push(currentSubProfile);
}
renderSubProfiles(page, currentProfile);
currentSubProfile = null;
closePopup($('#transcodingProfilePopup', page)[0]);
}
function renderContainerProfiles(page, profiles) {
let html = '';
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
let currentType;
for (let i = 0, length = profiles.length; i < length; i++) {
const profile = profiles[i];
if (profile.Type !== currentType) {
html += '<li data-role="list-divider">' + profile.Type + '</li>';
currentType = profile.Type;
}
html += '<div>';
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
if (profile.Conditions?.length) {
html += '<p>';
html += globalize.translate('ValueConditions', profile.Conditions.map(function (c) {
return c.Property;
}).join(', '));
html += '</p>';
}
html += '</a>';
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + i + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
html += '</div>';
}
html += '</ul>';
const elem = $('.containerProfiles', page).html(html).trigger('create');
$('.btnDeleteProfile', elem).on('click', function () {
const index = this.getAttribute('data-profileindex');
deleteContainerProfile(page, index);
});
$('.lnkEditSubProfile', elem).on('click', function () {
const index = parseInt(this.getAttribute('data-profileindex'), 10);
editContainerProfile(page, currentProfile.ContainerProfiles[index]);
});
}
function deleteContainerProfile(page, index) {
currentProfile.ContainerProfiles.splice(index, 1);
renderContainerProfiles(page, currentProfile.ContainerProfiles);
}
function editContainerProfile(page, containerProfile) {
isSubProfileNew = containerProfile == null;
containerProfile = containerProfile || {};
currentSubProfile = containerProfile;
const popup = $('#containerProfilePopup', page);
$('#selectContainerProfileType', popup).val(containerProfile.Type || 'Video').trigger('change');
$('#txtContainerProfileContainer', popup).val(containerProfile.Container || '');
$('.radioTabButton:first', popup).trigger('click');
openPopup(popup[0]);
}
function saveContainerProfile(page) {
currentSubProfile.Type = $('#selectContainerProfileType', page).val();
currentSubProfile.Container = $('#txtContainerProfileContainer', page).val();
if (isSubProfileNew) {
currentProfile.ContainerProfiles.push(currentSubProfile);
}
renderSubProfiles(page, currentProfile);
currentSubProfile = null;
closePopup($('#containerProfilePopup', page)[0]);
}
function renderCodecProfiles(page, profiles) {
let html = '';
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
let currentType;
for (let i = 0, length = profiles.length; i < length; i++) {
const profile = profiles[i];
const type = profile.Type.replace('VideoAudio', 'Video Audio');
if (type !== currentType) {
html += '<li data-role="list-divider">' + type + '</li>';
currentType = type;
}
html += '<div>';
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
html += '<p>' + globalize.translate('ValueCodec', profile.Codec || allText) + '</p>';
if (profile.Conditions?.length) {
html += '<p>';
html += globalize.translate('ValueConditions', profile.Conditions.map(function (c) {
return c.Property;
}).join(', '));
html += '</p>';
}
html += '</a>';
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + i + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
html += '</div>';
}
html += '</ul>';
const elem = $('.codecProfiles', page).html(html).trigger('create');
$('.btnDeleteProfile', elem).on('click', function () {
const index = this.getAttribute('data-profileindex');
deleteCodecProfile(page, index);
});
$('.lnkEditSubProfile', elem).on('click', function () {
const index = parseInt(this.getAttribute('data-profileindex'), 10);
editCodecProfile(page, currentProfile.CodecProfiles[index]);
});
}
function deleteCodecProfile(page, index) {
currentProfile.CodecProfiles.splice(index, 1);
renderCodecProfiles(page, currentProfile.CodecProfiles);
}
function editCodecProfile(page, codecProfile) {
isSubProfileNew = codecProfile == null;
codecProfile = codecProfile || {};
currentSubProfile = codecProfile;
const popup = $('#codecProfilePopup', page);
$('#selectCodecProfileType', popup).val(codecProfile.Type || 'Video').trigger('change');
$('#txtCodecProfileCodec', popup).val(codecProfile.Codec || '');
$('.radioTabButton:first', popup).trigger('click');
openPopup(popup[0]);
}
function saveCodecProfile(page) {
currentSubProfile.Type = $('#selectCodecProfileType', page).val();
currentSubProfile.Codec = $('#txtCodecProfileCodec', page).val();
if (isSubProfileNew) {
currentProfile.CodecProfiles.push(currentSubProfile);
}
renderSubProfiles(page, currentProfile);
currentSubProfile = null;
closePopup($('#codecProfilePopup', page)[0]);
}
function renderResponseProfiles(page, profiles) {
let html = '';
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
let currentType;
for (let i = 0, length = profiles.length; i < length; i++) {
const profile = profiles[i];
if (profile.Type !== currentType) {
html += '<li data-role="list-divider">' + profile.Type + '</li>';
currentType = profile.Type;
}
html += '<div>';
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
if (profile.Type == 'Video') {
html += '<p>' + globalize.translate('ValueVideoCodec', profile.VideoCodec || allText) + '</p>';
html += '<p>' + globalize.translate('ValueAudioCodec', profile.AudioCodec || allText) + '</p>';
} else if (profile.Type == 'Audio') {
html += '<p>' + globalize.translate('ValueCodec', profile.AudioCodec || allText) + '</p>';
}
if (profile.Conditions?.length) {
html += '<p>';
html += globalize.translate('ValueConditions', profile.Conditions.map(function (c) {
return c.Property;
}).join(', '));
html += '</p>';
}
html += '</a>';
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + i + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
html += '</div>';
}
html += '</ul>';
const elem = $('.mediaProfiles', page).html(html).trigger('create');
$('.btnDeleteProfile', elem).on('click', function () {
const index = this.getAttribute('data-profileindex');
deleteResponseProfile(page, index);
});
$('.lnkEditSubProfile', elem).on('click', function () {
const index = parseInt(this.getAttribute('data-profileindex'), 10);
editResponseProfile(page, currentProfile.ResponseProfiles[index]);
});
}
function deleteResponseProfile(page, index) {
currentProfile.ResponseProfiles.splice(index, 1);
renderResponseProfiles(page, currentProfile.ResponseProfiles);
}
function editResponseProfile(page, responseProfile) {
isSubProfileNew = responseProfile == null;
responseProfile = responseProfile || {};
currentSubProfile = responseProfile;
const popup = $('#responseProfilePopup', page);
$('#selectResponseProfileType', popup).val(responseProfile.Type || 'Video').trigger('change');
$('#txtResponseProfileContainer', popup).val(responseProfile.Container || '');
$('#txtResponseProfileAudioCodec', popup).val(responseProfile.AudioCodec || '');
$('#txtResponseProfileVideoCodec', popup).val(responseProfile.VideoCodec || '');
$('.radioTabButton:first', popup).trigger('click');
openPopup(popup[0]);
}
function saveResponseProfile(page) {
currentSubProfile.Type = $('#selectResponseProfileType', page).val();
currentSubProfile.Container = $('#txtResponseProfileContainer', page).val();
currentSubProfile.AudioCodec = $('#txtResponseProfileAudioCodec', page).val();
currentSubProfile.VideoCodec = $('#txtResponseProfileVideoCodec', page).val();
if (isSubProfileNew) {
currentProfile.ResponseProfiles.push(currentSubProfile);
}
renderSubProfiles(page, currentProfile);
currentSubProfile = null;
closePopup($('#responseProfilePopup', page)[0]);
}
function saveProfile(page, profile) {
updateProfile(page, profile);
const id = getParameterByName('id');
if (id) {
ApiClient.ajax({
type: 'POST',
url: ApiClient.getUrl('Dlna/Profiles/' + id),
data: JSON.stringify(profile),
contentType: 'application/json'
}).then(function () {
toast(globalize.translate('SettingsSaved'));
}, Dashboard.processErrorResponse);
} else {
ApiClient.ajax({
type: 'POST',
url: ApiClient.getUrl('Dlna/Profiles'),
data: JSON.stringify(profile),
contentType: 'application/json'
}).then(function () {
Dashboard.navigate('dashboard/dlna/profiles');
}, Dashboard.processErrorResponse);
}
loading.hide();
}
function updateProfile(page, profile) {
profile.Name = $('#txtName', page).val();
profile.EnableAlbumArtInDidl = $('#chkEnableAlbumArtInDidl', page).is(':checked');
profile.EnableSingleAlbumArtLimit = $('#chkEnableSingleImageLimit', page).is(':checked');
profile.SupportedMediaTypes = $('.chkMediaType:checked', page).get().map(function (c) {
return c.getAttribute('data-value');
}).join(',');
profile.Identification = profile.Identification || {};
profile.FriendlyName = $('#txtInfoFriendlyName', page).val();
profile.ModelName = $('#txtInfoModelName', page).val();
profile.ModelNumber = $('#txtInfoModelNumber', page).val();
profile.ModelDescription = $('#txtInfoModelDescription', page).val();
profile.ModelUrl = $('#txtInfoModelUrl', page).val();
profile.Manufacturer = $('#txtInfoManufacturer', page).val();
profile.ManufacturerUrl = $('#txtInfoManufacturerUrl', page).val();
profile.SerialNumber = $('#txtInfoSerialNumber', page).val();
profile.Identification.FriendlyName = $('#txtIdFriendlyName', page).val();
profile.Identification.ModelName = $('#txtIdModelName', page).val();
profile.Identification.ModelNumber = $('#txtIdModelNumber', page).val();
profile.Identification.ModelDescription = $('#txtIdModelDescription', page).val();
profile.Identification.ModelUrl = $('#txtIdModelUrl', page).val();
profile.Identification.Manufacturer = $('#txtIdManufacturer', page).val();
profile.Identification.ManufacturerUrl = $('#txtIdManufacturerUrl', page).val();
profile.Identification.SerialNumber = $('#txtIdSerialNumber', page).val();
profile.Identification.DeviceDescription = $('#txtIdDeviceDescription', page).val();
profile.AlbumArtPn = $('#txtAlbumArtPn', page).val();
profile.MaxAlbumArtWidth = $('#txtAlbumArtMaxWidth', page).val();
profile.MaxAlbumArtHeight = $('#txtAlbumArtMaxHeight', page).val();
profile.MaxIconWidth = $('#txtIconMaxWidth', page).val();
profile.MaxIconHeight = $('#txtIconMaxHeight', page).val();
profile.RequiresPlainFolders = $('#chkRequiresPlainFolders', page).is(':checked');
profile.RequiresPlainVideoItems = $('#chkRequiresPlainVideoItems', page).is(':checked');
profile.IgnoreTranscodeByteRangeRequests = $('#chkIgnoreTranscodeByteRangeRequests', page).is(':checked');
profile.MaxStreamingBitrate = $('#txtMaxAllowedBitrate', page).val();
profile.MusicStreamingTranscodingBitrate = $('#txtMusicStreamingTranscodingBitrate', page).val();
profile.ProtocolInfo = $('#txtProtocolInfo', page).val();
profile.XDlnaCap = $('#txtXDlnaCap', page).val();
profile.XDlnaDoc = $('#txtXDlnaDoc', page).val();
profile.SonyAggregationFlags = $('#txtSonyAggregationFlags', page).val();
profile.UserId = $('#selectUser', page).val();
}
let currentProfile;
let currentSubProfile;
let isSubProfileNew;
const allText = globalize.translate('All');
$(document).on('pageinit', '#dlnaProfilePage', function () {
const page = this;
$('.radioTabButton', page).on('click', function () {
$(this).siblings().removeClass('ui-btn-active');
$(this).addClass('ui-btn-active');
const value = this.tagName == 'A' ? this.getAttribute('data-value') : this.value;
const elem = $('.' + value, page);
elem.siblings('.tabContent').hide();
elem.show();
});
$('#selectDirectPlayProfileType', page).on('change', function () {
if (this.value == 'Video') {
$('#fldDirectPlayVideoCodec', page).show();
} else {
$('#fldDirectPlayVideoCodec', page).hide();
}
if (this.value == 'Photo') {
$('#fldDirectPlayAudioCodec', page).hide();
} else {
$('#fldDirectPlayAudioCodec', page).show();
}
});
$('#selectTranscodingProfileType', page).on('change', function () {
if (this.value == 'Video') {
$('#fldTranscodingVideoCodec', page).show();
$('#fldTranscodingProtocol', page).show();
$('#fldEnableMpegtsM2TsMode', page).show();
} else {
$('#fldTranscodingVideoCodec', page).hide();
$('#fldTranscodingProtocol', page).hide();
$('#fldEnableMpegtsM2TsMode', page).hide();
}
if (this.value == 'Photo') {
$('#fldTranscodingAudioCodec', page).hide();
$('#fldEstimateContentLength', page).hide();
$('#fldReportByteRangeRequests', page).hide();
} else {
$('#fldTranscodingAudioCodec', page).show();
$('#fldEstimateContentLength', page).show();
$('#fldReportByteRangeRequests', page).show();
}
});
$('#selectResponseProfileType', page).on('change', function () {
if (this.value == 'Video') {
$('#fldResponseProfileVideoCodec', page).show();
} else {
$('#fldResponseProfileVideoCodec', page).hide();
}
if (this.value == 'Photo') {
$('#fldResponseProfileAudioCodec', page).hide();
} else {
$('#fldResponseProfileAudioCodec', page).show();
}
});
$('.btnAddDirectPlayProfile', page).on('click', function () {
editDirectPlayProfile(page);
});
$('.btnAddTranscodingProfile', page).on('click', function () {
editTranscodingProfile(page);
});
$('.btnAddContainerProfile', page).on('click', function () {
editContainerProfile(page);
});
$('.btnAddCodecProfile', page).on('click', function () {
editCodecProfile(page);
});
$('.btnAddResponseProfile', page).on('click', function () {
editResponseProfile(page);
});
$('.btnAddIdentificationHttpHeader', page).on('click', function () {
editIdentificationHeader(page);
});
$('.btnAddXmlDocumentAttribute', page).on('click', function () {
editXmlDocumentAttribute(page);
});
$('.btnAddSubtitleProfile', page).on('click', function () {
editSubtitleProfile(page);
});
$('.dlnaProfileForm').off('submit', DlnaProfilePage.onSubmit).on('submit', DlnaProfilePage.onSubmit);
$('.editDirectPlayProfileForm').off('submit', DlnaProfilePage.onDirectPlayFormSubmit).on('submit', DlnaProfilePage.onDirectPlayFormSubmit);
$('.transcodingProfileForm').off('submit', DlnaProfilePage.onTranscodingProfileFormSubmit).on('submit', DlnaProfilePage.onTranscodingProfileFormSubmit);
$('.containerProfileForm').off('submit', DlnaProfilePage.onContainerProfileFormSubmit).on('submit', DlnaProfilePage.onContainerProfileFormSubmit);
$('.codecProfileForm').off('submit', DlnaProfilePage.onCodecProfileFormSubmit).on('submit', DlnaProfilePage.onCodecProfileFormSubmit);
$('.editResponseProfileForm').off('submit', DlnaProfilePage.onResponseProfileFormSubmit).on('submit', DlnaProfilePage.onResponseProfileFormSubmit);
$('.identificationHeaderForm').off('submit', DlnaProfilePage.onIdentificationHeaderFormSubmit).on('submit', DlnaProfilePage.onIdentificationHeaderFormSubmit);
$('.xmlAttributeForm').off('submit', DlnaProfilePage.onXmlAttributeFormSubmit).on('submit', DlnaProfilePage.onXmlAttributeFormSubmit);
$('.subtitleProfileForm').off('submit', DlnaProfilePage.onSubtitleProfileFormSubmit).on('submit', DlnaProfilePage.onSubtitleProfileFormSubmit);
}).on('pageshow', '#dlnaProfilePage', function () {
const page = this;
$('#radioInfo', page).trigger('click');
loadProfile(page);
});
window.DlnaProfilePage = {
onSubmit: function () {
loading.show();
saveProfile($(this).parents('.page'), currentProfile);
return false;
},
onDirectPlayFormSubmit: function () {
saveDirectPlayProfile($(this).parents('.page'));
return false;
},
onTranscodingProfileFormSubmit: function () {
saveTranscodingProfile($(this).parents('.page'));
return false;
},
onContainerProfileFormSubmit: function () {
saveContainerProfile($(this).parents('.page'));
return false;
},
onCodecProfileFormSubmit: function () {
saveCodecProfile($(this).parents('.page'));
return false;
},
onResponseProfileFormSubmit: function () {
saveResponseProfile($(this).parents('.page'));
return false;
},
onIdentificationHeaderFormSubmit: function () {
saveIdentificationHeader($(this).parents('.page'));
return false;
},
onXmlAttributeFormSubmit: function () {
saveXmlDocumentAttribute($(this).parents('.page'));
return false;
},
onSubtitleProfileFormSubmit: function () {
saveSubtitleProfile($(this).parents('.page'));
return false;
}
};

View file

@ -1,32 +0,0 @@
<div id="dlnaProfilesPage" data-role="page" class="page type-interior dlnaPage withTabs">
<div>
<div class="content-primary">
<div class="readOnlyContent">
<div class="verticalSection verticalSection-extrabottompadding">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle">${HeaderCustomDlnaProfiles}</h2>
<a is="emby-linkbutton" href="#/dashboard/dlna/profiles/edit" class="fab submit" style="margin:0 0 0 1em">
<span class="material-icons add" aria-hidden="true"></span>
</a>
</div>
<p>${CustomDlnaProfilesHelp}</p>
<div class="customProfiles"></div>
</div>
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle">${HeaderSystemDlnaProfiles}</h2>
</div>
<p>${SystemDlnaProfilesHelp}</p>
<div class="systemProfiles"></div>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,93 +0,0 @@
import escapeHtml from 'escape-html';
import 'jquery';
import globalize from '../../../scripts/globalize';
import loading from '../../../components/loading/loading';
import libraryMenu from '../../../scripts/libraryMenu';
import '../../../components/listview/listview.scss';
import '../../../elements/emby-button/emby-button';
import confirm from '../../../components/confirm/confirm';
function loadProfiles(page) {
loading.show();
ApiClient.getJSON(ApiClient.getUrl('Dlna/ProfileInfos')).then(function (result) {
renderUserProfiles(page, result);
renderSystemProfiles(page, result);
loading.hide();
});
}
function renderUserProfiles(page, profiles) {
renderProfiles(page, page.querySelector('.customProfiles'), profiles.filter(function (p) {
return p.Type == 'User';
}));
}
function renderSystemProfiles(page, profiles) {
renderProfiles(page, page.querySelector('.systemProfiles'), profiles.filter(function (p) {
return p.Type == 'System';
}));
}
function renderProfiles(page, element, profiles) {
let html = '';
if (profiles.length) {
html += '<div class="paperList">';
}
for (let i = 0, length = profiles.length; i < length; i++) {
const profile = profiles[i];
html += '<div class="listItem listItem-border">';
html += '<span class="listItemIcon material-icons live_tv" aria-hidden="true"></span>';
html += '<div class="listItemBody two-line">';
html += "<a is='emby-linkbutton' style='padding:0;margin:0;' data-ripple='false' class='clearLink' href='#/dashboard/dlna/profiles/edit?id=" + profile.Id + "'>";
html += '<div>' + escapeHtml(profile.Name) + '</div>';
html += '</a>';
html += '</div>';
if (profile.Type == 'User') {
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile" data-profileid="' + profile.Id + '" title="' + globalize.translate('Delete') + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
}
html += '</div>';
}
if (profiles.length) {
html += '</div>';
}
element.innerHTML = html;
$('.btnDeleteProfile', element).on('click', function () {
const id = this.getAttribute('data-profileid');
deleteProfile(page, id);
});
}
function deleteProfile(page, id) {
confirm(globalize.translate('MessageConfirmProfileDeletion'), globalize.translate('HeaderConfirmProfileDeletion')).then(function () {
loading.show();
ApiClient.ajax({
type: 'DELETE',
url: ApiClient.getUrl('Dlna/Profiles/' + id)
}).then(function () {
loading.hide();
loadProfiles(page);
});
});
}
function getTabs() {
return [{
href: '#/dashboard/dlna',
name: globalize.translate('Settings')
}, {
href: '#/dashboard/dlna/profiles',
name: globalize.translate('TabProfiles')
}];
}
$(document).on('pageshow', '#dlnaProfilesPage', function () {
libraryMenu.setTabs('dlna', 1, getTabs);
loadProfiles(this);
});

View file

@ -1,69 +0,0 @@
<div id="dlnaSettingsPage" data-role="page" class="page type-interior withTabs">
<div>
<div class="content-primary">
<form class="dlnaSettingsForm">
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle">${Settings}</h2>
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/networking/dlna">${Help}</a>
</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnablePlayTo" />
<span>${LabelEnableDlnaPlayTo}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LabelEnableDlnaPlayToHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableDlnaDebugLogging" />
<span>${LabelEnableDlnaDebugLogging}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LabelEnableDlnaDebugLoggingHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="number" id="txtClientDiscoveryInterval" min="1" max="300" label="${LabelEnableDlnaClientDiscoveryInterval}" />
<div class="fieldDescription">${LabelEnableDlnaClientDiscoveryIntervalHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkEnableServer" />
<span>${LabelEnableDlnaServer}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LabelEnableDlnaServerHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkBlastAliveMessages" />
<span>${LabelEnableBlastAliveMessages}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LabelEnableBlastAliveMessagesHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="number" id="txtBlastInterval" min="1" max="3600" label="${LabelBlastMessageInterval}" />
<div class="fieldDescription">${LabelBlastMessageIntervalHelp}</div>
</div>
<div class="selectContainer">
<select is="emby-select" id="selectUser" data-mini="true" label="${LabelDefaultUser}"></select>
<div class="fieldDescription">${LabelDefaultUserHelp}</div>
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
</div>
</form>
</div>
</div>
</div>

View file

@ -1,60 +0,0 @@
import escapeHtml from 'escape-html';
import 'jquery';
import loading from '../../../components/loading/loading';
import libraryMenu from '../../../scripts/libraryMenu';
import globalize from '../../../scripts/globalize';
import Dashboard from '../../../utils/dashboard';
function loadPage(page, config, users) {
page.querySelector('#chkEnablePlayTo').checked = config.EnablePlayTo;
page.querySelector('#chkEnableDlnaDebugLogging').checked = config.EnableDebugLog;
$('#txtClientDiscoveryInterval', page).val(config.ClientDiscoveryIntervalSeconds);
$('#chkEnableServer', page).prop('checked', config.EnableServer);
$('#chkBlastAliveMessages', page).prop('checked', config.BlastAliveMessages);
$('#txtBlastInterval', page).val(config.BlastAliveMessageIntervalSeconds);
const usersHtml = users.map(function (u) {
return '<option value="' + u.Id + '">' + escapeHtml(u.Name) + '</option>';
}).join('');
$('#selectUser', page).html(usersHtml).val(config.DefaultUserId || '');
loading.hide();
}
function onSubmit() {
loading.show();
const form = this;
ApiClient.getNamedConfiguration('dlna').then(function (config) {
config.EnablePlayTo = form.querySelector('#chkEnablePlayTo').checked;
config.EnableDebugLog = form.querySelector('#chkEnableDlnaDebugLogging').checked;
config.ClientDiscoveryIntervalSeconds = $('#txtClientDiscoveryInterval', form).val();
config.EnableServer = $('#chkEnableServer', form).is(':checked');
config.BlastAliveMessages = $('#chkBlastAliveMessages', form).is(':checked');
config.BlastAliveMessageIntervalSeconds = $('#txtBlastInterval', form).val();
config.DefaultUserId = $('#selectUser', form).val();
ApiClient.updateNamedConfiguration('dlna', config).then(Dashboard.processServerConfigurationUpdateResult);
});
return false;
}
function getTabs() {
return [{
href: '#/dashboard/dlna',
name: globalize.translate('Settings')
}, {
href: '#/dashboard/dlna/profiles',
name: globalize.translate('TabProfiles')
}];
}
$(document).on('pageinit', '#dlnaSettingsPage', function () {
$('.dlnaSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#dlnaSettingsPage', function () {
libraryMenu.setTabs('dlna', 0, getTabs);
loading.show();
const page = this;
const promise1 = ApiClient.getNamedConfiguration('dlna');
const promise2 = ApiClient.getUsers();
Promise.all([promise1, promise2]).then(function (responses) {
loadPage(page, responses[0], responses[1]);
});
});

View file

@ -1257,12 +1257,16 @@ function renderTags(page, item) {
tags = [];
}
for (let i = 0, length = tags.length; i < length; i++) {
tagElements.push(tags[i]);
}
tags.forEach(tag => {
tagElements.push(
`<a href="#/search.html?query=${encodeURIComponent(tag)}" class="button-link emby-button" is="emby-linkbutton">`
+ escapeHtml(tag)
+ '</a>'
);
});
if (tagElements.length) {
itemTags.innerText = globalize.translate('TagsValue', tagElements.join(', '));
itemTags.innerHTML = globalize.translate('TagsValue', tagElements.join(', '));
itemTags.classList.remove('hide');
} else {
itemTags.innerHTML = '';

View file

@ -401,10 +401,15 @@ function onNewItemClick() {
const instance = this;
import('../components/playlisteditor/playlisteditor').then(({ default: PlaylistEditor }) => {
new PlaylistEditor({
const playlistEditor = new PlaylistEditor();
playlistEditor.show({
items: [],
serverId: instance.params.serverId
}).catch(() => {
// Dialog closed
});
}).catch(err => {
console.error('[onNewItemClick] failed to load playlist editor', err);
});
}

View file

@ -36,7 +36,7 @@ function handleConnectionResult(page, result) {
function submitServer(page) {
loading.show();
const host = page.querySelector('#txtServerHost').value;
const host = page.querySelector('#txtServerHost').value.replace(/\/+$/, '');
ServerConnections.connectToAddress(host, {
enableAutoLogin: appSettings.enableAutoLogin()
}).then(function(result) {

View file

@ -0,0 +1,59 @@
import classNames from 'classnames';
import React, { type DetailedHTMLProps, type InputHTMLAttributes, type FC, useState, useCallback } from 'react';
import './emby-input.scss';
interface InputProps extends DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
id: string,
label?: string
}
const Input: FC<InputProps> = ({
id,
label,
className,
onBlur,
onFocus,
...props
}) => {
const [ isFocused, setIsFocused ] = useState(false);
const onBlurInternal = useCallback(e => {
setIsFocused(false);
onBlur?.(e);
}, [ onBlur ]);
const onFocusInternal = useCallback(e => {
setIsFocused(true);
onFocus?.(e);
}, [ onFocus ]);
return (
<>
<label
htmlFor={id}
className={classNames(
'inputLabel',
{
inputLabelUnfocused: !isFocused,
inputLabelFocused: isFocused
}
)}
>
{label}
</label>
<input
id={id}
className={classNames(
'emby-input',
className
)}
onBlur={onBlurInternal}
onFocus={onFocusInternal}
{...props}
/>
</>
);
};
export default Input;

4
src/global.d.ts vendored
View file

@ -14,4 +14,8 @@ export declare global {
interface DocumentEventMap {
'viewshow': CustomEvent;
}
const __JF_BUILD_VERSION__: string;
const __USE_SYSTEM_FONTS__: string;
const __WEBPACK_SERVE__: string;
}

17
src/hooks/usePrevious.ts Normal file
View file

@ -0,0 +1,17 @@
import { useEffect, useRef } from 'react';
/**
* A hook that returns the previous value of a stateful value.
* @param value A stateful value created by a `useState` hook.
* @param initialValue The default value.
* @returns The previous value.
*/
export function usePrevious<T>(value: T, initialValue?: T): T | undefined {
const ref = useRef<T | undefined>(initialValue);
useEffect(() => {
ref.current = value;
}, [ value ]);
return ref.current;
}

View file

@ -3,21 +3,34 @@ import type { Api } from '@jellyfin/sdk';
import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api';
import type { AxiosRequestConfig } from 'axios';
import { useApi } from './useApi';
import { queryOptions } from 'utils/query/queryOptions';
const fetchSystemInfo = async (
api: Api | undefined,
options: AxiosRequestConfig
api?: Api,
options?: AxiosRequestConfig
) => {
if (!api) throw new Error('No API instance available');
if (!api) {
console.warn('[fetchSystemInfo] No API instance available');
return;
}
const response = await getSystemApi(api)
.getSystemInfo(options);
return response.data;
};
export const useSystemInfo = (api: Api | undefined) => {
return useQuery({
export const getSystemInfoQuery = (
api?: Api
) => queryOptions({
queryKey: [ 'SystemInfo' ],
queryFn: ({ signal }) => fetchSystemInfo(api, { signal }),
// Allow for query reuse in legacy javascript.
staleTime: 1000, // 1 second
enabled: !!api
});
});
export const useSystemInfo = () => {
const { api } = useApi();
return useQuery(getSystemInfoQuery(api));
};

View file

@ -94,7 +94,7 @@ function onGlobalizeInit() {
if (browser.tv && !browser.android) {
console.debug('using system fonts with explicit sizes');
import('./styles/fonts.sized.scss');
} else if (__USE_SYSTEM_FONTS__) { // eslint-disable-line no-undef
} else if (__USE_SYSTEM_FONTS__) {
console.debug('using system fonts');
import('./styles/fonts.scss');
} else {

View file

@ -395,8 +395,7 @@ export function canPlaySecondaryAudio(videoTestElement) {
&& !browser.firefox
// It seems to work on Tizen 5.5+ (2020, Chrome 69+). See https://developer.tizen.org/forums/web-application-development/video-tag-not-work-audiotracks
&& (browser.tizenVersion >= 5.5 || !browser.tizen)
// Assume webOS 5+ (2020, Chrome 68+) supports secondary audio like Tizen 5.5+
&& (browser.web0sVersion >= 5.0 || !browser.web0sVersion);
&& (browser.web0sVersion >= 4.0 || !browser.web0sVersion);
}
export default function (options) {
@ -574,8 +573,8 @@ export default function (options) {
const hlsInFmp4VideoCodecs = [];
if (canPlayAv1(videoTestElement)
&& !browser.mobile && (browser.edgeChromium || browser.firefox || browser.chrome)) {
// disable av1 on mobile since it can be very slow software decoding
&& (browser.safari || (!browser.mobile && (browser.edgeChromium || browser.firefox || browser.chrome)))) {
// disable av1 on non-safari mobile browsers since it can be very slow software decoding
hlsInFmp4VideoCodecs.push('av1');
}
@ -616,13 +615,21 @@ export default function (options) {
if (canPlayVp9) {
mp4VideoCodecs.push('vp9');
// webm support is unreliable on safari 17
if (!browser.safari
|| (browser.safari && browser.versionMajor >= 15 && browser.versionMajor < 17)) {
webmVideoCodecs.push('vp9');
}
}
if (canPlayAv1(videoTestElement)) {
mp4VideoCodecs.push('av1');
// webm support is unreliable on safari 17
if (!browser.safari
|| (browser.safari && browser.versionMajor >= 15 && browser.versionMajor < 17)) {
webmVideoCodecs.push('av1');
}
}
if (canPlayVp8 || browser.tizen) {
videoAudioCodecs.push('vorbis');

View file

@ -187,10 +187,15 @@ export default function (view) {
view.querySelector('.btnNewPlaylist').addEventListener('click', function () {
import('../components/playlisteditor/playlisteditor').then(({ default: PlaylistEditor }) => {
const serverId = ApiClient.serverInfo().Id;
new PlaylistEditor({
const playlistEditor = new PlaylistEditor();
playlistEditor.show({
items: [],
serverId: serverId
}).catch(() => {
// Dialog closed
});
}).catch(err => {
console.error('[btnNewPlaylist] failed to load playlist editor', err);
});
});
onViewStyleChange();

View file

@ -2,17 +2,17 @@ import focusManager from '../components/focusManager';
import dom from './dom';
import '../styles/scrollstyles.scss';
function getBoundingClientRect(elem) {
function getBoundingClientRect(elem: Element) {
// Support: BlackBerry 5, iOS 3 (original iPhone)
// If we don't have gBCR, just use 0,0 rather than error
if (elem.getBoundingClientRect) {
return elem.getBoundingClientRect();
} else {
return { top: 0, left: 0 };
return { top: 0, left: 0, width: undefined, height: undefined };
}
}
export function getPosition(scrollContainer, item, horizontal) {
export function getPosition(scrollContainer: HTMLElement, item: HTMLElement, horizontal: boolean) {
const slideeOffset = getBoundingClientRect(scrollContainer);
const itemOffset = getBoundingClientRect(item);
@ -41,7 +41,7 @@ export function getPosition(scrollContainer, item, horizontal) {
};
}
export function toCenter(container, elem, horizontal, skipWhenVisible) {
export function toCenter(container: HTMLElement, elem: HTMLElement, horizontal: boolean, skipWhenVisible?: boolean) {
const pos = getPosition(container, elem, horizontal);
if (skipWhenVisible && pos.isVisible) {
@ -61,7 +61,7 @@ export function toCenter(container, elem, horizontal, skipWhenVisible) {
}
}
export function toStart(container, elem, horizontal, skipWhenVisible) {
export function toStart(container: HTMLElement, elem: HTMLElement, horizontal: boolean, skipWhenVisible?: boolean) {
const pos = getPosition(container, elem, horizontal);
if (skipWhenVisible && pos.isVisible) {
@ -81,7 +81,7 @@ export function toStart(container, elem, horizontal, skipWhenVisible) {
}
}
function centerOnFocus(e, scrollSlider, horizontal) {
function centerOnFocus(e: Event, scrollSlider: HTMLElement, horizontal: boolean) {
const focused = focusManager.focusableParent(e.target);
if (focused) {
@ -89,16 +89,16 @@ function centerOnFocus(e, scrollSlider, horizontal) {
}
}
function centerOnFocusHorizontal(e) {
function centerOnFocusHorizontal(this: HTMLElement, e: Event) {
centerOnFocus(e, this, true);
}
function centerOnFocusVertical(e) {
function centerOnFocusVertical(this: HTMLElement, e: Event) {
centerOnFocus(e, this, false);
}
export const centerFocus = {
on: function (element, horizontal) {
on: function (element: Element, horizontal: boolean) {
element.setAttribute(`data-scroll-mode-${horizontal ? 'x' : 'y'}`, 'custom');
if (horizontal) {
@ -113,7 +113,7 @@ export const centerFocus = {
});
}
},
off: function (element, horizontal) {
off: function (element: Element, horizontal: boolean) {
element.removeAttribute(`data-scroll-mode-${horizontal ? 'x' : 'y'}`);
if (horizontal) {

View file

@ -35,7 +35,7 @@ export function getIncludeCorsCredentials() {
export function getMultiServer() {
// Enable multi-server support when served by webpack
if (__WEBPACK_SERVE__) { // eslint-disable-line no-undef
if (__WEBPACK_SERVE__) {
return Promise.resolve(true);
}

View file

@ -1722,5 +1722,6 @@
"LabelEnableAudioVbrHelp": "معدل البِت المتغير ينتج على جودة أفضل مقارنة بمعدل البت المتوسط، ولكن في بعض الحالات النادرة قد يسبب مشاكل في التخزين المؤقت والتوافق.",
"LabelSegmentKeepSecondsHelp": "الزمن بالثواني الذي يجب الاحتفاظ به للشرائح قبل أن يتم الكتابة فوقها. يجب أن يكون أكبر من \"بعد الخنق\". يعمل هذا ألأعداد فقط إذا كان حذف الشرائح مفعلًا.",
"AiTranslated": "مترجمة من قبل ذكاء اسطناعي",
"SelectAudioNormalizationHelp": "كسب الالبوم-تعديل الصوت لكل مسار لكي يعملون بنفس مستوى- كسب الالبوم- تعديل مستوى الصوت لكل المسارات في البوم واحد مع ابقاء على النطاق الديناميكي للألبوم."
"SelectAudioNormalizationHelp": "كسب الالبوم-تعديل الصوت لكل مسار لكي يعملون بنفس مستوى- كسب الالبوم- تعديل مستوى الصوت لكل المسارات في البوم واحد مع ابقاء على النطاق الديناميكي للألبوم.",
"ButtonEditUser": "تعديل مستخدم"
}

View file

@ -223,7 +223,7 @@
"LabelCollection": "Col·lecció",
"LabelCommunityRating": "Valoració de la comunitat",
"LabelContentType": "Tipus de contingut",
"LabelCountry": "País",
"LabelCountry": "País/Regió",
"LabelCriticRating": "Valoració dels сrítics",
"LabelCurrentPassword": "Contrasenya actual",
"LabelCustomCss": "Codi CSS propi",
@ -1778,5 +1778,7 @@
"MachineTranslated": "Traduït per Màquina",
"ForeignPartsOnly": "Només parts Forçades/Forànies",
"HeaderGuestCast": "Estrelles Convidades",
"HeaderAllRecordings": "Tots els enregistraments"
"HeaderAllRecordings": "Tots els enregistraments",
"LabelServerVersion": "Versió del Servidor",
"LabelWebVersion": "Versió Web"
}

View file

@ -379,7 +379,7 @@
"LabelCollection": "Kolekce",
"LabelCommunityRating": "Hodnocení komunity",
"LabelContentType": "Typ obsahu",
"LabelCountry": "Země",
"LabelCountry": "Země/oblast",
"LabelCriticRating": "Hodnocení kritiků",
"LabelCurrentPassword": "Aktuální heslo",
"LabelCustomCss": "Vlastní CSS",
@ -1318,8 +1318,8 @@
"LabelSyncPlayTimeOffset": "Časový rozdíl mezi serverem:",
"HeaderSyncPlayEnabled": "Synchronizace přehrávání povolena",
"HeaderSyncPlaySelectGroup": "Připojit ke skupině",
"EnableDetailsBannerHelp": "Zobrazí obrázek ve vrchní části detailu položky.",
"EnableDetailsBanner": "Obrázek detailu",
"EnableDetailsBannerHelp": "Zobrazí banner v horní části stránky s detailem položky.",
"EnableDetailsBanner": "Zobrazit banner na stránce s detailem",
"ShowMore": "Zobrazit více",
"ShowLess": "Zobrazit méně",
"EnableBlurHashHelp": "Obrázky, které se ještě načítají, budou zobrazeny pomocí jedinečných zástupných obrázků.",
@ -1782,5 +1782,9 @@
"LabelTrackGain": "Na úrovni skladby",
"SelectAudioNormalizationHelp": "Normalizace na úrovni skladby upraví hlasitost všech skladeb tak, aby byla všude stejná. Normalizace na úrovni alba upraví hlasitost všech skladeb tak, aby byla hlasitost stejná v rámci jednotlivých alb.",
"SearchResultsEmpty": "Pro “{0}” nebylo nic nalezeno",
"HeaderAllRecordings": "Všechny nahrávky"
"HeaderAllRecordings": "Všechny nahrávky",
"LabelBuildVersion": "Verze sestavení",
"LabelServerVersion": "Verze serveru",
"LabelWebVersion": "Verze webu",
"ButtonEditUser": "Upravit uživatele"
}

View file

@ -346,7 +346,7 @@
"LabelCollection": "Samling",
"LabelCommunityRating": "Fællesskabsvurdering",
"LabelContentType": "Indholdstype",
"LabelCountry": "Land",
"LabelCountry": "Land/Region",
"LabelCriticRating": "Kritiker bedømmelse",
"LabelCurrentPassword": "Nuværende kode",
"LabelCustomCertificatePath": "Brugerdefineret SSL certifikat sti",
@ -1754,5 +1754,12 @@
"LabelLevel": "Niveau",
"LabelMediaDetails": "Media detaljer",
"MenuClose": "Luk menu",
"MediaInfoDvBlSignalCompatibilityId": "DV bl signals kompatibilitets id"
"MediaInfoDvBlSignalCompatibilityId": "DV bl signals kompatibilitets id",
"ButtonEditUser": "Rediger bruger",
"HeaderAllRecordings": "alle optagelser",
"ListView": "liste udsigt",
"LabelVppTonemappingContrastHelp": "Tilføj kontrastere",
"LogLevel.Trace": "Spore",
"LabelServerVersion": "server version",
"LabelWebVersion": "Web version"
}

View file

@ -428,7 +428,7 @@
"LabelCollection": "Sammlung",
"LabelCommunityRating": "Community Bewertung",
"LabelContentType": "Typ des Inhalts",
"LabelCountry": "Land",
"LabelCountry": "Land/Region",
"LabelCriticRating": "Kritikerbewertung",
"LabelCurrentPassword": "Aktuelles Passwort",
"LabelCustomCertificatePath": "Benutzerdefinierter Pfad zum SSL-Zertifikat",
@ -1761,7 +1761,7 @@
"LabelThrottleDelaySeconds": "Drosseln nach",
"LabelThrottleDelaySecondsHelp": "Zeit in Sekunden, nach der die Transkodierung gedrosselt wird. Muss groß genug sein, damit der Client einen gesunden Puffer aufrechterhalten kann. Funktioniert nur wenn \"Transkodierung drosseln\" aktiviert ist.",
"LabelSegmentKeepSeconds": "Zeit, wie lange Segmente behalten werden",
"LabelSegmentKeepSecondsHelp": "Zeit in Sekunden, in der Segmente nicht überschrieben werden dürfen. Muss größer sein als \"Drosseln nach\". Funktioniert nur wenn \"Segmente löschen\" aktiviert ist.",
"LabelSegmentKeepSecondsHelp": "Zeit in Sekunden, für die Segmente behalten werden sollen, bevor sie überschrieben werden. Muss größer sein als \"Drosseln nach\". Funktioniert nur wenn \"Segmente löschen\" aktiviert ist.",
"LogoScreensaver": "Logo Bildschirmschoner",
"UnknownError": "Ein unbekannter Fehler trat auf.",
"GridView": "Kachelansicht",
@ -1779,8 +1779,13 @@
"LabelBackdropScreensaverIntervalHelp": "Die Zeit in Sekunden zwischen dem Wechsel verschiedener Hintergrundbilder im Bildschirmschoner.",
"SearchResultsEmpty": "Entschuldigung! Es konnten keine Ergebnisse für „{0}“ gefunden werden",
"LabelTrackGain": "Titel Gain",
"SelectAudioNormalizationHelp": "Track Gain - passt die Lautstärke der einzelnen Tracks an, so dass sie mit der gleichen Lautstärke wiedergegeben werden. Albumverstärkung - passt die Lautstärke aller Titel eines Albums an, wobei der Dynamikbereich des Albums erhalten bleibt.",
"SelectAudioNormalizationHelp": "Track Gain - passt die Lautstärke der einzelnen Tracks an, sodass sie mit der gleichen Lautstärke wiedergegeben werden. Albumverstärkung - passt die Lautstärke aller Titel eines Albums an, wobei der Dynamikbereich des Albums erhalten bleibt.",
"LabelAlbumGain": "Albumlautstärke",
"LabelSelectAudioNormalization": "Audio Normalisierung",
"HeaderAllRecordings": "Alle Aufnahmen"
"HeaderAllRecordings": "Alle Aufnahmen",
"LabelBuildVersion": "Build-Version",
"LabelServerVersion": "Server-Version",
"LabelWebVersion": "Web-Version",
"ButtonEditUser": "Editiere Benutzer",
"DlnaMovedMessage": "Die DLNA-Funktion wurde in ein Plugin verschoben."
}

View file

@ -984,7 +984,7 @@
"LabelCustomCertificatePathHelp": "Path to a PKCS #12 file containing a certificate and private key to enable TLS support on a custom domain.",
"LabelCurrentPassword": "Current password",
"LabelCriticRating": "Critics rating",
"LabelCountry": "Country",
"LabelCountry": "Country/Region",
"LabelContentType": "Content type",
"LabelCommunityRating": "Community rating",
"LabelCertificatePassword": "Certificate password",
@ -1782,5 +1782,9 @@
"SearchResultsEmpty": "Sorry! No results found for \"{0}\"",
"LabelSelectAudioNormalization": "Audio Normalisation",
"LabelTrackGain": "Track Gain",
"HeaderAllRecordings": "All Recordings"
"HeaderAllRecordings": "All Recordings",
"ButtonEditUser": "Edit user",
"LabelBuildVersion": "Build version",
"LabelServerVersion": "Server version",
"LabelWebVersion": "Web version"
}

View file

@ -88,6 +88,7 @@
"ButtonChangeServer": "Change Server",
"ButtonClose": "Close",
"ButtonEditOtherUserPreferences": "Edit this user's profile, image and personal preferences.",
"ButtonEditUser": "Edit user",
"ButtonForgotPassword": "Forgot Password",
"ButtonFullscreen": "Fullscreen",
"ButtonGotIt": "Got It",
@ -97,7 +98,6 @@
"ButtonMore": "More",
"ButtonNextTrack": "Next track",
"ButtonOk": "Ok",
"ButtonOpen": "Open",
"ButtonParentalControl": "Parental control",
"ButtonPause": "Pause",
"ButtonPlayer": "Player",
@ -134,6 +134,11 @@
"Categories": "Categories",
"ChangingMetadataImageSettingsNewContent": "Changes to metadata or artwork downloading settings will only apply to new content added to your library. To apply the changes to existing titles, you'll need to refresh their metadata manually.",
"ChannelAccessHelp": "Select the channels to share with this user. Administrators will be able to edit all channels using the metadata manager.",
"ChannelResolutionSD": "SD",
"ChannelResolutionSDPAL": "SD (PAL)",
"ChannelResolutionHD": "HD",
"ChannelResolutionFullHD": "Full HD",
"ChannelResolutionUHD4K": "UHD (4K)",
"ChannelNameOnly": "Channel {0} only",
"ChannelNumber": "Channel number",
"Channels": "Channels",
@ -165,10 +170,7 @@
"CopyStreamURLSuccess": "URL copied successfully.",
"CriticRating": "Critics rating",
"Cursive": "Cursive",
"CustomDlnaProfilesHelp": "Create a custom profile to target a new device or override a system profile.",
"DailyAt": "Daily at {0}",
"DashboardServerName": "Server: {0}",
"DashboardVersionNumber": "Version: {0}",
"Data": "Data",
"DateAdded": "Date added",
"DatePlayed": "Date played",
@ -210,6 +212,7 @@
"DisplayMissingEpisodesWithinSeasons": "Display missing episodes within seasons",
"DisplayMissingEpisodesWithinSeasonsHelp": "This must also be enabled for TV libraries in the server configuration.",
"DisplayModeHelp": "Select the layout style you want for the interface.",
"DlnaMovedMessage": "The DLNA functionality has moved to a plugin.",
"DoNotRecord": "Do not record",
"Down": "Down",
"Download": "Download",
@ -340,21 +343,15 @@
"HeaderCastAndCrew": "Cast & Crew",
"HeaderChannelAccess": "Channel Access",
"HeaderChapterImages": "Chapter Images",
"HeaderCodecProfile": "Codec Profile",
"HeaderCodecProfileHelp": "Codec profiles indicate the limitations of a device when playing specific codecs. If a limitation applies then the media will be transcoded, even if the codec is configured for direct playback.",
"HeaderConfigureRemoteAccess": "Set up Remote Access",
"HeaderConfirmPluginInstallation": "Confirm Plugin Installation",
"HeaderConfirmRepositoryInstallation": "Confirm Plugin Repository Installation",
"HeaderConfirmProfileDeletion": "Confirm Profile Deletion",
"HeaderConfirmRevokeApiKey": "Revoke API Key",
"HeaderConnectionFailure": "Connection Failure",
"HeaderConnectToServer": "Connect to Server",
"HeaderContainerProfile": "Container Profile",
"HeaderContainerProfileHelp": "Container profiles indicate the limitations of a device when playing specific formats. If a limitation applies then the media will be transcoded, even if the format is configured for direct playback.",
"HeaderContinueListening": "Continue Listening",
"HeaderContinueWatching": "Continue Watching",
"HeaderContinueReading": "Continue Reading",
"HeaderCustomDlnaProfiles": "Custom Profiles",
"HeaderDateIssued": "Date Issued",
"HeaderDefaultRecordingSettings": "Default Recording Settings",
"HeaderDeleteDevice": "Delete Device",
@ -367,8 +364,6 @@
"HeaderDeveloperInfo": "Developer Info",
"HeaderDeviceAccess": "Device Access",
"HeaderDevices": "Devices",
"HeaderDirectPlayProfile": "Direct Playback Profile",
"HeaderDirectPlayProfileHelp": "Add direct playback profiles to indicate which formats the device can handle natively.",
"HeaderDownloadSync": "Download & Sync",
"HeaderDummyChapter": "Chapter Images",
"HeaderDVR": "DVR",
@ -385,14 +380,9 @@
"HeaderFrequentlyPlayed": "Frequently Played",
"HeaderGuestCast": "Guest Stars",
"HeaderGuideProviders": "TV Guide Data Providers",
"HeaderHttpHeaders": "HTTP Headers",
"HeaderHttpsSettings": "HTTPS Settings",
"HeaderIdentification": "Identification",
"HeaderIdentificationCriteriaHelp": "Enter at least one identification criteria.",
"HeaderIdentificationHeader": "Identification Header",
"HeaderIdentifyItemHelp": "Enter one or more search criteria. Remove criteria to increase search results.",
"HeaderImageOptions": "Image Options",
"HeaderImageSettings": "Image Settings",
"HeaderInstall": "Install",
"HeaderInstantMix": "Instant Mix",
"HeaderKeepRecording": "Keep Recording",
@ -441,8 +431,6 @@
"HeaderPluginInstallation": "Plugin Installation",
"HeaderPortRanges": "Firewall and Proxy Settings",
"HeaderPreferredMetadataLanguage": "Preferred Metadata Language",
"HeaderProfileInformation": "Profile Information",
"HeaderProfileServerSettingsHelp": "These values control how the server will present itself to clients.",
"HeaderRecentlyPlayed": "Recently Played",
"HeaderRecordingMetadataSaving": "Recording Metadata",
"HeaderRecordingOptions": "Recording Options",
@ -451,8 +439,6 @@
"HeaderRemoteControl": "Remote Control",
"HeaderRemoveMediaFolder": "Remove Media Folder",
"HeaderRemoveMediaLocation": "Remove Media Location",
"HeaderResponseProfile": "Response Profile",
"HeaderResponseProfileHelp": "Response profiles provide a way to customize information sent to the device when playing certain kinds of media.",
"HeaderRevisionHistory": "Revision History",
"HeaderRunningTasks": "Running Tasks",
"HeaderScenes": "Scenes",
@ -470,7 +456,6 @@
"HeaderSeriesOptions": "Series Options",
"HeaderSeriesStatus": "Series Status",
"HeaderServerAddressSettings": "Server Address Settings",
"HeaderServerSettings": "Server Settings",
"HeaderSetupLibrary": "Setup your media libraries",
"HeaderSortBy": "Sort By",
"HeaderSortOrder": "Sort Order",
@ -480,20 +465,14 @@
"HeaderStopRecording": "Stop Recording",
"HeaderSubtitleAppearance": "Subtitle Appearance",
"HeaderSubtitleDownloads": "Subtitle Downloads",
"HeaderSubtitleProfile": "Subtitle Profile",
"HeaderSubtitleProfiles": "Subtitle Profiles",
"HeaderSubtitleProfilesHelp": "Subtitle profiles describe the subtitle formats supported by the device.",
"HeaderSyncPlayEnabled": "SyncPlay enabled",
"HeaderSyncPlaySelectGroup": "Join a group",
"HeaderSyncPlaySettings": "SyncPlay Settings",
"HeaderSyncPlayPlaybackSettings": "Playback",
"HeaderSyncPlayTimeSyncSettings": "Time sync",
"HeaderSystemDlnaProfiles": "System Profiles",
"HeaderTaskTriggers": "Task Triggers",
"HeaderThisUserIsCurrentlyDisabled": "This user is currently disabled",
"HeaderTracks": "Tracks",
"HeaderTranscodingProfile": "Transcoding Profile",
"HeaderTranscodingProfileHelp": "Add transcoding profiles to indicate which formats should be used when transcoding is required.",
"HeaderTunerDevices": "Tuner Devices",
"HeaderTuners": "Tuners",
"HeaderTypeImageFetchers": "Image fetchers ({0})",
@ -508,9 +487,6 @@
"HeaderVideos": "Videos",
"HeaderVideoType": "Video Type",
"HeaderVideoTypes": "Video Types",
"HeaderXmlDocumentAttribute": "XML Document Attribute",
"HeaderXmlDocumentAttributes": "XML Document Attributes",
"HeaderXmlSettings": "XML Settings",
"HeaderYears": "Years",
"Help": "Help",
"Hide": "Hide",
@ -543,12 +519,7 @@
"LabelAirsBeforeSeason": "Airs before season",
"LabelAirTime": "Air time",
"LabelAlbum": "Album",
"LabelAlbumArtHelp": "PN used for album art, within the 'dlna:profileID' attribute on 'upnp:albumArtURI'. Some devices require a specific value, regardless of the size of the image.",
"LabelAlbumArtists": "Album artists",
"LabelAlbumArtMaxHeight": "Album art max height",
"LabelAlbumArtMaxResHelp": "Maximum resolution of album art exposed via the 'upnp:albumArtURI' property.",
"LabelAlbumArtMaxWidth": "Album art max width",
"LabelAlbumArtPN": "Album art PN",
"LabelAlbumGain": "Album Gain",
"LabelAllowedRemoteAddresses": "Remote IP address filter",
"LabelAllowedRemoteAddressesMode": "Remote IP address filter mode",
@ -582,6 +553,7 @@
"LabelBlastMessageInterval": "Alive message interval",
"LabelBlastMessageIntervalHelp": "Determine the duration in seconds between blast alive messages.",
"LabelBlockContentWithTags": "Block items with tags",
"LabelBuildVersion": "Build version",
"LabelBurnSubtitles": "Burn subtitles",
"LabelCache": "Cache",
"LabelCachePath": "Cache path",
@ -598,7 +570,7 @@
"LabelCommunityRating": "Community rating",
"LabelContentType": "Content type",
"LabelCorruptedFrames": "Corrupted frames",
"LabelCountry": "Country",
"LabelCountry": "Country/Region",
"LabelCreateHttpPortMap": "Enable automatic port mapping for HTTP traffic as well as HTTPS.",
"LabelCreateHttpPortMapHelp": "Permit automatic port mapping to create a rule for HTTP traffic in addition to HTTPS traffic.",
"LabelCriticRating": "Critics rating",
@ -619,12 +591,8 @@
"LabelDay": "Day of week",
"LabelDeathDate": "Death date",
"LabelDefaultScreen": "Default screen",
"LabelDefaultUser": "Default user",
"LabelDefaultUserHelp": "Determine which user library should be displayed on connected devices. This can be overridden for each device using profiles.",
"LabelDeinterlaceMethod": "Deinterlacing method",
"LabelDeveloper": "Developer",
"LabelDeviceDescription": "Device description",
"LabelDidlMode": "DIDL mode",
"LabelDisableCustomCss": "Disable custom CSS code for theming/branding provided from the server.",
"LabelDiscNumber": "Disc number",
"LabelDisplayLanguage": "Display language",
@ -646,22 +614,10 @@
"LabelChapterImageResolution": "Resolution",
"LabelChapterImageResolutionHelp": "The resolution of the extracted chapter images. Changing this will have no effect on existing dummy chapters.",
"LabelDynamicExternalId": "{0} Id",
"LabelEmbedAlbumArtDidl": "Embed album art in DIDL",
"LabelEmbedAlbumArtDidlHelp": "Some devices prefer this method for getting the album art. Others may fail to play with this option enabled.",
"LabelEnableAudioVbr": "Enable VBR audio encoding",
"LabelEnableAudioVbrHelp": "Variable bitrate offers better quality to average bitrate ratio, but in some rare cases may cause buffering and compatibility issues.",
"LabelEnableAutomaticPortMap": "Enable automatic port mapping",
"LabelEnableAutomaticPortMapHelp": "Automatically forward public ports on your router to local ports on your server via UPnP. This may not work with some router models or network configurations. Changes will not apply until after a server restart.",
"LabelEnableBlastAliveMessages": "Blast alive messages",
"LabelEnableBlastAliveMessagesHelp": "Enable this if the server is not detected reliably by other UPnP devices on your network.",
"LabelEnableDlnaClientDiscoveryInterval": "Client discovery interval",
"LabelEnableDlnaClientDiscoveryIntervalHelp": "Determine the duration in seconds between two SSDP searches.",
"LabelEnableDlnaDebugLogging": "Enable DLNA debug logging",
"LabelEnableDlnaDebugLoggingHelp": "Create large log files and should only be used as needed for troubleshooting purposes.",
"LabelEnableDlnaPlayTo": "Enable 'Play To' DLNA feature",
"LabelEnableDlnaPlayToHelp": "Detect devices within your network and offer the ability to control them remotely.",
"LabelEnableDlnaServer": "Enable DLNA server",
"LabelEnableDlnaServerHelp": "Allow UPnP devices on your network to browse and play content.",
"LabelEnableHardwareDecodingFor": "Enable hardware decoding for",
"LabelEnableHttps": "Enable HTTPS",
"LabelEnableHttpsHelp": "Listen on the configured HTTPS port. A valid certificate must also be supplied for this to take effect.",
@ -673,8 +629,6 @@
"LabelEnableLUFSScanHelp": "Clients can normalize audio playback to get equal loudness across tracks. This will make library scans longer and take more resources.",
"LabelEnableRealtimeMonitor": "Enable real time monitoring",
"LabelEnableRealtimeMonitorHelp": "Changes to files will be processed immediately on supported file systems.",
"LabelEnableSingleImageInDidlLimit": "Limit to single embedded image",
"LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within DIDL.",
"LabelEncoderPreset": "Encoding preset",
"LabelEndDate": "End date",
"LabelEpisodeNumber": "Episode number",
@ -703,10 +657,6 @@
"LabelHomeScreenSectionValue": "Home screen section {0}",
"LabelHttpsPort": "Local HTTPS port number",
"LabelHttpsPortHelp": "The TCP port number for the HTTPS server.",
"LabelIconMaxHeight": "Icon maximum height",
"LabelIconMaxResHelp": "Maximum resolution of icons exposed via the 'upnp:icon' property.",
"LabelIconMaxWidth": "Icon maximum width",
"LabelIdentificationFieldHelp": "A case-insensitive substring or regex expression.",
"LabelImageFetchersHelp": "Enable and rank your preferred image fetchers in order of priority.",
"LabelImageType": "Image type",
"LabelImportOnlyFavoriteChannels": "Restrict to channels marked as favorite",
@ -742,9 +692,6 @@
"LabelLoginDisclaimer": "Login disclaimer",
"LabelLoginDisclaimerHelp": "A message that will be displayed at the bottom of the login page.",
"LabelLogs": "Logs",
"LabelManufacturer": "Manufacturer",
"LabelManufacturerUrl": "Manufacturer URL",
"LabelMatchType": "Match type",
"LabelMaxAudiobookResume": "Audiobook remaining minutes to resume",
"LabelMaxAudiobookResumeHelp": "Titles are assumed fully played if stopped when the remaining duration is less than this value.",
"LabelMaxBackdropsPerItem": "Maximum number of backdrops per item",
@ -754,8 +701,6 @@
"LabelMaxParentalRating": "Maximum allowed parental rating",
"LabelMaxResumePercentage": "Maximum resume percentage",
"LabelMaxResumePercentageHelp": "Titles are assumed fully played if stopped after this time.",
"LabelMaxStreamingBitrate": "Maximum streaming quality",
"LabelMaxStreamingBitrateHelp": "Specify a maximum bitrate when streaming.",
"LabelMessageText": "Message text",
"LabelMessageTitle": "Message title",
"LabelMetadata": "Metadata",
@ -767,7 +712,6 @@
"LabelMetadataReadersHelp": "Rank your preferred local metadata sources in order of priority. The first file found will be read.",
"LabelMetadataSavers": "Metadata savers",
"LabelMetadataSaversHelp": "Pick the file formats to use when saving your metadata.",
"LabelMethod": "Method",
"LabelMinAudiobookResume": "Minimum Audiobook resume in minutes",
"LabelMinAudiobookResumeHelp": "Titles are assumed unplayed if stopped before this time.",
"LabelMinBackdropDownloadWidth": "Minimum backdrop download width",
@ -775,16 +719,10 @@
"LabelMinResumeDurationHelp": "The shortest video length in seconds that will save playback location and let you resume.",
"LabelMinResumePercentage": "Minimum resume percentage",
"LabelMinResumePercentageHelp": "Titles are assumed unplayed if stopped before this time.",
"LabelModelDescription": "Model description",
"LabelModelName": "Model name",
"LabelModelNumber": "Model number",
"LabelModelUrl": "Model URL",
"LabelMovieCategories": "Movie categories",
"LabelMoviePrefix": "Movie prefix",
"LabelMoviePrefixHelp": "If a prefix is applied to movie titles, enter it here so the server can handle it properly.",
"LabelMovieRecordingPath": "Movie recording path",
"LabelMusicStreamingTranscodingBitrate": "Music transcoding bitrate",
"LabelMusicStreamingTranscodingBitrateHelp": "Specify a maximum bitrate when streaming music.",
"LabelName": "Name",
"LabelNewName": "New name",
"LabelNewPassword": "New password",
@ -824,15 +762,8 @@
"LabelPostProcessorArgumentsHelp": "Use {path} as the path to the recording file.",
"LabelPreferredDisplayLanguage": "Preferred display language",
"LabelPreferredSubtitleLanguage": "Preferred subtitle language",
"LabelProfileAudioCodecs": "Audio codecs",
"LabelProfileCodecs": "Codecs",
"LabelProfileCodecsHelp": "Separated by comma. This can be left empty to apply to all codecs.",
"LabelProfileContainer": "Container",
"LabelProfileContainersHelp": "Separated by comma. This can be left empty to apply to all containers.",
"LabelProfileVideoCodecs": "Video codecs",
"LabelProtocol": "Protocol",
"LabelProtocolInfo": "Protocol info",
"LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device.",
"LabelPublicHttpPort": "Public HTTP port number",
"LabelPublicHttpPortHelp": "The public port number that should be mapped to the local HTTP port.",
"LabelPublicHttpsPort": "Public HTTPS port number",
@ -863,12 +794,12 @@
"LabelSelectFolderGroups": "Automatically group content from the following folders into views such as 'Movies', 'Music' and 'TV'",
"LabelSelectFolderGroupsHelp": "Folders that are unchecked will be displayed by themselves in their own view.",
"LabelSelectVersionToInstall": "Select version to install",
"LabelSerialNumber": "Serial number",
"LabelSeriesRecordingPath": "Series recording path",
"LabelServerHost": "Host",
"LabelServerHostHelp": "192.168.1.100:8096 or https://myserver.com",
"LabelServerName": "Server name",
"LabelServerNameHelp": "This name will be used to identify the server and will default to the server's hostname.",
"LabelServerVersion": "Server version",
"LabelSimultaneousConnectionLimit": "Simultaneous stream limit",
"LabelSize": "Size",
"LabelSkipBackLength": "Skip back length",
@ -879,8 +810,6 @@
"LabelSkipIfGraphicalSubsPresentHelp": "Keeping text versions of subtitles will result in more efficient delivery and decrease the likelihood of video transcoding.",
"LabelSlowResponseEnabled": "Log a warning message if the server was slow to answer",
"LabelSlowResponseTime": "Time in ms after which a response is considered slow",
"LabelSonyAggregationFlags": "Sony aggregation flags",
"LabelSonyAggregationFlagsHelp": "Determine the content of the 'aggregationFlags' element in the 'urn:schemas-sonycom:av' namespace.",
"LabelSortBy": "Sort by",
"LabelSortName": "Sort name",
"LabelSortOrder": "Sort order",
@ -895,10 +824,8 @@
"LabelStopWhenPossible": "Stop when possible",
"LabelStreamType": "Stream type",
"LabelSubtitleDownloaders": "Subtitle downloaders",
"LabelSubtitleFormatHelp": "Example: srt",
"LabelSubtitlePlaybackMode": "Subtitle mode",
"LabelSubtitleVerticalPosition": "Vertical position",
"LabelSupportedMediaTypes": "Supported Media Types",
"LabelSyncPlayAccess": "SyncPlay access",
"LabelSyncPlayAccessCreateAndJoinGroups": "Allow user to create and join groups",
"LabelSyncPlayAccessJoinGroups": "Allow user to join groups",
@ -973,8 +900,6 @@
"LabelUnstable": "Unstable",
"LabelUser": "User",
"LabelUserAgent": "User agent",
"LabelUserLibrary": "User library",
"LabelUserLibraryHelp": "Select which user library to display to the device. Leave empty to inherit the default setting.",
"LabelUserLoginAttemptsBeforeLockout": "Failed login tries before user is locked out",
"LabelUserMaxActiveSessions": "Maximum number of simultaneous user sessions",
"LabelUsername": "Username",
@ -989,10 +914,7 @@
"LabelVideoRange": "Video range",
"LabelVideoResolution": "Video resolution",
"LabelWeb": "Web",
"LabelXDlnaCap": "Device Capability ID",
"LabelXDlnaCapHelp": "Determine the content of the 'X_DLNACAP' element in the 'urn:schemas-dlna-org:device-1-0' namespace.",
"LabelXDlnaDoc": "Device Class ID",
"LabelXDlnaDocHelp": "Determine the content of the 'X_DLNADOC' element in the 'urn:schemas-dlna-org:device-1-0' namespace.",
"LabelWebVersion": "Web version",
"LabelYear": "Year",
"LabelYoureDone": "You're Done!",
"LabelZipCode": "Zip Code",
@ -1069,7 +991,6 @@
"MessageConfirmAppExit": "Do you want to exit?",
"MessageConfirmDeleteGuideProvider": "Are you sure you wish to delete this guide provider?",
"MessageConfirmDeleteTunerDevice": "Are you sure you wish to delete this device?",
"MessageConfirmProfileDeletion": "Are you sure you wish to delete this profile?",
"MessageConfirmRecordingCancellation": "Cancel recording?",
"MessageConfirmRemoveMediaLocation": "Are you sure you wish to remove this location?",
"MessageConfirmRestart": "Are you sure you wish to restart Jellyfin?",
@ -1214,7 +1135,6 @@
"OptionAutomaticallyGroupSeries": "Automatically merge series that are spread across multiple folders",
"OptionAutomaticallyGroupSeriesHelp": "Series that are spread across multiple folders within this library will be automatically merged into a single series.",
"OptionBluray": "BD",
"OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)",
"OptionCommunityRating": "Community Rating",
"OptionCriticRating": "Critics Rating",
"OptionDaily": "Daily",
@ -1229,28 +1149,19 @@
"OptionDisplayFolderView": "Display a folder view to show plain media folders",
"OptionDisplayFolderViewHelp": "Display folders alongside your other media libraries. This can be useful if you'd like to have a plain folder view.",
"OptionDvd": "DVD",
"OptionEmbedSubtitles": "Embed within container",
"OptionEnableAccessFromAllDevices": "Enable access from all devices",
"OptionEnableAccessToAllChannels": "Enable access to all channels",
"OptionEnableAccessToAllLibraries": "Enable access to all libraries",
"OptionEnableExternalContentInSuggestions": "Enable external content in suggestions",
"OptionEnableExternalContentInSuggestionsHelp": "Allow internet trailers and live TV programs to be included within suggested content.",
"OptionEnableForAllTuners": "Enable for all tuner devices",
"OptionEnableM2tsMode": "Enable M2TS mode",
"OptionEnableM2tsModeHelp": "Enable M2TS mode when encoding to MPEG-TS.",
"OptionEquals": "Equals",
"OptionEstimateContentLength": "Estimate content length when transcoding",
"OptionEveryday": "Every day",
"OptionExternallyDownloaded": "External download",
"OptionExtractChapterImage": "Enable chapter image extraction",
"OptionForceRemoteSourceTranscoding": "Force transcoding of remote media sources such as Live TV",
"OptionHasThemeSong": "Theme Song",
"OptionHasThemeVideo": "Theme Video",
"OptionHideUser": "Hide this user from login screens",
"OptionHideUserFromLoginHelp": "Useful for private or hidden administrator accounts. The user will need to sign in manually by entering their username and password.",
"OptionHlsSegmentedSubtitles": "HLS segmented subtitles",
"OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "These requests will be honored but will ignore the byte range header.",
"OptionImdbRating": "IMDb Rating",
"OptionIsHD": "HD",
"OptionIsSD": "SD",
@ -1263,27 +1174,16 @@
"OptionNew": "New…",
"OptionOnInterval": "On an interval",
"OptionParentalRating": "Parental Rating",
"OptionPlainStorageFolders": "Display all folders as plain storage folders",
"OptionPlainStorageFoldersHelp": "All folders are represented in DIDL as 'object.container.storageFolder' instead of a more specific type, such as 'object.container.person.musicArtist'.",
"OptionPlainVideoItems": "Display all videos as plain video items",
"OptionPlainVideoItemsHelp": "All videos are represented in DIDL as 'object.item.videoItem' instead of a more specific type, such as 'object.item.videoItem.movie'.",
"OptionPlayCount": "Play Count",
"OptionPremiereDate": "Premiere Date",
"OptionProtocolHls": "HTTP Live Streaming (HLS)",
"OptionProtocolHttp": "HTTP",
"OptionRandom": "Random",
"OptionRegex": "Regex",
"OptionReleaseDate": "Release Date",
"OptionReportByteRangeSeekingWhenTranscoding": "Report that the server supports byte seeking when transcoding",
"OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well.",
"OptionRequirePerfectSubtitleMatch": "Only download subtitles that are a perfect match for video files",
"OptionRequirePerfectSubtitleMatchHelp": "Requiring a perfect match will filter subtitles to include only those that have been tested and verified with your exact video file. Unchecking this will increase the likelihood of subtitles being downloaded, but will increase the chances of mistimed or incorrect subtitle text.",
"OptionResElement": "'res' element",
"OptionResumable": "Resumable",
"OptionSaveMetadataAsHidden": "Save metadata and images as hidden files",
"OptionSaveMetadataAsHiddenHelp": "Changing this will apply to new metadata saved going forward. Existing metadata files will be updated the next time they are saved by the server.",
"OptionSpecialEpisode": "Specials",
"OptionSubstring": "Substring",
"OptionTrackName": "Track Name",
"OptionTvdbRating": "TheTVDB Rating",
"OptionUnairedEpisode": "Unaired Episodes",
@ -1500,14 +1400,10 @@
"Sunday": "Sunday",
"SyncPlayAccessHelp": "The SyncPlay feature enables to sync playback with other devices. Select the level of access this user has to the SyncPlay.",
"SyncPlayGroupDefaultTitle": "{0}'s group",
"SystemDlnaProfilesHelp": "System profiles are read-only. Changes to a system profile will be saved to a new custom profile.",
"TabAccess": "Access",
"TabAdvanced": "Advanced",
"TabCatalog": "Catalog",
"TabCodecs": "Codecs",
"TabContainers": "Containers",
"TabDashboard": "Dashboard",
"TabDirectPlay": "Direct Playback",
"TabLatest": "Recently Added",
"TabLogs": "Logs",
"TabMusic": "Music",
@ -1518,9 +1414,7 @@
"TabOther": "Other",
"TabParentalControl": "Parental Control",
"TabPlugins": "Plugins",
"TabProfiles": "Profiles",
"TabRepositories": "Repositories",
"TabResponses": "Responses",
"TabScheduledTasks": "Scheduled Tasks",
"TabServer": "Server",
"TabStreaming": "Streaming",
@ -1577,10 +1471,6 @@
"UserMenu": "User Menu",
"UserProfilesIntro": "Jellyfin includes support for user profiles with granular display settings, play state, and parental controls.",
"ValueAlbumCount": "{0} albums",
"ValueAudioCodec": "Audio Codec: {0}",
"ValueCodec": "Codec: {0}",
"ValueConditions": "Conditions: {0}",
"ValueContainer": "Container: {0}",
"ValueDiscNumber": "Disc {0}",
"ValueEpisodeCount": "{0} episodes",
"ValueMinutes": "{0} min",
@ -1598,7 +1488,6 @@
"ValueSpecialEpisodeName": "Special - {0}",
"ValueTimeLimitMultiHour": "Time limit: {0} hours",
"ValueTimeLimitSingleHour": "Time limit: 1 hour",
"ValueVideoCodec": "Video Codec: {0}",
"Vertical": "Vertical",
"Video": "Video",
"VideoAudio": "Video Audio",
@ -1614,7 +1503,6 @@
"WriteAccessRequired": "Jellyfin requires write access to this folder. Please ensure write access and try again.",
"Writer": "Writer",
"Writers": "Writers",
"XmlDocumentAttributeListHelp": "These attributes are applied to the root element of every XML response.",
"XmlTvKidsCategoriesHelp": "Programs with these categories will be displayed as programs for children. Separate multiple with '|'.",
"XmlTvMovieCategoriesHelp": "Programs with these categories will be displayed as movies. Separate multiple with '|'.",
"XmlTvNewsCategoriesHelp": "Programs with these categories will be displayed as news programs. Separate multiple with '|'.",

View file

@ -733,7 +733,7 @@
"MessageReenableUser": "Mira abajo para reactivarlo",
"MessageTheFollowingLocationWillBeRemovedFromLibrary": "Se eliminarán las siguientes ubicaciones de medios de tu biblioteca",
"MessageUnableToConnectToServer": "No podemos conectar con el servidor seleccionado ahora mismo. Por favor, asegúrate de que esta funcionando e inténtalo otra vez.",
"MessageUnsetContentHelp": "El contenido se mostrará como carpetas planas. Para tener mejores resultados utiliza el gestor de metadatos para establecer los tipos de contenidos de las sub-carpetas.",
"MessageUnsetContentHelp": "El contenido se mostrará como carpetas simples. Para tener mejores resultados utiliza el gestor de metadatos para establecer los tipos de contenidos de las sub-carpetas.",
"MessageYouHaveVersionInstalled": "Actualmente tienes la versión {0} instalada.",
"Metadata": "Metadatos",
"MetadataManager": "Administrador de Metadatos",
@ -801,7 +801,7 @@
"OptionDisableUserHelp": "El servidor no aceptará conexiones de este usuario. Si existen conexiones de este usuario, finalizarán inmediatamente.",
"OptionDislikes": "No me gusta",
"OptionDisplayFolderView": "Mostrar una vista de carpeta para ver las carpetas de medios en plano",
"OptionDisplayFolderViewHelp": "Mostrar carpetas junto con tus otras bibliotecas de medios. Esto es útil si te gustar tener una vista plana de carpetas.",
"OptionDisplayFolderViewHelp": "Mostrar carpetas junto con tus otras bibliotecas de medios. Esto es útil si te gustar tener una vista simple de carpetas.",
"OptionDownloadImagesInAdvance": "Descargar imágenes con antelación",
"OptionDownloadImagesInAdvanceHelp": "Por defecto, la mayoría de las imágenes solo se descargan cuando son solicitadas por un cliente. Activa esta opción para descargar todas las imágenes por adelantado, a medida que se importan nuevos medios. Esto puede causar escaneos de biblioteca significativamente más largos.",
"OptionEmbedSubtitles": "Integrado con el contenedor",
@ -1781,5 +1781,7 @@
"SelectAudioNormalizationHelp": "Ganancia de pista - ajusta el volumen de cada pista para que suenen al mismo volumen. Ganancia de álbum - ajusta el volumen de todas las pistas de un álbum, manteniendo el rango dinámico del álbum.",
"LabelAlbumGain": "Ganancia de Álbum",
"LabelSelectAudioNormalization": "Normalización de audio",
"LabelTrackGain": "Ganancia de pista"
"LabelTrackGain": "Ganancia de pista",
"HeaderAllRecordings": "Todas las grabaciones",
"ButtonEditUser": "Editar usuario"
}

View file

@ -98,7 +98,7 @@
"LabelCriticRating": "Kriitikute hinne",
"LabelCreateHttpPortMap": "Luba automaatne pordikaardistus nii HTTP kui ka HTTPS liikluse jaoks.",
"LabelCreateHttpPortMapHelp": "Luba automaatsel pordikaardistusel luua lisaks HTTP liiklusele ka HTTPS liikluse reegel.",
"LabelCountry": "Riik",
"LabelCountry": "Riik/piirkond",
"LabelCorruptedFrames": "Rikutud kaadrid",
"LabelContentType": "Sisu tüüp",
"LabelCommunityRating": "Kogukonna hinne",

View file

@ -468,7 +468,7 @@
"LabelSelectAudioChannels": "Mga channel",
"LabelAllowedAudioChannels": "Pinakamataas na Mga Pinahihintulutang Audio Channel",
"AllowHevcEncoding": "Payagan ang pag-encode sa HEVC na format",
"PreferFmp4HlsContainerHelp": "Mas gustong gamitin ang fMP4 bilang default na lalagyan para sa HLS, na ginagawang posible na idirekta ang pag-stream ng HEVC na nilalaman sa mga sinusuportahang device.",
"PreferFmp4HlsContainerHelp": "Mas gustong gamitin ang fMP4 bilang default na lalagyan para sa HLS, na ginagawang posible na idirekta ang pag-stream ng HEVC at AV1 na nilalaman sa mga sinusuportahang device.",
"PreferFmp4HlsContainer": "Mas gusto ang fMP4-HLS Media Container",
"LabelSyncPlayInfo": "Impormasyon ng SyncPlay",
"LabelOriginalMediaInfo": "Orihinal na Impormasyon ng Media",
@ -485,7 +485,7 @@
"AspectRatioCover": "Cover",
"EnableFallbackFontHelp": "I-enable ang mga custom alternate font. Maiiwasan nito ang problema ng maling pag-render ng subtitle.",
"EnableFallbackFont": "I-enable ang mga fallback na font",
"LabelFallbackFontPathHelp": "Tumukoy ng path na naglalaman ng mga fallback na font para sa pag-render ng mga subtitle ng ASS/SSA. Ang maximum na pinapayagang kabuuang laki ng font ay 20 MB. Inirerekomenda ang magaan at web-friendly na mga format ng font gaya ng woff2.",
"LabelFallbackFontPathHelp": "Ang mga fonts na ito ay ginagamit ng ilang kilyente para magrender ng mga subtitulo. Pakiusap at sumangguni sa dokyumentasyon para sa karagdagang impormasyon.",
"LabelFallbackFontPath": "Path ng Fallback font folder",
"HeaderSelectFallbackFontPathHelp": "Mag-browse o ilagay ang path ng fallback font folder na gagamitin para sa pag-render ng mga subtitle ng ASS/SSA.",
"MoveRight": "Lumipat pakanan",
@ -621,7 +621,7 @@
"LabelDisplayOrder": "Order ng Display",
"LabelDisplayName": "Ang Display name",
"LabelDisplayLanguageHelp": "Ang pagsasalin ng Jellyfin ay isang patuloy na proyekto.",
"LabelDisplayLanguage": "Display language",
"LabelDisplayLanguage": "Ipakita ang lingwahe",
"LabelDiscNumber": "Numero ng disc",
"LabelDisableCustomCss": "I-disable ang custom na CSS code para sa theming/branding na ibinigay mula sa server.",
"LabelDidlMode": "Mode ng DIDL",
@ -652,7 +652,7 @@
"LabelCommunityRating": "Rating ng komunidad",
"LabelColorTransfer": "Color transfer",
"LabelColorSpace": "Color space",
"LabelColorPrimaries": "Color primaries",
"LabelColorPrimaries": "Mga primerang kulay",
"LabelCollection": "Koleksyon",
"LabelChromecastVersion": "Bersyon ng Google Cast",
"LabelChannels": "Mga Channel",
@ -714,7 +714,7 @@
"QuickConnectInvalidCode": "Di-wastong Quick Connect code",
"QuickConnectDescription": "Upang mag-sign in gamit ang Quick Connect, piliin ang button na 'Quick Connect' sa device kung saan ka nagla-log in at ilagay ang ipinapakitang code sa ibaba.",
"QuickConnectDeactivated": "Na-deactivate ang Quick Connect bago maaprubahan ang kahilingan sa pag-log in",
"QuickConnectAuthorizeSuccess": "Pinahintulutan ang kahilingan",
"QuickConnectAuthorizeSuccess": "Matagumpay mong na pahitululang ang iyong device!",
"QuickConnectAuthorizeFail": "Hindi kilalang Quick Connect code",
"QuickConnectAuthorizeCode": "Ipasok ang code {0} upang mag-login",
"QuickConnectActivationSuccessful": "Matagumpay na na-activate",
@ -826,7 +826,7 @@
"HeaderResponseProfile": "Response Profile",
"HeaderRemoveMediaLocation": "Alisin ang Lokasyon ng Media",
"HeaderRemoveMediaFolder": "Alisin ang Media Folder",
"HeaderRemoteControl": "Remote Control",
"HeaderRemoteControl": "Remote Kontrol",
"HeaderRemoteAccessSettings": "Mga Setting ng Remote Access",
"HeaderRecordingPostProcessing": "Pagre-record ng Post Processing",
"HeaderRecordingOptions": "Mga Opsyon sa Pagre-record",
@ -958,11 +958,11 @@
"MessageSent": "Naipadala ang mensahe.",
"MessagePluginInstallError": "Nagkaroon ng error habang ini-install ang plugin.",
"MessagePluginInstalled": "Matagumpay na na-install ang plugin. Kailangang i-restart ang server para magkabisa ang mga pagbabago.",
"MessagePluginInstallDisclaimer": "Ang mga plugin na ginawa ng mga miyembro ng komunidad ay isang mahusay na paraan upang mapahusay ang iyong karanasan sa mga karagdagang feature at benepisyo. Bago i-install, mangyaring magkaroon ng kamalayan sa mga epekto na maaaring mayroon sila sa iyong server, tulad ng mas mahabang pag-scan sa library, karagdagang pagproseso sa background, at pagbaba ng katatagan ng system.",
"MessagePluginInstallDisclaimer": "BABALA: Ang pagiinstall ng mga third party plugins ay may kaakibat na peligro. Ito'y maaring maglalaman ng unstable o malisyosong code, at pwedeng magbago anumang oras. Mag install lamang ng mga plugins galing sa mga may-akdang iyong pinagkakatiwalaan, at pakiusap na alamin ang mga posibleng epekto na kaakibat nito, kasama ang panlabas na service queries, mas matagal na scans ng aklatan, o karagdagan background processing.",
"MessagePluginConfigurationRequiresLocalAccess": "Upang i-set up ang plugin na ito mangyaring mag-sign in nang direkta sa iyong lokal na server.",
"MessagePleaseEnsureInternetMetadata": "Pakitiyak na ang pag-download ng metadata sa internet ay maka-enable.",
"MessagePlayAccessRestricted": "Ang pag-playback ng nilalamang ito ay kasalukuyang pinaghihigpitan. Mangyaring makipag-ugnayan sa administrator ng iyong server para sa higit pang impormasyon.",
"MessagePasswordResetForUsers": "Na-reset ng mga sumusunod na user ang kanilang mga password. Maaari na silang mag-sign in gamit ang Easy PIN code na ginamit para gawin ang pag-reset.",
"MessagePasswordResetForUsers": "Na-reset ng mga sumusunod na user ang kanilang mga password. Maaari na silang mag-sign in gamit ang PIN code na ginamit para gawin ang pag-reset.",
"MessageNoTrailersFound": "I-install ang trailers channel upang mapahusay ang iyong karanasan sa pelikula sa pamamagitan ng pagdaragdag ng library ng mga trailer sa internet.",
"MessageNothingHere": "Walang nakalagay dito.",
"MessageNoServersAvailable": "Walang nahanap na mga server gamit ang awtomatikong pagtuklas ng server.",
@ -1084,7 +1084,7 @@
"LabelRefreshMode": "Mode ng pag-refresh",
"LabelRecordingPathHelp": "Tukuyin ang default na lokasyon upang i-save ang mga pag-record. Kung iniwang walang laman, gagamitin ang folder ng data ng programa ng server.",
"LabelRecordingPath": "Default na path ng pag-record",
"LabelRecord": "Record",
"LabelRecord": "Rekord",
"LabelReasonForTranscoding": "Dahilan ng transcoding",
"LabelQuickConnectCode": "Quick Connect code",
"LabelPublishedServerUriHelp": "I-override ang URI na ginagamit ng Jellyfin, batay sa interface, o IP address ng kliyente.",
@ -1098,7 +1098,7 @@
"LabelProtocol": "Protocol",
"LabelProfileVideoCodecs": "Mga Video codec",
"LabelProfileContainersHelp": "Pinaghiwalay ng kuwit. Maaari itong iwanang walang laman upang mailapat sa lahat ng container.",
"LabelProfileContainer": "Container",
"LabelProfileContainer": "Lalagyan",
"LabelProfileCodecsHelp": "Pinaghiwalay ng kuwit. Maaari itong iwanang walang laman upang mailapat sa lahat ng mga codec.",
"LabelProfileCodecs": "Mga codec",
"LabelProfileAudioCodecs": "Codecs ng Audio",
@ -1116,13 +1116,13 @@
"LabelPlaceOfBirth": "Lugar ng kapanganakan",
"LabelPersonRoleHelp": "Halimbawa: Ice cream truck driver",
"LabelPersonRole": "Role",
"LabelPath": "Path",
"LabelPath": "Pagdaraanan",
"LabelPasswordResetProvider": "Provider ng Password Reset",
"LabelPasswordRecoveryPinCode": "PIN code",
"LabelPasswordConfirm": "Password (kumpirmahin)",
"LabelPassword": "Password",
"LabelParentNumber": "Numbero ng Parent",
"LabelParentalRating": "Parental rating",
"LabelParentalRating": "Rating ng patnubay",
"LabelOverview": "Pangkalahatang-ideya",
"LabelOriginalTitle": "Orihinal na pamagat",
"LabelOriginalName": "Orihinal na pangalan",
@ -1159,7 +1159,7 @@
"LabelMinBackdropDownloadWidth": "Minimum na lapad ng pag-download ng backdrop",
"LabelMinAudiobookResumeHelp": "Ang mga pamagat ay ipinapalagay na hindi nai-play kung huminto bago ang oras na ito.",
"LabelMinAudiobookResume": "Minimum sa pag-resume ng Audiobook sa ilang minuto",
"LabelMethod": "Method",
"LabelMethod": "Metodolohiya",
"LabelMetadataSaversHelp": "Piliin ang mga format ng file na gagamitin kapag sine-save ang iyong metadata.",
"LabelMetadataSavers": "Mga metadata saver",
"LabelMetadataReadersHelp": "I-rank ang iyong ginustong lokal na pinagmumulan ng metadata ayon sa priyoridad. Ang unang file na natagpuan ay babasahin.",
@ -1179,12 +1179,12 @@
"LabelMaxParentalRating": "Pinakamataas na pinapayagang parental rating",
"LabelMaxMuxingQueueSizeHelp": "Maximum na bilang ng mga packet na maaaring i-buffer habang naghihintay na magsimula ang lahat ng stream. Subukang dagdagan ito kung matugunan mo pa rin ang error na \"Too many packets buffered for output stream\" sa mga log ng FFmpeg. Ang inirerekomendang halaga ay 2048.",
"LabelMaxMuxingQueueSize": "Size ng Max muxing queue",
"LabelMaxChromecastBitrate": "Google Cast streaming quality",
"LabelMaxChromecastBitrate": "Kalidad ng Google Cast streaming",
"LabelMaxBackdropsPerItem": "Maximum na bilang ng mga backdrop bawat item",
"LabelMaxAudiobookResume": "Natitirang minuto ng Audiobook upang ma-resume",
"LabelMatchType": "Uri ng Match",
"LabelManufacturerUrl": "URL ng Manufacturer",
"LabelManufacturer": "Manufacturer",
"LabelManufacturer": "Pabrikante",
"LabelLogs": "Mga log",
"LabelLoginDisclaimerHelp": "Isang mensahe na ipapakita sa ibaba ng pahina ng pag-login.",
"LabelLoginDisclaimer": "Disclaimer sa pag-login",
@ -1275,8 +1275,8 @@
"ErrorAddingMediaPathToVirtualFolder": "Nagkaroon ng error sa pagdaragdag ng media path. Pakitiyak na wasto ang landas at may access ang Jellyfin sa lokasyong iyon.",
"ErrorAddingListingsToSchedulesDirect": "Nagkaroon ng error sa pagdaragdag ng lineup sa iyong Schedules Direct account. Nagbibigay-daan lang ang Schedules Direct ng limitadong bilang ng mga lineup sa bawat account. Maaaring kailanganin mong mag-log in sa Schedules Direct website at alisin ang iba pang mga listahan mula sa iyong account bago magpatuloy.",
"Episodes": "Mga episode",
"Episode": "Episode",
"Engineer": "Sound engineer",
"Episode": "Episodyo",
"Engineer": "Inhinyerong pantunog",
"EndsAtValue": "Magtatapos sa {0}",
"Ended": "Natapos na",
"EncoderPresetHelp": "Pumili ng isang mas mabilis na halaga upang mapabuti ang pagganap, o isang mas mabagal na halaga upang mapabuti ang kalidad.",
@ -1292,20 +1292,20 @@
"EnableFasterAnimations": "Mas mabilis na mga animation",
"EnableExternalVideoPlayersHelp": "Lalabas ang external player menu kapag sinisimulan ang pag-playback ng video.",
"EnableExternalVideoPlayers": "Mga external na video player",
"EnableDisplayMirroring": "Display mirroring",
"EnableDisplayMirroring": "Pagsasalamin ng Display",
"EnableDetailsBannerHelp": "Magpakita ng larawan ng banner sa tuktok ng pahina ng mga detalye ng item.",
"EnableDetailsBanner": "Banner ng Mga Detalye",
"EnableColorCodedBackgrounds": "Mga background na may color code",
"EnableCinemaMode": "Cinema mode",
"EnableCinemaMode": "Takada ng takilya",
"EnableBlurHashHelp": "Ang mga larawang nilo-load pa rin ay ipapakita na may natatanging placeholder.",
"EnableBackdropsHelp": "Ipakita ang mga backdrop sa background ng ilang page habang nagba-browse sa library.",
"EnableAutoCast": "Itakda bilang Default",
"EnableAutoCast": "Itakda bilang default",
"EditSubtitles": "I-edit ang mga subtitle",
"EditMetadata": "I-edit ang metadata",
"EditImages": "I-edit ang mga larawan",
"Edit": "I-edit",
"EasyPasswordHelp": "Ang iyong Easy PIN code ay ginagamit para sa offline na pag-access sa mga sinusuportahang kliyente at maaari ding gamitin para sa madaling pag-sign in sa network.",
"DropShadow": "Drop Shadow",
"DropShadow": "Lalim ng Anino",
"DrmChannelsNotImported": "Hindi mai-import ang mga channel na may DRM.",
"DownloadsValue": "{0} (na) pag-download",
"Download": "I-download",
@ -1315,13 +1315,13 @@
"DisplayMissingEpisodesWithinSeasons": "Ipakita ang mga nawawalang episode sa loob ng mga season",
"DisplayInOtherHomeScreenSections": "Magpakita sa home screen ng mga seksyon gaya ng 'Bagong Media' at 'Ituloy ang Panonood'",
"DisplayInMyMedia": "Ipakita sa home screen",
"Display": "Display",
"Display": "Ipakita",
"Disconnect": "Idiskonekta",
"Disc": "Disc",
"DirectStreaming": "Direktang streaming",
"DirectStreamHelp2": "Ang power na na-consume sa pamamagitan ng direktang streaming ay karaniwang nakadepende sa audio profile. Tanging ang video stream ay lossless.",
"DirectStreamHelp1": "Ang video stream ay tugma sa device, ngunit may hindi tugmang format ng audio (DTS, Dolby TrueHD, atbp.) o bilang ng mga audio channel. Ang video stream ay ire-repack nang walang pagkawala sa mabilisang bago ipadala sa device. Tanging ang audio stream lang ang ita-transcode.",
"DirectPlayHelp": "Ang source file ay ganap na tugma sa client na ito, at ang session ay tumatanggap ng file nang walang pagbabago.",
"DirectPlayHelp": "",
"DirectPlaying": "Direktang pag-play",
"Directors": "Mga direktor",
"Director": "Direktor",
@ -1329,7 +1329,7 @@
"DetectingDevices": "Pag-detect ng mga device",
"Desktop": "Desktop",
"Descending": "Pababa",
"Depressed": "Depressed",
"Depressed": "Banguwas",
"DeleteUserConfirmation": "Sigurado ka bang gusto mong tanggalin ang user na ito?",
"DeleteUser": "Tanggalin ang User",
"DeleteMedia": "Tanggalin ang media",
@ -1366,10 +1366,10 @@
"ConfirmDeleteImage": "Tanggalin ang larawan?",
"ConfigureDateAdded": "I-set up kung paano tinutukoy ang metadata para sa 'Petsa ng idinagdag' sa Dashboard > Mga Library > Mga Setting ng NFO",
"Conductor": "Konduktor",
"Composer": "Composer",
"Composer": "Kompositór",
"CommunityRating": "Rating ng komunidad",
"ColorPrimaries": "Color primaries",
"ColorSpace": "Color space",
"ColorPrimaries": "Mga primerong kulay",
"ColorSpace": "Espasyo ng Kulay",
"ColorTransfer": "Color transfer",
"ClientSettings": "Mga Setting ng Client",
"ClearQueue": "I-clear ang queue",
@ -1440,7 +1440,7 @@
"LabelVideoRange": "Range ng video",
"LabelVideoCodec": "Codec ng video",
"LabelVideoBitrate": "Bitrate ng video",
"LabelValue": "Value",
"LabelValue": "Halaga",
"LabelVaapiDeviceHelp": "Ito ang render node na ginagamit para sa hardware acceleration.",
"LabelVaapiDevice": "Device ng VA-API",
"LabelUserRemoteClientBitrateLimitHelp": "I-override ang default na global value na itinakda sa mga setting ng server, tingnan ang Dashboard > Pag-playback > Streaming.",
@ -1503,7 +1503,7 @@
"LabelSyncPlaySettingsSyncCorrectionHelp": "Paganahin ang aktibong pag-sync ng playback sa pamamagitan ng pagpapabilis ng media o sa pamamagitan ng paghahanap sa tinantyang posisyon. Huwag paganahin ito sa kaso ng matinding pagkautal.",
"LabelSyncPlaySettingsSyncCorrection": "Pagwawasto sa Sync",
"LabelSyncPlaySettingsExtraTimeOffsetHelp": "Manu-manong isaayos ang time offset (sa ms) gamit ang napiling device para sa time sync. Tweak with care.",
"LabelSyncPlaySettingsExtraTimeOffset": "Extra time offset",
"LabelSyncPlaySettingsExtraTimeOffset": "Karagdagang time offset",
"LabelSyncPlaySettingsDescription": "Baguhin ang mga kagustuhan sa SyncPlay",
"LabelSyncPlayTimeSyncOffset": "Time offset",
"LabelSyncPlayTimeSyncDevice": "Pag-sync ng oras sa",
@ -1540,7 +1540,7 @@
"ButtonPreviousTrack": "Previous na track",
"ButtonRefreshGuideData": "I-refresh ang Guide Data",
"ButtonQuickStartGuide": "Gabay sa Mabilis na Pagsisimula",
"ButtonPlayer": "Player",
"ButtonPlayer": "Pampatugtug",
"ButtonPause": "I-pause",
"ButtonOpen": "Buksan",
"ButtonOk": "Ok",
@ -1573,7 +1573,7 @@
"BoxRear": "Kahon (likod)",
"Box": "Kahon",
"BookLibraryHelp": "Ang mga audio at text book ay suportado. Suriin ang {0} gabay sa pagpapangalan ng aklat {1}.",
"Blacklist": "Blacklist",
"Blacklist": "Di Pinahihintulutan",
"BirthPlaceValue": "Lugar ng kapanganakan: {0}",
"BirthLocation": "Lokasyon ng kapanganakan",
"BirthDateValue": "Ipinanganak: {0}",
@ -1583,19 +1583,19 @@
"Auto": "Auto",
"AuthProviderHelp": "Pumili ng provider ng pagpapatotoo na gagamitin upang patotohanan ang password ng user na ito.",
"Authorize": "Pahintulutan",
"Audio": "Audio",
"AspectRatio": "Aspect Ratio",
"Audio": "Odyo",
"AspectRatio": "Aspektong Bahagimbilang",
"AsManyAsPossible": "Bilang marami hangga't maaari",
"AskAdminToCreateLibrary": "Hilingin sa isang administrator na gumawa ng library.",
"Ascending": "Paakyat",
"Artist": "Artista",
"Art": "Clearart",
"Arranger": "Arranger",
"Arranger": "Nagsisi-ayos",
"AroundTime": "Sa paligid ng {0}",
"ApiKeysCaption": "Listahan ng mga kasalukuyang naka-enable na API key",
"Anytime": "Kahit kailan",
"AnyLanguage": "Anumang wika",
"AllowTonemappingHelp": "Maaaring baguhin ng tone-mapping ang dynamic range ng isang video mula sa HDR patungong SDR habang pinapanatili ang mga detalye at kulay ng larawan, na napakahalagang impormasyon para sa kumakatawan sa original scene. Kasalukuyang gumagana lang sa mga HDR10 o HLG na video. Nangangailangan ito ng kaukulang OpenCL o CUDA runtime.",
"AllowTonemappingHelp": "Ang Tone-mapping ay may kakahayang baguhin ang dynamic range ng isang bidyo mula HDR papuntang SDR habang minimintina ang detalye at kulay ng imahe, alin may napakaimportanteng impormasyon para sa pagrerepresenta ng orihinal na senaryo. Kasalukuyang gumagana lamang sa HDR10, HLG at DoVi na mga bidyo. Ito'y nangangailangan ng koresponding OpenCL at Cuda runtime.",
"AgeValue": "({0} taong gulang)",
"LabelHardwareEncodingOptions": "Mga opsyon sa hardware encoding",
"IntelLowPowerEncHelp": "Maaaring panatilihin ng Low-Power Encoding ang hindi kinakailangang pag-sync ng CPU at GPU. Sa Linux dapat ito ay naka-disable kung ang i915 HuC firmware ay hindi na-configure.",
@ -1632,12 +1632,12 @@
"EnableCardLayout": "Magpakita ng cardbox",
"ButtonBackspace": "Backspace",
"ButtonClose": "Isara",
"ButtonSpace": "Space",
"ButtonSpace": "Puwang",
"GoogleCastUnsupported": "Hindi suportado ang Google Cast",
"Copied": "Nakopya",
"Copy": "Kopyahin",
"CopyFailed": "Di makopya",
"Digital": "Digital",
"Digital": "Dihital",
"DownloadAll": "Idownload ang lahat",
"HeaderDummyChapter": "Mga larawan kada kabanata",
"AddToFavorites": "Idagdag sa mga paborito",
@ -1658,7 +1658,7 @@
"SubtitleWhite": "Puti",
"SubtitleYellow": "Dilaw",
"HeaderRecordingMetadataSaving": "Pagre-record ng Metadata",
"LabelDummyChapterDurationHelp": "Ang agwat ng pagkuha ng larawan ng kabanata sa segundo.",
"LabelDummyChapterDurationHelp": "Ang agwat ng bawat dummy chapters. I-set ng 0 para i-disable ang pag generate ng dummy chapter. Ang pagbago nito ay walang epekto sa nakairal ng dummy chapters.",
"HomeVideosPhotos": "Mga Videos at Larawan sa Home",
"IgnoreDts": "Ibalewala ang DTS (pag-decode ng timestamp)",
"IgnoreDtsHelp": "Ang pag-disable ng option na ito ay maaring maglutas ng ilang isyu, hal. nawawalang audio sa mga channel na may magkahiwalay na audio at video streams.",
@ -1673,5 +1673,6 @@
"LabelThrottleDelaySeconds": "I-throttle pagkatapos",
"LabelAlbumGain": "Gain na Album",
"LabelThrottleDelaySecondsHelp": "Oras sa segundo pagkatapos babagal ang transcoder. Kailangan sapat ang laki upang mapanatili ng kliyente ang isang malusog ang buffer. Gumagana lang kung naka-enable ang throttling.",
"LabelSegmentKeepSeconds": "Oras upang panatilihin ang mga segmenta"
"LabelSegmentKeepSeconds": "Oras upang panatilihin ang mga segmenta",
"BackdropScreensaver": "Backdrop Screensaver"
}

View file

@ -449,7 +449,7 @@
"LabelCollection": "Collection",
"LabelCommunityRating": "Note de la communauté",
"LabelContentType": "Type de contenu",
"LabelCountry": "Pays",
"LabelCountry": "Pays/Région",
"LabelCriticRating": "Note des critiques",
"LabelCurrentPassword": "Mot de passe actuel",
"LabelCustomCertificatePath": "Chemin vers le certificat SSL personnalisé",
@ -1782,5 +1782,10 @@
"LabelAlbumGain": "Gain de l'album",
"LabelSelectAudioNormalization": "Normalisation de l'audio",
"LabelTrackGain": "Gain de piste",
"HeaderAllRecordings": "Tous les enregistrements"
"HeaderAllRecordings": "Tous les enregistrements",
"LabelBuildVersion": "Numéro de build",
"LabelServerVersion": "Version du serveur",
"LabelWebVersion": "Version web",
"ButtonEditUser": "Modifier l'utilisateur",
"DlnaMovedMessage": "La fonctionnalité DLNA a été déplacée vers un plugin."
}

26
src/strings/ga.json Normal file
View file

@ -0,0 +1,26 @@
{
"Actor": "Aisteoir",
"Add": "Cuir le",
"AddedOnValue": "Curtha {0}",
"AddToFavorites": "Cur le ceanáin",
"AgeValue": "({0} bliana d'aois)",
"AirDate": "Dáta Croalta",
"Aired": "Craolta",
"AlbumArtist": "Ealaíontóir Albam",
"Albums": "Albaim",
"Alerts": "Foláirimh",
"AllChannels": "Gach cainéal",
"AllLanguages": "Gach teanga",
"AllEpisodes": "Gach eagrán",
"AllLibraries": "Gach leabharlann",
"AllowCollectionManagement": "Lig don úsáideoir seo bailiúcháin a bhainistiú",
"Absolute": "Absalóideach",
"AccessRestrictedTryAgainLater": "Tá rochtain srianta faoi láthair. Bain triail eile as ar ball.",
"All": "Uilig",
"Album": "Albam",
"AddToCollection": "Cur le bailiúcháin",
"AddToPlaylist": "Cur le seinnliosta",
"AddToPlayQueue": "Cur le scuaine seinnteoir",
"AllComplexFormats": "Gach formáid chasta (ASS, SSA, VobSub, PGS, SUB, IDX, ...)",
"AllowedRemoteAddressesHelp": "Liosta scartha camóg de seoltaí IP nó iontráil IP/netmask do líonraí a ligfear nascadh go cianda. Má fhágtar folamh é, ceadófar gach ciansheoladh."
}

View file

@ -135,7 +135,7 @@
"LabelCollection": "אוספים",
"LabelCommunityRating": "דירוג הקהילה",
"LabelContentType": "סוג התוכן",
"LabelCountry": "מדינה",
"LabelCountry": "מדינה/אזור",
"LabelCriticRating": "דירוג מבקרים",
"LabelCurrentPassword": "סיסמא נוכחית",
"LabelCustomCss": "CSS מותאם אישית",
@ -1216,5 +1216,33 @@
"LabelSelectAudioNormalization": "נרמול אודיו",
"LabelStereoDownmixAlgorithm": "אלגוריתם עירבול מטה לסטריאו",
"LabelEncoderPreset": "חבילת קידוד",
"LabelEnableLUFSScanHelp": "לקוחות יכולים לנרמל את ניגון האודיו בכדי לקבל עצמה זהה בין שירים. זה יגרום להארכת משך סריקת הספרייה ולצריכת משאבים מוגברת."
"LabelEnableLUFSScanHelp": "לקוחות יכולים לנרמל את ניגון האודיו בכדי לקבל עצמה זהה בין שירים. זה יגרום להארכת משך סריקת הספרייה ולצריכת משאבים מוגברת.",
"Bold": "מודגש",
"LabelSubtitleFormatHelp": "למשל: srt",
"LabelSerialNumber": "מספר סידורי",
"LabelStreamType": "סוג הזרמה",
"LabelTextWeight": "עובי טקסט",
"LabelSkipBackLength": "אורך דילוג אחורה",
"LabelSportsCategories": "קטגוריות ספורט",
"LabelSubtitleDownloaders": "מורידי כתוביות",
"LabelSupportedMediaTypes": "סוגי מדיה נתמכים",
"LabelSkipForwardLength": "אורך דילוג קדימה",
"LabelSortBy": "מיון לפי",
"LabelPreferredSubtitleLanguage": "שפת כתוביות מועדפת",
"LabelSyncPlaySettingsSyncCorrection": "תיקון סנכרון",
"LabelProfileCodecsHelp": "מופרד בפסיקים. אפשר להשאיר ריק בשביל כל המפענחים.",
"LabelRequireHttps": "אכיפת HTTPS",
"LabelRepositoryUrl": "כתובת המאגר",
"LabelRepositoryName": "שם המאגר",
"LabelSubtitlePlaybackMode": "מצב כתוביות",
"LabelSubtitleVerticalPosition": "מקום אנכי",
"LabelSyncPlayNoGroups": "אין קבוצות זמינות",
"LabelSyncPlayTimeSyncDevice": "סנכרון הזמן מול",
"LabelSyncPlaySyncMethod": "שיטת סנכרון",
"LabelSimultaneousConnectionLimit": "מגבלת תזרימים מקביליים",
"LabelSelectVersionToInstall": "נא לבחור גרסה להתקנה",
"LabelTextBackgroundColor": "צבע רקע טקסט",
"LabelSystem": "מערכת",
"LabelProfileContainersHelp": "מופרד בפסיקים. אפשר להשאיר ריק בשביל כל המכולות.",
"LabelServerVersion": "גרסת שרת"
}

View file

@ -56,7 +56,7 @@
"UnsupportedPlayback": "Jellyfin DRM द्वारा संरक्षित सामग्री को डिक्रिप्ट नहीं कर सकता है, लेकिन सभी सामग्री की परवाह किए बिना, संरक्षित शीर्षकों सहित प्रयास किया जाएगा। एन्क्रिप्शन या अन्य असमर्थित सुविधाओं जैसे इंटरेक्टिव शीर्षक के कारण कुछ फाइलें पूरी तरह से काली दिखाई दे सकती हैं।",
"BoxRear": "बॉक्स (पीछे)",
"Box": "बॉक्स",
"Books": "पुस्तकं",
"Books": "पुस्तकं",
"BookLibraryHelp": "ऑडियो और पाठ्य पुस्तकें समर्थित हैं। {0} पुस्तक नामकरण गाइड {1} की समीक्षा करें।",
"Blacklist": "काला सूची में डालना",
"BirthPlaceValue": "जन्म स्थान: {0}",
@ -72,7 +72,7 @@
"AskAdminToCreateLibrary": "लाइब्रेरी बनाने के लिए किसी व्यवस्थापक से पूछें।",
"Ascending": "आरोही",
"AsManyAsPossible": "जितने अधिक संभव हों",
"Artists": "कलाकारों",
"Artists": "कलाकार",
"Artist": "कलाकार",
"Art": "कला",
"AroundTime": "लगभग {0}",
@ -80,10 +80,10 @@
"AnyLanguage": "कोई भी भाषा",
"AlwaysPlaySubtitlesHelp": "भाषा की वरीयता से मेल खाने वाले उपशीर्षक ऑडियो भाषा की परवाह किए बिना लोड किए जाएंगे।",
"AlwaysPlaySubtitles": "हमेशा खेलो",
"AllowedRemoteAddressesHelp": "कोमा ने नेटवर्क के लिए आईपी पते या आईपी / नेटमास्क प्रविष्टियों की सूची को अलग कर दिया है जिन्हें दूरस्थ रूप से कनेक्ट करने की अनुमति दी जाएगी। यदि खाली छोड़ दिया जाता है, तो सभी दूरस्थ पते की अनुमति दी जाएगी।",
"AllowedRemoteAddressesHelp": "नेटवर्क के लिए आईपी पतों आईपी/नेटमास्क एंट्रीज़ की कॉमा विभाजित सूची जो रिमोट रूप से कनेक्ट करने की अनुमति देगा। यदि खाली छोड़ा गया है, तो सभी रिमोट पतों को अनुमति दी जाएगी।",
"AllowRemoteAccessHelp": "अनियंत्रित होने पर, सभी दूरस्थ कनेक्शन अवरुद्ध हो जाएंगे।",
"AllowRemoteAccess": "इस सर्वर को असमीप संपर्क की अनुमति दें",
"AllowFfmpegThrottlingHelp": "जब क ट्रांसकोड या रीमक्स वर्तमान प्लेबैक स्थिति से काफी आगे हो जाता है, तो प्रक्रिया को रोकें ताकि यह कम संसाधनों का उपभोग करेगा। अक्सर मांग किए बिना देखने पर यह सबसे उपयोगी है। यदि आप प्लेबैक समस्याओं का अनुभव करते हैं तो इसे बंद कर दें।",
"AllowFfmpegThrottlingHelp": "जब ोई ट्रांसकोड या रीमक्स वर्तमान प्लेबैक स्थिति से काफी आगे निकल जाता है, तो प्रक्रिया को रोकें ताकि यह कम संसाधनों का उपभोग करे। अक्सर बिना खोजे देखते समय यह सबसे उपयोगी होता है। यदि आप प्लेबैक समस्याओं का अनुभव करते हैं तो इसे बंद कर दें।",
"AllowFfmpegThrottling": "थ्रोटल ट्रांसकोड",
"AllowOnTheFlySubtitleExtractionHelp": "वीडियो ट्रांसकोडिंग को रोकने में मदद करने के लिए एंबेडेड सबटाइटल वीडियो से निकाले जा सकते हैं और सादे पाठ में ग्राहकों तक पहुंचाए जाते हैं। कुछ प्रणालियों पर यह एक लंबा समय ले सकता है और निष्कर्षण प्रक्रिया के दौरान वीडियो प्लेबैक को स्टाल करने का कारण बन सकता है। जब वे क्लाइंट डिवाइस द्वारा मूल रूप से समर्थित नहीं होते हैं, तो वीडियो ट्रांसकोडिंग के साथ जले हुए एम्बेडेड उपशीर्षक को अक्षम करें।",
"AlbumArtist": "एल्बम कलाकार",
@ -96,13 +96,13 @@
"BurnSubtitlesHelp": "निर्धारित करता है कि वीडियो ट्रांसकोडिंग करते समय सर्वर को उपशीर्षक बर्न-इन करना चाहिए। इससे बचने से प्रदर्शन में बहुत सुधार होगा। छवि आधारित उपशीर्षक (VOBSUB, PGS, SUB, IDX, …) एवं ASS अथवा SSA जैसे उपशीर्षक बर्न-इन करने के लिए ऑटो का चयन करें।",
"ButtonRemove": "हटाना",
"ButtonOpen": "खोलो",
"HeaderContinueWatching": "देखते रहिए",
"HeaderContinueWatching": "देखना जारी रखें",
"HeaderAlbumArtists": "एल्बम कलाकार",
"Genres": "शैल",
"Folders": "फ़ोल्डरें",
"Genres": "शैलियां",
"Folders": "फ़ोल्डर",
"Favorites": "पसंदीदा",
"Default": "प्राथमिक",
"Collections": "संग्रहों",
"Collections": "संग्रह",
"Channels": "चैनल",
"Movies": "फ़िल्म",
"ButtonActivate": "सक्रिय",
@ -151,9 +151,11 @@
"Shows": "शो",
"ValueSpecialEpisodeName": "विशेष - {0}",
"Sync": "समाकलयति",
"AllowCollectionManagement": "इस यूजर को कलेक्शन परिवर्तन करने की अनुमति दें",
"AllowCollectionManagement": "इस यूजर को संग्रह प्रबंधित करने की अनुमति दें",
"AllowSegmentDeletion": "खंड हटाएँ",
"AllowSegmentDeletionHelp": "क्लाइंट को भेजे जाने के बाद पुराने सेगमेंट हटा दें। यह संपूर्ण ट्रांसकोड की गई फ़ाइल को डिस्क पर स्टोर करने से रोकता है। केवल थ्रॉटलिंग सक्षम होने पर ही काम करेगा। यदि आप प्लेबैक समस्याओं का अनुभव करते हैं तो इसे बंद कर दें।",
"LabelThrottleDelaySeconds": "थ्रॉटर बाद",
"LabelSegmentKeepSeconds": "सेगमेंट रखने का समय"
"LabelSegmentKeepSeconds": "सेगमेंट रखने का समय",
"LabelThrottleDelaySecondsHelp": "सेकंड में समय जिसके बाद ट्रांसकोडर को थ्रॉटल कर दिया जाएगा। क्लाइंट के लिए स्वस्थ बफ़र बनाए रखने के लिए पर्याप्त बड़ा होना चाहिए। केवल तभी काम करता है जब थ्रॉटलिंग सक्रिय हो।",
"LabelSegmentKeepSecondsHelp": "किन खंडों को अधिलेखित करने से पहले रखा जाना चाहिए, इसका समय सेकंड में। \"थ्रोटल आफ्टर\" से बड़ा होना चाहिए। केवल तभी काम करता है जब खंड हटाना सक्रिय हो।"
}

View file

@ -294,7 +294,7 @@
"LabelCollection": "Kolekcija",
"LabelCommunityRating": "Ocjene zajednice",
"LabelContentType": "Tip sadržaja",
"LabelCountry": "Zemlja",
"LabelCountry": "Zemlja/Regija",
"LabelCriticRating": "Ocjena kritičara",
"LabelCurrentPassword": "Trenutna lozinka",
"LabelCustomCss": "Prilagođeni CSS kod",
@ -1512,5 +1512,8 @@
"ShowYear": "Prikaži godinu",
"LabelLocalCustomCss": "Prilagođeni CSS kod za stil koji se odnosi samo na ovog klijenta. Možda ćete htjeti onemogućiti prilagođeni CSS kod poslužitelja.",
"LabelPlayerDimensions": "Dimenzije izvođača",
"LabelSegmentKeepSecondsHelp": "Vrijeme u sekundama za koje se segmenti trebaju čuvati prije nego što se prebrišu. Mora biti veći od \"Throttle after\". Radi samo ako je omogućeno brisanje segmenta."
"LabelSegmentKeepSecondsHelp": "Vrijeme u sekundama za koje se segmenti trebaju čuvati prije nego što se prebrišu. Mora biti veći od \"Throttle after\". Radi samo ako je omogućeno brisanje segmenta.",
"LabelKnownProxies": "Poznati proxy-i",
"ButtonBackspace": "Backspace",
"LabelHomeScreenSectionValue": "{0}. odjeljak početne"
}

View file

@ -170,7 +170,7 @@
"LabelCollection": "Gyűjtemény",
"LabelCommunityRating": "Közösségi értékelés",
"LabelContentType": "Tartalom típusa",
"LabelCountry": "Ország",
"LabelCountry": "Ország/Régió",
"LabelCriticRating": "Kritikusok értékelése",
"LabelCurrentPassword": "Jelenlegi jelszó",
"LabelCustomDeviceDisplayNameHelp": "Adj meg egy egyedi nevet, vagy hagyd üresen a készülék által elküldött név használatához.",
@ -1781,5 +1781,6 @@
"LabelTrackGain": "Címnyereség",
"ForeignPartsOnly": "Kényszerített/külföldi alkatrészek",
"HearingImpairedShort": "HI/SDH",
"HeaderGuestCast": "Vendégsztárok"
"HeaderGuestCast": "Vendégsztárok",
"HeaderAllRecordings": "Összes Felvétel"
}

View file

@ -53,7 +53,7 @@
"ButtonBack": "Indietro",
"ButtonCancel": "Annulla",
"ButtonChangeServer": "Cambia Server",
"ButtonEditOtherUserPreferences": "Modifica questo utente di profilo, l'immagine e le preferenze personali.",
"ButtonEditOtherUserPreferences": "Modifica il profilo, l'immagine e le preferenze personali di questo utente.",
"ButtonForgotPassword": "Password Dimenticata",
"ButtonFullscreen": "Schermo Intero",
"ButtonGotIt": "Ho capito",
@ -74,16 +74,16 @@
"ButtonResume": "Riprendi",
"ButtonRevoke": "Revoca",
"ButtonScanAllLibraries": "Scansiona Tutte le Librerie",
"ButtonSelectDirectory": "Seleziona cartella",
"ButtonSelectDirectory": "Seleziona Cartella",
"ButtonSelectView": "Seleziona vista",
"ButtonSend": "Invia",
"ButtonShutdown": "Arresta Server",
"ButtonSignIn": "Accedi",
"ButtonSignOut": "Esci",
"ButtonStart": "Avvio",
"ButtonStart": "Avvia",
"ButtonSubmit": "Invia",
"ButtonUninstall": "Disinstalla",
"ButtonWebsite": "Web",
"ButtonWebsite": "Sito Web",
"CancelRecording": "Annulla la registrazione",
"CancelSeries": "Annulla Serie TV",
"Categories": "Categorie",
@ -431,7 +431,7 @@
"LabelCollection": "Collezione",
"LabelCommunityRating": "Voto del pubblico",
"LabelContentType": "Tipo di contenuto",
"LabelCountry": "Nazione",
"LabelCountry": "Nazione/Regione",
"LabelCriticRating": "Voto della critica",
"LabelCurrentPassword": "Password corrente",
"LabelCustomCertificatePath": "Percorso personalizzato certificato SSL",
@ -1114,7 +1114,7 @@
"Box": "Scatola",
"ButtonInfo": "Info",
"ButtonOk": "Ok",
"ButtonStop": "Stop",
"ButtonStop": "Ferma",
"ButtonTrailer": "Trailer",
"ChangingMetadataImageSettingsNewContent": "I cambiamenti alle impostazioni dei download dei metadati verranno applicati solamente ai nuovi contenuti aggiunti alla libreria. Per applicare i cambiamenti ai titoli già esistenti devi ricaricare i metadati manualmente.",
"DownloadsValue": "{0} scaricati",
@ -1782,5 +1782,9 @@
"AiTranslated": "Traduzione con AI",
"HeaderGuestCast": "Personaggi Famosi",
"SearchResultsEmpty": "Ci dispiace! Nessun risultato trovato per \"{0}\"",
"HeaderAllRecordings": "Tutte le registrazioni"
"HeaderAllRecordings": "Tutte le registrazioni",
"LabelBuildVersion": "Versione Compilata",
"LabelServerVersion": "Versione server",
"LabelWebVersion": "Versione web",
"ButtonEditUser": "Modifica utente"
}

View file

@ -729,7 +729,7 @@
"LabelCollection": "コレクション",
"LabelCommunityRating": "コミュニティ評価",
"LabelContentType": "コンテンツタイプ",
"LabelCountry": "国",
"LabelCountry": "国/地域",
"LabelDashboardTheme": "サーバーダッシュボードテーマ",
"LabelPublicHttpsPortHelp": "公開ポート番号はローカルHTTPSポートにマッピングしてください。",
"LabelAlbumArtMaxWidth": "アルバムアート最大高さ",
@ -1338,7 +1338,7 @@
"QuickConnectDescription": "クイックコネクトでサインインする場合、ログインするデバイスのクイックコネクトボタンを選択して以下のコードを入力します。",
"QuickConnectDeactivated": "ログインのリクエストが承認される前にクイックコネクトが無効化されました",
"QuickConnectAuthorizeFail": "不明なクイックコネクトコードです",
"QuickConnectAuthorizeSuccess": "リクエストが承認されました",
"QuickConnectAuthorizeSuccess": "デバイスの認証に成功しました!",
"QuickConnectAuthorizeCode": "コード {0} を入力してログイン",
"QuickConnectActivationSuccessful": "有効化に成功しました",
"QuickConnect": "クイックコネクト",
@ -1429,7 +1429,7 @@
"DeleteAll": "すべて削除",
"EnableFallbackFontHelp": "カスタム代替フォントの有効にする。これにより間違った字幕レンダリングの問題を回避することができます。",
"EnableFallbackFont": "代替フォントを有効化",
"LabelFallbackFontPathHelp": "ASS/SSA字幕レンダリングのための代替フォントを含むパスを指定してください。フォントファイルサイズは最大20MBです。woff2のような軽量でweb向けのフォントフォーマットが推奨です。",
"LabelFallbackFontPathHelp": "これらのフォントは、一部のクライアントによって字幕を表示するために使用されます。詳細については、ドキュメントを参照してください。",
"LabelFallbackFontPath": "代替フォントフォルダパス",
"HeaderSelectFallbackFontPathHelp": "ASS/SSA字幕レンダリングに使用する代替フォントフォルダパスを参照/入力。",
"HeaderSelectFallbackFontPath": "代替フォントフォルダパスを選択",
@ -1624,7 +1624,7 @@
"LabelRemuxingInfo": "リミキシング情報",
"LabelOriginalMediaInfo": "オリジナルメディア情報",
"PreferFmp4HlsContainer": "fMP4-HLSメディアコンテナを優先する",
"PreferFmp4HlsContainerHelp": "HLSのデフォルトコンテナとしてfMP4を優先的に使用し、対応デバイスでHEVCコンテンツのダイレクトストリーミングを可能にします。",
"PreferFmp4HlsContainerHelp": "HLSのデフォルトコンテナとしてfMP4を優先的に使用し、対応デバイスでHEVCとAV1コンテンツのダイレクトストリーミングを可能にします。",
"AllowHevcEncoding": "HEVCフォーマットでのエンコードを可能にする",
"LabelAllowedAudioChannels": "最大許容オーディオチャンネル数",
"LabelSelectAudioChannels": "チャンネル",
@ -1731,7 +1731,7 @@
"LogLevel.Warning": "警告",
"LogLevel.None": "なし",
"LabelEnableLUFSScan": "LUFS解析を有効",
"LabelEnableLUFSScanHelp": "音楽用のLUFS解析を有効(より時間とリソースを要します)。",
"LabelEnableLUFSScanHelp": "クライアントはオーディオ再生を正規化して、トラック間で等しい大きさを実現できます。これにより、ライブラリのスキャン時間が長くなり、より多くのリソースを消費します。",
"MenuClose": "メニューを閉じる",
"Notifications": "通知",
"NotificationsMovedMessage": "通知機能はWebhookプラグインに移行されました。",
@ -1765,5 +1765,23 @@
"LogoScreensaver": "ロゴスクリーンセーバー",
"AiTranslated": "AI翻訳",
"MachineTranslated": "機械翻訳",
"BackdropScreensaver": "背景スクリーンセーバー"
"BackdropScreensaver": "背景スクリーンセーバー",
"ButtonEditUser": "ユーザーを編集",
"SearchResultsEmpty": "申し訳ありません!「{0}」に対する結果が見つかりませんでした",
"LabelAlbumGain": "アルバムゲイン",
"HearingImpairedShort": "HI/SDH",
"LabelIsHearingImpaired": "聴覚障害者向けSDH",
"GridView": "グリッドビュー",
"ListView": "リスト表示",
"LabelBackdropScreensaverInterval": "バックドロップスクリーンセーバー間隔",
"LabelBackdropScreensaverIntervalHelp": "バックドロップスクリーンセーバーを使用時に異なるバックドロップ間での秒数。",
"HeaderAllRecordings": "すべての録音",
"SelectAudioNormalizationHelp": "トラックゲイン - 各トラックの音量を調整して、同じ大きさで再生されるようにします。アルバムゲイン - アルバム内の全てのトラックの音量を調整し、アルバムのダイナミックレンジを保持します。",
"LabelSelectAudioNormalization": "音量正規化",
"LabelBuildVersion": "ビルドバージョン",
"LabelServerVersion": "サーバーバージョン",
"LabelWebVersion": "ウェブバージョン",
"LabelTrackGain": "トラックゲイン",
"ForeignPartsOnly": "強制/外国語パートのみ",
"HeaderGuestCast": "ゲストスター"
}

View file

@ -1585,5 +1585,7 @@
"ButtonExitApp": "അപ്ലിക്കേഷനിൽ നിന്ന് പുറത്തു വരുക",
"ButtonClose": "അടയ്ക്കുക",
"ButtonBackspace": "ബാക്ക്സ്പേസ്",
"AddToFavorites": "പ്രിയപ്പെട്ടതിലേക്ക് ചേർക്കുക"
"AddToFavorites": "പ്രിയപ്പെട്ടതിലേക്ക് ചേർക്കുക",
"AllowCollectionManagement": "ശേഖരങ്ങൾ നിയന്ത്രിക്കാൻ ഈ ഉപയോക്താവിനെ അനുവദിക്കുക",
"AllowSegmentDeletion": "സെഗ്‌മെൻ്റുകൾ ഇല്ലാതാക്കുക"
}

View file

@ -102,7 +102,7 @@
"AskAdminToCreateLibrary": "Minta pentadbir untuk membuat perpustakaan.",
"Artist": "Artis",
"ApiKeysCaption": "Senarai kunci API yang diaktifkan sekarang",
"AllowTonemappingHelp": "Pemetaan-tone dapat mengubah dinamik video daripada HDR ke SDR sambil mengekalkan perincian dan warna gambar, yang merupakan maklumat yang sangat penting untuk mewakili pemandangan asal. Pada masa ini ia hanya berfungsi dengan video 10bit HDR10, HLG dan Dolby Visoion. Ini memerlukan pemadanan bersesuian dengan OpenCL atau CUDA runtime.",
"AllowTonemappingHelp": "Pemetaan-tone dapat mengubah dinamik video daripada HDR ke SDR sambil mengekalkan perincian dan warna gambar, yang merupakan maklumat yang sangat penting untuk mewakili pemandangan asal. Pada masa ini ia hanya berfungsi dengan video 10bit HDR10, HLG, dan Dolby Vision. Ini memerlukan pemadanan bersesuian dengan OpenCL atau CUDA runtime.",
"Songs": "Lagu-lagu",
"Playlists": "Senarai ulangmain",
"Photos": "Gambar-gambar",
@ -210,7 +210,7 @@
"DisplayInMyMedia": "Pamerkan di skrin utama",
"DirectStreaming": "Mainkan secara terus",
"DirectStreamHelp1": "Strim video ini serasi dengan peranti ini, tetapi ia mempunya format audio (DTS, Dolby TrueHD, dll.) ataupun jumlah saluran audio yang tidak sepadan. Strim video ini akan dipakej tanpa dimampatkan secara masa sebenar sebelum dihantar ke peranti. Hanya strim audio sahaja yang akan ditranskod.",
"DirectPlayHelp": "Fail sumber ini serasi sepenuhnya dengan klien ini, dan sesi ini menerima fail tanpa pengubahsuaian.",
"DirectPlayHelp": "Fail sumber ini serasi sepenuhnya dengan klien ini dan sesi ini menerima fail tanpa pengubahsuaian.",
"DirectPlaying": "Mainkan secara terus",
"Directors": "Pengarah",
"Digital": "Digital",
@ -258,5 +258,42 @@
"Cursive": "Sambung",
"DefaultSubtitlesHelp": "Sari kata dimuatkan berdasarkan bendera lalai dan paksa dalam metadata yang disematkan. Keutamaan bahasa diambil kira apabila terdapat beberapa pilihan.",
"LabelThrottleDelaySecondsHelp": "Masa dalam saat selepas mana transkoder akan dihadkan. Mesti cukup besar untuk klien mengekalkan penimbal yang sihat. Hanya berfungsi jika penghadan diaktifkan.",
"LabelSegmentKeepSeconds": "Masa untuk mengekalkan segmen"
"LabelSegmentKeepSeconds": "Masa untuk mengekalkan segmen",
"PreviousTrack": "Langkau ke sebelumnya",
"QuickConnectAuthorizeFail": "Kod Sambung Pantas tidak dikenali",
"AllowEmbeddedSubtitlesAllowImageOption": "Benarkan Imej",
"QuickConnect": "Sambung Pantas",
"Production": "Penerbitan",
"Primary": "Utama",
"Print": "Cetak",
"Producer": "Penerbit",
"Profile": "Profil",
"Quality": "Kualiti",
"ProductionLocations": "Lokasi penerbitan",
"QuickConnectNotActive": "Sambung Pantas tidak aktif pada pelayan ini",
"QuickConnectNotAvailable": "Minta pentadbir pelayan anda untuk mendayakan Sambung Pantas",
"Rate": "Kadar",
"PreviousChapter": "Bab sebelumnya",
"QuickConnectAuthorizeCode": "Masukkan kod {0} untuk log masuk",
"QuickConnectDeactivated": "Sambungan Pantas telah dinyahaktifkan sebelum permintaan log masuk dapat diluluskan",
"Previous": "Sebelumnya",
"LabelSegmentKeepSecondsHelp": "Masa dalam saat yang segmen harus disimpan sebelum ia ditulis ganti. Mesti melebihi \"Penghad laju setelah\". Hanya berfungsi jika pemadaman segmen didayakan.",
"AllowEmbeddedSubtitlesAllowAllOption": "Benarkan Semua",
"AllowEmbeddedSubtitlesAllowTextOption": "Benarkan Teks",
"QuickConnectDescription": "Untuk log masuk dengan Sambung Pantas, pilihlah butang 'Sambung Pantas' pada peranti yang anda menggunakan untuk log masuk dan memasukkan kod yang muncul di bawah.",
"QuickConnectInvalidCode": "Kod Sambung Pantas tidak sah",
"EnableNextVideoInfoOverlay": "Tunjukkan informasi video seterusnya semasa main balik",
"EnablePhotosHelp": "Imej akan dikesan dan dipaparkan bersama fail media lain.",
"Help": "Bantuan",
"DownloadAll": "Muat Turun Semua",
"EnablePhotos": "Paparkan gambar",
"EnableQuickConnect": "Dayakan Sambung Pantas pada pelayan ini",
"DisplayMissingEpisodesWithinSeasonsHelp": "Ini juga mesti didayakan untuk perpustakaan TV dalam konfigurasi pelayan.",
"EnableExternalVideoPlayers": "Pemain video luaran",
"Desktop": "Desktop",
"EnableNextVideoInfoOverlayHelp": "Pada penghujung video, paparkan maklumat tentang video seterusnya yang muncul dalam senarai main semasa.",
"EnableCinemaMode": "Mod pawagam",
"DisableCustomCss": "Lumpuhkan kod CSS kustom yang disediakan oleh pelayan",
"EnablePlugin": "Dayakan",
"DisablePlugin": "Lumpuhkan"
}

View file

@ -1781,5 +1781,6 @@
"LabelAlbumGain": "Albumjustering",
"LabelSelectAudioNormalization": "Lydnormalisering",
"LabelTrackGain": "Sporjustering",
"HeaderAllRecordings": "Alle opptak"
"HeaderAllRecordings": "Alle opptak",
"ButtonEditUser": "Rediger bruker"
}

View file

@ -33,5 +33,57 @@
"AddToFavorites": "मनपर्ने मा थप्नुहोस्",
"Add": "थप्नुहोस्",
"Actor": "अभिनेता",
"Absolute": "निरपेक्ष"
"Absolute": "निरपेक्ष",
"AddToCollection": "संग्रहमा थप्नुहोस्",
"AirDate": "प्रसारण मिति",
"Aired": "प्रसारित",
"ButtonAddImage": "चित्र थप्नुहोस्",
"ButtonResume": "सुचारु गर्नुहोस्",
"ButtonScanAllLibraries": "भन्डरमा खोज्नुहोस्",
"LatestFromLibrary": "हालसालै थपिएको",
"LabelTitle": "शीर्षक",
"LogLevel.Warning": "चेतावनी",
"ButtonOk": "हुन्छ",
"ButtonOpen": "खोल्नुहोस",
"ButtonPause": "रोक्नुहोस्",
"ButtonRename": "पुन: नामकरण",
"ButtonRemove": "हटाउनुहोस्",
"ButtonSend": "पठाउनुहोस्",
"ButtonShutdown": "बन्द गर्नुहोस्",
"ButtonSignIn": "",
"Lyricist": "गीतकार",
"LabelVideoInfo": "श्रव्यदृश्यको जानकारी",
"LabelTime": "समय",
"LiveTV": "प्रत्यक्ष टेलिभिजन",
"LiveBroadcasts": "प्रत्यक्ष प्रसारण",
"Localization": "स्थानीयकरण",
"LogLevel.Information": "जानकारी",
"LabelWeb": "संजल",
"LabelYear": "वर्ष",
"LabelSystem": "प्रणाली",
"LogLevel.Error": "त्रुटि",
"LogLevel.None": "",
"ButtonCancel": "रद्द गर्नुहोस्",
"Edit": "सम्पादन गर्नुहोस्",
"Controls": "नियन्त्रणहरू",
"LabelTimeLimitHours": "समय सीमा (घण्टा)",
"LabelYoureDone": "तपाईंले पूरा गर्नुभयो !",
"Live": "प्रत्यक्ष",
"List": "सूची",
"CommunityRating": "सामुदायिक मूल्याङ्कन",
"LabelTextColor": "अक्षरको रङ",
"LabelTextSize": "अक्षरको मोटाइ",
"LabelTextWeight": "अक्षरको बजन",
"BirthLocation": "जन्मस्थान",
"BirthPlaceValue": "जन्मथलो:{}",
"Blacklist": "कालोसूची",
"ButtonAddUser": "प्रयोगकर्ता थप्नुहोस्",
"ButtonArrowLeft": "बायाँ",
"ButtonInfo": "जानकारी",
"ButtonStart": "सुरु गर्नुहोस्",
"ButtonStop": "रोक्नुहोस्",
"ButtonSubmit": "पेश गर्नुहोस्",
"AiTranslated": "कृत्रिम बौद्धिक अनुवाद",
"Categories": "श्रेणीहरू",
"ButtonArrowRight": "दायाँ"
}

View file

@ -432,7 +432,7 @@
"LabelCollection": "Collectie",
"LabelCommunityRating": "Beoordeling gemeenschap",
"LabelContentType": "Inhoudstype",
"LabelCountry": "Land",
"LabelCountry": "Land/regio",
"LabelCriticRating": "Beoordeling critici",
"LabelCurrentPassword": "Huidig wachtwoord",
"LabelCustomCertificatePath": "Aangepast SSL-certificaatpad",
@ -1781,5 +1781,10 @@
"LabelSelectAudioNormalization": "Geluidsnormalisatie",
"LabelTrackGain": "Nummerversterking",
"SearchResultsEmpty": "Sorry! Geen resultaten gevonden voor \"{0}\"",
"HeaderAllRecordings": "Alle opnamen"
"HeaderAllRecordings": "Alle opnamen",
"LabelBuildVersion": "Buildversie",
"LabelServerVersion": "Serverversie",
"LabelWebVersion": "Webversie",
"ButtonEditUser": "Gebruiker bewerken",
"DlnaMovedMessage": "De DLNA-functionaliteit is verplaatst naar een plug-in."
}

View file

@ -458,7 +458,7 @@
"LabelCollection": "Kolekcja",
"LabelCommunityRating": "Ocena społeczności",
"LabelContentType": "Typ zawartości",
"LabelCountry": "Kraj",
"LabelCountry": "Kraj/region",
"LabelCriticRating": "Ocena krytyków",
"LabelCurrentPassword": "Aktualne hasło",
"LabelCustomCertificatePath": "Ścieżka do niestandardowego certyfikatu SSL",
@ -1782,5 +1782,10 @@
"LabelTrackGain": "Wzmocnienie utworu",
"SelectAudioNormalizationHelp": "Wzmocnienie utworu reguluje głośność każdego utworu tak, aby odtwarzał się z tą samą głośnością. Wzmocnienie albumu reguluje głośność tylko wszystkich utworów w albumie, zachowując zakres dynamiki albumu.",
"SearchResultsEmpty": "Wybacz! Nie znaleziono wyników dla „{0}”",
"HeaderAllRecordings": "Wszystkie nagrania"
"HeaderAllRecordings": "Wszystkie nagrania",
"LabelBuildVersion": "Wersja kompilacji",
"LabelServerVersion": "Wersja serwera",
"LabelWebVersion": "Wersja sieciowa",
"ButtonEditUser": "Edytuj użytkownika",
"DlnaMovedMessage": "Funkcjonalność DLNA została przeniesiona do wtyczki."
}

View file

@ -822,7 +822,7 @@
"Schedule": "Agendamentos",
"ScanForNewAndUpdatedFiles": "Procurar ficheiros novos ou atualizados",
"SaveSubtitlesIntoMediaFoldersHelp": "Guardar ficheiros de legendas junto aos ficheiros vídeo facilita a gestão.",
"SaveSubtitlesIntoMediaFolders": "Guardar legendas nas pastas multimédia",
"SaveSubtitlesIntoMediaFolders": "Guardar legendas nas pastas multimídia",
"Runtime": "Duração",
"ResumeAt": "Retomar a partir de {0}",
"ReplaceAllMetadata": "Substituir todos os metadados",
@ -1766,5 +1766,16 @@
"LabelBackdropScreensaverIntervalHelp": "O tempo em segundos entre diferentes cenários ao usar o protetor de ecrã de cenário.",
"LabelBackdropScreensaverInterval": "Intervalo do fundo do protetor de ecrã",
"HeaderAllRecordings": "Todas as Gravações",
"SelectAudioNormalizationHelp": "Ganho de faixa - ajusta o volume de cada faixa para que sejam reproduzidas com o mesmo volume. Ganho do álbum - ajusta o volume de todas as faixas apenas de um álbum, mantendo a faixa dinâmica do álbum."
"SelectAudioNormalizationHelp": "Ganho de faixa - ajusta o volume de cada faixa para que sejam reproduzidas com o mesmo volume. Ganho do álbum - ajusta o volume de todas as faixas apenas de um álbum, mantendo a faixa dinâmica do álbum.",
"LogoScreensaver": "Logotipo de proteção de ecrã",
"TonemappingModeHelp": "Selecione o modo de mapeamento de tons. Se você tiver destaques estourados, tente mudar para o modo RGB.",
"MachineTranslated": "Traduzido Automaticamente",
"ForeignPartsOnly": "Apenas partes forçadas/externas",
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Os extras geralmente têm o mesmo nome incorporado do parente, marque isto para usar títulos incorporados de qualquer maneira.",
"SaveRecordingImagesHelp": "Guardar imagens do fornecedor de listagens EPG junto com a mídia.",
"MessageRepositoryInstallDisclaimer": "AVISO: Instalar um repositório de plugins de terceiros traz riscos. Ele pode conter código instável ou malicioso e pode mudar a qualquer momento. Instale apenas repositórios de autores em quem você confia.",
"SaveRecordingNFO": "Guardar metadados EPG da gravação no NFO",
"SaveRecordingNFOHelp": "Guardar metadados do fornecedor de listagens EPG junto com a mídia.",
"SaveRecordingImages": "Guardar imagens da gravação EPG",
"LabelTrackGain": "Ganho da Faixa"
}

View file

@ -185,7 +185,7 @@
"LabelProfileContainersHelp": "Separados por vírgula. Pode ser deixado em branco para usar com todos os contentores.",
"LabelProfileContainer": "Contentor",
"LabelProfileCodecsHelp": "Separados por vírgula. Pode ser deixado em branco para usar com todos os codecs.",
"LabelProfileCodecs": "Codecs",
"LabelProfileCodecs": "Codificadores",
"LabelProfileAudioCodecs": "Codecs do áudio",
"LabelPreferredSubtitleLanguage": "Idioma de legendas preferido",
"LabelPreferredDisplayLanguage": "Idioma de visualização preferido",
@ -353,7 +353,7 @@
"LabelMetadataReadersHelp": "Ordene as fontes locais de metadados por ordem de prioridade. O primeiro ficheiro a ser encontrado será lido.",
"LabelMetadataReaders": "Leirores de metadados",
"LabelMetadataDownloadersHelp": "Active e ordene os seus pesquisadores de metadados por ordem de prioridade. Pesquisadores com menor prioridade só serão utilizados para completar informação em falta.",
"LabelLogs": "Histórico",
"LabelLogs": "Registros",
"LabelKodiMetadataUserHelp": "Guardar dados de utilização em NFO para que outras aplicações os utilizem.",
"LabelKodiMetadataUser": "Guardar dados de utilização em NFO para",
"LabelImageFetchersHelp": "Activar e ordenar os pesquisadores de imagens por ordem de preferência.",
@ -372,7 +372,7 @@
"LabelCustomCertificatePath": "Localização do certificado SSL personalizado",
"LabelCurrentPassword": "Palavra-passe actual",
"LabelCriticRating": "Avaliação da crítica",
"LabelCountry": "País",
"LabelCountry": "País/Região",
"LabelContentType": "Tipo de conteúdo",
"LabelCommunityRating": "Avaliação da comunidade",
"LabelCollection": "Colecção",
@ -1181,7 +1181,7 @@
"OptionForceRemoteSourceTranscoding": "Força a transcodificação de fontes de mídia remotas, como TV ao vivo",
"PreferEmbeddedTitlesOverFileNames": "Preferir títulos incorporados ao invés dos nomes dos arquivos",
"OptionSaveMetadataAsHiddenHelp": "Alterar isso será aplicado aos novos metadados salvos daqui para frente. Os arquivos de metadados existentes serão atualizados na próxima vez em que forem salvos pelo Jellyfin Server.",
"OptionRegex": "Regex",
"OptionRegex": "Expressão Regular",
"OptionLoginAttemptsBeforeLockoutHelp": "Um valor zero significa herdar o padrão de três tentativas para usuários normais e cinco para administradores. Definir como -1 desativará o recurso.",
"OptionExtractChapterImage": "Ativar extração de imagem de capítulo",
"PreferEmbeddedEpisodeInfosOverFileNames": "Preferir informações de episódios incorporados ao invés de nomes de arquivos",
@ -1200,7 +1200,7 @@
"HeaderDVR": "DVR",
"ApiKeysCaption": "Lista das chaves de API ativadas no momento",
"ButtonTogglePlaylist": "Lista de leitura",
"ButtonSyncPlay": "SyncPlay",
"ButtonSyncPlay": "Sincronização",
"Data": "Dados",
"ButtonUseQuickConnect": "Usar conexão rápida",
"ButtonActivate": "Ativar",
@ -1368,7 +1368,7 @@
"EnableRewatchingNextUp": "Activar Rever em 'A Seguir'",
"Arranger": "Organizador",
"Print": "Imprimir",
"Poster": "Poster",
"Poster": "Cartaz",
"Small": "Pequeno",
"QuickConnectActivationSuccessful": "Activado com sucesso",
"SaveChanges": "Gravar alterações",
@ -1471,5 +1471,312 @@
"PasswordRequiredForAdmin": "É necessária uma palavra passe para contas de administrador.",
"PleaseConfirmRepositoryInstallation": "Clique em OK para confirmar que leu o texto acima e deseja prosseguir com a instalação do repositório de plug-ins.",
"LabelThrottleDelaySecondsHelp": "Tempo em segundos após o qual o transcodificador será limitado. Deve ser grande o suficiente para que o cliente mantenha um buffer saudável. Só funciona se a limitação estiver ligada.",
"QuickConnectAuthorizeFail": "Código 'Quick Connect' desconhecido"
"QuickConnectAuthorizeFail": "Código 'Quick Connect' desconhecido",
"TonemappingAlgorithmHelp": "O mapeamento de tons pode ser ajustado. Se você não estiver familiarizado com essas opções, mantenha o padrão. O valor recomendado é 'BT.2390'.",
"UseEpisodeImagesInNextUp": "Use imagens de episódios nas seções 'Próximo' e 'Continuar a assistir'",
"UseEpisodeImagesInNextUpHelp": "As seções 'Próximo' e 'Continuar a assistir' usarão imagens dos episódios como miniaturas em vez da miniatura principal do programa.",
"Yadif": "YADIF",
"IntelLowPowerEncHelp": "A codificação de baixo consumo pode manter uma sincronização CPU-GPU desnecessária. No Linux eles devem ser desativados se o firmware i915 HuC não estiver configurado.",
"LabelTonemappingThresholdHelp": "Os parâmetros do algoritmo de mapeamento de tom são ajustados para cada cena. Um limite é usado para detectar se a cena mudou ou não. Se a distância entre o brilho médio do quadro atual e a média atual exceder um valor limite, recalcularíamos a média da cena e o brilho máximo. Os valores recomendados e padrão são 0,8 e 0,2.",
"SyncPlayAccessHelp": "O recurso SyncPlay permite sincronizar a reprodução com outros dispositivos. Selecione o nível de acesso que este usuário tem ao SyncPlay.",
"AllowVppTonemappingHelp": "Mapeamento de tons completo baseado no driver Intel. Atualmente funciona apenas em determinados hardwares com vídeos HDR10. Isto tem uma prioridade mais alta em comparação com outra implementação OpenCL.",
"LabelSyncPlaySettingsMinDelaySkipToSyncHelp": "Atraso mínimo de reprodução (em ms) após o qual SkipToSync tenta corrigir a posição de reprodução.",
"OptionMaxActiveSessions": "Definir o número máximo de sessões de usuário simultâneas.",
"LabelSyncPlaySettingsSpeedToSyncDurationHelp": "Quantidade de milissegundos usados pelo SpeedToSync para corrigir a posição de reprodução.",
"SubtitleAppearanceSettingsDisclaimer": "As configurações a seguir não se aplicam às legendas gráficas mencionadas acima ou às legendas ASS/SSA que incorporam seus próprios estilos.",
"UnsupportedPlayback": "Jellyfin não pode desencriptar conteúdo protegido por DRM, mas todo o conteúdo será testado de qualquer maneira, incluindo títulos protegidos. Alguns arquivos podem aparecer completamente pretos devido à encriptação ou a outros recursos não suportados, como títulos interativos.",
"MessageSyncPlayGroupWait": "{0} está a armazenar em buffer…",
"LabelSelectAudioNormalization": "Normalização de áudio",
"LabelBackdropScreensaverIntervalHelp": "O tempo em segundos entre diferentes cenários ao usar o protetor de tela de cenário.",
"LabelServerVersion": "Versão do servidor",
"LabelTonemappingPeakHelp": "Substitua o sinal/pico nominal/de referência por este valor. Útil quando as informações de pico incorporadas nos metadados de exibição não são confiáveis ou quando há mapeamento de tons de uma faixa mais baixa para uma faixa mais alta. Os valores recomendados e padrão são 100 e 0.",
"OptionDateShowAdded": "Data de exibição adicionada",
"Studios": "Estúdios",
"WriteAccessRequired": "Jellyfin requer acesso de gravação a esta pasta. Garanta o acesso de gravação e tente novamente.",
"LabelFallbackFontPathHelp": "Essas fontes são usadas por alguns clientes para renderizar legendas. Consulte a documentação para obter mais informações.",
"EnableGamepadHelp": "Ouça a entrada de qualquer controlador conectado. (Requer: Modo de exibição 'TV')",
"BackdropScreensaver": "Protetor de tela de fundo",
"LabelLevel": "Nível",
"LabelSyncPlaySettingsMinDelaySpeedToSync": "Atraso mínimo da velocidade de sincronização",
"OptionDateEpisodeAdded": "Data do episódio adicionado",
"SubtitleDownloadersHelp": "Habilite e classifique seus downloaders de legendas preferidos em ordem de prioridade.",
"Sort": "Organizar",
"SmartSubtitlesHelp": "As legendas que correspondem à preferência de idioma serão carregadas quando o áudio estiver em um idioma estrangeiro.",
"SubtitleMagenta": "Magenta",
"Smart": "Inteligente",
"StopPlayback": "Parar a reprodução",
"MessageSyncPlayEnabled": "Sincronização ativada.",
"MessageSyncPlayGroupDoesNotExist": "Falha ao ingressar no grupo porque ele não existe.",
"Subtitle": "Legenda",
"MixedMoviesShows": "Filmes e Shows Misturados",
"MessageSyncPlayIsDisabled": "Permissão necessária para usar a Sincronização.",
"LabelMediaDetails": "Detalhes da mídia",
"MessageSyncPlayErrorNoActivePlayer": "Nenhum tocador ativo encontrado. A Sincronização foi desativada.",
"SpecialFeatures": "Características especiais",
"MessageSyncPlayNoGroupsAvailable": "Nenhum grupo disponível. Comece a jogar algo primeiro.",
"Smaller": "Menor",
"MessageSyncPlayErrorMedia": "Falha ao ativar o SyncPlay! Erro de mídia.",
"MessageSyncPlayErrorAccessingGroups": "Ocorreu um erro ao acessar a lista de grupos.",
"SubtitleBlack": "Preto",
"SubtitleBlue": "Azul",
"SubtitleVerticalPositionHelp": "Número da linha onde o texto aparece. Números positivos indicam de cima para baixo. Números negativos indicam de baixo para cima.",
"Remuxing": "Remuxing",
"OnApplicationStartup": "Na inicialização do aplicativo",
"MessageSyncPlayLibraryAccessDenied": "O acesso a este conteúdo é restrito.",
"SortName": "Classificar nome",
"SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Estas definições também se aplicam a qualquer reprodução do Google Cast iniciada por este dispositivo.",
"OnWakeFromSleep": "Ao acordar do sono",
"OptionMaxActiveSessionsHelp": "Um valor 0 desativará o recurso.",
"Watched": "Assistido",
"SyncPlayGroupDefaultTitle": "grupo de {0}",
"TabRepositories": "Repositórios",
"TagsValue": "Etiquetas: {0}",
"TextSent": "Texto enviado.",
"ThemeSongs": "Músicas tema",
"ThemeVideos": "Vídeos temáticos",
"TheseSettingsAffectSubtitlesOnThisDevice": "Estas configurações afetam as legendas neste dispositivo",
"Thumb": "Miniatura",
"ThumbCard": "Cartão de Miniatura",
"TitleHardwareAcceleration": "Aceleraçao do hardware",
"Trailers": "Prévia",
"TypeOptionPluralAudio": "Áudios",
"TypeOptionPluralBook": "Livros",
"TypeOptionPluralMovie": "Filmes",
"Typewriter": "Máquina de escrever",
"UnknownError": "Ocorreu um erro desconhecido.",
"Up": "Acima",
"Upload": "Carregar",
"ValueCodec": "Codec: {0}",
"ValueContainer": "Contêiner: {0}",
"ValueDiscNumber": "Disco {0}",
"ValueMovieCount": "{0} filmes",
"ValueEpisodeCount": "{0} episódios",
"ValueMusicVideoCount": "{0} videoclipes",
"ValueOneEpisode": "1 episódio",
"ValueOneSong": "1 música",
"ValueSeconds": "{0} segundos",
"ViewAlbum": "Ver álbum",
"ViewAlbumArtist": "Ver artista do álbum",
"ViewPlaybackInfo": "Ver informações de reprodução",
"WeeklyAt": "{0} às {1}",
"Whitelist": "Lista de permissões",
"Writers": "Escritores",
"XmlTvKidsCategoriesHelp": "Os programas com estas categorias serão exibidos como programas para crianças. Separe múltiplos com '|'.",
"Yes": "Sim",
"HeaderSelectFallbackFontPathHelp": "Navegue ou insira o caminho da pasta de fontes substitutas a ser usada para renderizar legendas ASS/SSA.",
"RemuxHelp1": "A mídia está em um contêiner de arquivo incompatível (MKV, AVI, WMV, etc.), mas tanto o fluxo de vídeo quanto o fluxo de áudio são compatíveis com o dispositivo. A mídia será reembalada sem perdas imediatamente antes de ser enviada ao dispositivo.",
"PreferFmp4HlsContainer": "Prefira o contêiner de mídia fMP4-HLS",
"LabelSyncPlayInfo": "Informações de Sincronização",
"PreferFmp4HlsContainerHelp": "Prefira usar fMP4 como contêiner padrão para HLS, tornando possível transmitir diretamente conteúdo HEVC e AV1 em dispositivos suportados.",
"LabelSelectAudioChannels": "Canais",
"LabelSelectStereo": "Estéreo",
"ContainerNotSupported": "O contêiner não é compatível",
"LabelSyncPlayNoGroups": "Nenhum grupo disponível",
"LabelSyncPlaySettingsMaxDelaySpeedToSync": "Atraso máximo da Velocidade de sincronização",
"LabelSyncPlaySettingsSkipToSync": "Ativar pulo de sincronização",
"LabelSyncPlaySettingsSpeedToSync": "Ativar SpeedToSync",
"NotificationsMovedMessage": "A funcionalidade de notificações foi movida para o plugin Webhook.",
"AspectRatioCover": "Capa",
"AspectRatioFill": "Preencher",
"AudioCodecNotSupported": "O codec de áudio não é compatível",
"HeaderGuestCast": "Estrelas convidadas",
"LabelSyncPlaySettingsSpeedToSyncHelp": "Método de correção de sincronização que consiste em acelerar a reprodução. A correção de sincronização deve estar habilitada.",
"MessageSyncPlayDisabled": "SyncPlay desativado.",
"LabelVideoInfo": "Informações do vídeo",
"LabelRemuxingInfo": "Informações sobre remixagem",
"LabelDate": "Data",
"MessageRenameMediaFolder": "Renomear uma biblioteca de mídia fará com que todos os metadados sejam perdidos, proceda com cuidado.",
"LabelSyncPlaySettingsMinDelaySkipToSync": "Atraso mínimo do pulo de sincronização",
"LogLevel.None": "Nada",
"LogLevel.Critical": "Crítico",
"New": "Novo",
"SubtitleWhite": "Branco",
"SubtitleLightGray": "Cinza claro",
"Track": "Trilha",
"TypeOptionPluralMusicArtist": "Artistas musicais",
"TypeOptionPluralSeason": "Temporadas",
"LabelSelectMono": "Mono",
"HeaderEpisodesStatus": "Status dos episódios",
"LabelSyncPlaySettingsSkipToSyncHelp": "Método de correção de sincronismo que consiste em buscar a posição estimada. A correção de sincronização deve estar habilitada.",
"LogLevel.Trace": "Vestígio",
"LogLevel.Debug": "Depurar",
"LogLevel.Information": "Informação",
"MessageRepositoryInstallDisclaimer": "AVISO: Instalar um repositório de plugins de terceiros traz riscos. Ele pode conter código instável ou malicioso e pode mudar a qualquer momento. Instale apenas repositórios de autores em que você confia.",
"OptionAllowContentDownload": "Permitir downloads de mídia",
"StoryArc": "Arco da história",
"TabNetworking": "Rede",
"ValueOneMovie": "1 filme",
"HeaderSelectFallbackFontPath": "Selecione o caminho da pasta de fontes substitutas",
"XmlTvMovieCategoriesHelp": "Os programas com estas categorias serão exibidos como filmes. Separe múltiplos com '|'.",
"LabelVideoRange": "Alcance de vídeo",
"Mixer": "Misturador",
"LabelSystem": "Sistema",
"SaveRecordingImages": "Salvar gravação de imagens EPG",
"SaveRecordingImagesHelp": "Salve imagens do provedor de listagens EPG junto com a mídia lateral.",
"UseDoubleRateDeinterlacing": "Dobre a taxa de quadros ao desentrelaçar",
"MessageSyncPlayUserJoined": "{0} entrou no grupo.",
"MessageSyncPlayUserLeft": "{0} saiu do grupo.",
"MillisecondsUnit": "ms",
"SubtitleGreen": "Verde",
"Notifications": "Notificações",
"XmlTvNewsCategoriesHelp": "Os programas com estas categorias serão exibidos como programas de notícias. Separe múltiplos com '|'.",
"LabelOriginalMediaInfo": "Informações da mídia original",
"YoutubeBadRequest": "Requisição ruim.",
"YoutubeDenied": "O vídeo solicitado não pode ser reproduzido em players incorporados.",
"TypeOptionPluralBoxSet": "Conjuntos de caixas",
"LabelBackdropScreensaverInterval": "Intervalo do protetor de tela de fundo",
"UseDoubleRateDeinterlacingHelp": "Essa configuração usa a taxa de campo durante o desentrelaçamento, geralmente chamada de desentrelaçamento bob, que dobra a taxa de quadros do vídeo para fornecer movimento total, como o que você veria ao visualizar um vídeo entrelaçado em uma TV.",
"LabelDirectStreamingInfo": "Informações de transmissão direta",
"LabelAlbumGain": "Ganho do álbum",
"LogoScreensaver": "Logotipo do Protetor de tela",
"MediaInfoColorSpace": "Espaço colorido",
"Studio": "Estúdio",
"SubtitleGray": "Cinza",
"TabLogs": "Histórico",
"Tags": "Etiquetas",
"Vertical": "Vertical",
"Video": "Vídeo",
"VideoAudio": "Vídeo Áudio",
"MessageNoItemsAvailable": "Nenhum item está disponível no momento.",
"MessageNoFavoritesAvailable": "Nenhum favorito está disponível no momento.",
"SaveRecordingNFOHelp": "Salve metadados do provedor de listagens EPG junto com a mídia.",
"TonemappingRangeHelp": "Selecione o intervalo de cores de saída. Auto é igual ao intervalo de entrada.",
"TypeOptionPluralEpisode": "Episódios",
"MediaInfoColorTransfer": "Transferência de cores",
"MediaInfoVideoRange": "Alcance de vídeo",
"MessageNoGenresAvailable": "Permita que alguns provedores de metadados extraiam gêneros da Internet.",
"ListView": "Lista de Exibição",
"LogLevel.Warning": "Aviso",
"MessageChangeRecordingPath": "Alterar sua pasta de gravação não migrará as gravações existentes do local antigo para o novo. Você precisará movê-los manualmente, se desejar.",
"ListPaging": "{0}-{1} de {2}",
"Localization": "Localização",
"Lyricist": "Letrista",
"NextChapter": "Próximo Capítulo",
"TvLibraryHelp": "Revise o {0}guia de nomenclatura de TV{1}.",
"TypeOptionPluralSeries": "Programas de televisão",
"TypeOptionPluralVideo": "Vídeos",
"ValueSeriesCount": "{0} série",
"LabelAudioInfo": "Informações de áudio",
"RemuxHelp2": "Remux usa muito pouco poder de processamento com qualidade de mídia completamente sem perdas.",
"MenuOpen": "Abrir Menu",
"MenuClose": "Fechar Menu",
"OptionAllowContentDownloadHelp": "Os usuários podem baixar mídia e armazená-la em seus dispositivos. Isso não é o mesmo que um recurso de sincronização. As bibliotecas de livros exigem que isso esteja ativado para funcionar corretamente.",
"SmallCaps": "Letras Minúsculas",
"StereoDownmixAlgorithmHelp": "Algoritmo usado para fazer downmix de áudio multicanal para estéreo.",
"Suggestions": "Sugestões",
"TabCodecs": "Codificadores",
"SubtitleRed": "Vermelho",
"SubtitleYellow": "Amarelo",
"Unrated": "Sem classificação",
"TypeOptionPluralMusicAlbum": "Álbuns de música",
"TypeOptionPluralMusicVideo": "Vídeos musicais",
"UserMenu": "Menu do Usuário",
"ValueAlbumCount": "{0} álbuns",
"ValueOneAlbum": "1 álbum",
"ValueSongCount": "{0} músicas",
"Controls": "Controles",
"LabelSyncPlaySettingsSpeedToSyncDuration": "Duração da Velocidade de sincronização",
"EnableFallbackFontHelp": "Ative fontes alternativas personalizadas. Isso pode evitar o problema de renderização incorreta de legendas.",
"MessagePlaybackError": "Ocorreu um erro ao reproduzir este arquivo no receptor do Google Cast.",
"NextTrack": "Pular para o próximo",
"TitleHostingSettings": "Configurações de hospedagem",
"LabelTextWeight": "Peso do texto",
"Bold": "Negrito",
"MessageSyncPlayCreateGroupDenied": "Permissão necessária para criar um grupo.",
"MessageUnauthorizedUser": "Você não está autorizado a acessar o servidor neste momento. Entre em contato com o administrador do servidor para obter mais informações.",
"OtherArtist": "Outro Artista",
"Transcoding": "Transcodificação",
"YoutubePlaybackError": "O vídeo solicitado não pode ser reproduzido.",
"MessageChromecastConnectionError": "Seu receptor do Google Cast não consegue entrar em contato com o servidor Jellyfin. Verifique a conexão e tente novamente.",
"EnableFallbackFont": "Ativar fontes substitutas",
"LabelDeveloper": "Desenvolvedor",
"MessageSyncPlayErrorMissingSession": "Falha ao ativar o Sincronização! Sessão perdida.",
"MessageSyncPlayPlaybackPermissionRequired": "É necessária permissão de reprodução.",
"OriginalAirDate": "Data de transmissão original",
"SelectAudioNormalizationHelp": "Ganho de faixa - ajusta o volume de cada faixa para que sejam reproduzidas com o mesmo volume. Ganho do álbum - ajusta o volume de todas as faixas apenas de um álbum, mantendo a faixa dinâmica do álbum.",
"LabelSyncPlaySettingsSyncCorrectionHelp": "Habilite a sincronização ativa da reprodução acelerando a mídia ou buscando a posição estimada. Desative isso em caso de gagueira intensa.",
"Larger": "Maior",
"LogLevel.Error": "Erro",
"MediaInfoTitle": "Título",
"MessageAddRepository": "Caso queira adicionar um repositório, clique no botão próximo ao cabeçalho e preencha as informações solicitadas.",
"MessageSyncPlayJoinGroupDenied": "Não é possível entrar no grupo.",
"LabelTrackGain": "Rastrear ganho",
"ValueMinutes": "{0} minutos",
"LabelBuildVersion": "Versão de compilação",
"LabelWebVersion": "Versão web",
"SubtitleCyan": "Ciano",
"SubtitleOffset": "Deslocamento de legenda",
"ValueOneMusicVideo": "1 videoclipe",
"ValueOneSeries": "1 série",
"XmlTvSportsCategoriesHelp": "Os programas com estas categorias serão exibidos como programas esportivos. Separe múltiplos com '|'.",
"LabelFallbackFontPath": "Caminho da pasta de fontes substitutas",
"LabelPlaybackInfo": "Informações de reprodução",
"LabelTranscodingInfo": "Informações de transcodificação",
"AllowHevcEncoding": "Permitir codificação no formato HEVC",
"LabelAllowedAudioChannels": "Máximo de canais de áudio permitidos",
"YoutubeNotFound": "Vídeo não encontrado.",
"EnableEnhancedNvdecDecoder": "Habilitar decodificador NVDEC aprimorado",
"EnableVppTonemapping": "Habilitar mapeamento de tom VPP",
"MediaInfoDvProfile": "Perfil DV",
"VideoCodecNotSupported": "O codec de vídeo não é compatível",
"SubtitleCodecNotSupported": "O codec de legenda não é compatível",
"RefFramesNotSupported": "Quadros de referência não são suportados",
"SecondaryAudioNotSupported": "Faixas de áudio secundárias não são suportadas",
"VideoLevelNotSupported": "O nível do codec de vídeo não é compatível",
"AudioBitDepthNotSupported": "A profundidade de bits do áudio não é suportada",
"LabelHardwareEncodingOptions": "Opções de codificação de hardware",
"UnknownAudioStreamInfo": "As informações do fluxo de áudio são desconhecidas",
"DirectPlayError": "Ocorreu um erro ao iniciar a reprodução direta",
"Short": "Curto",
"Clip": "Clipe",
"BehindTheScenes": "Por trás das cenas",
"DeletedScene": "Cena deletada",
"Interview": "Entrevista",
"LabelVideoRangeType": "Tipo de intervalo de vídeo",
"MediaInfoVideoRangeType": "Tipo de intervalo de vídeo",
"MediaInfoDoViTitle": "Título do DVD",
"MediaInfoDvVersionMajor": "Versão DV principal",
"MediaInfoDvLevel": "Nível DV",
"Unreleased": "Ainda não lançado",
"AllowAv1Encoding": "Permitir codificação no formato AV1",
"AiTranslated": "IA traduzida",
"MachineTranslated": "Traduzido automaticamente",
"AnamorphicVideoNotSupported": "O vídeo anamórfico não é compatível",
"InterlacedVideoNotSupported": "Vídeo entrelaçado não é compatível",
"EnableEnhancedNvdecDecoderHelp": "Implementação experimental de NVDEC, não habilite esta opção a menos que você encontre erros de decodificação.",
"AudioChannelsNotSupported": "O número de canais de áudio não é suportado",
"ContainerBitrateExceedsLimit": "A taxa de bits do vídeo excede o limite",
"VideoBitrateNotSupported": "A taxa de bits do vídeo não é suportada",
"LabelVppTonemappingBrightness": "Ganho de brilho do mapeamento de tom VPP",
"LabelVppTonemappingBrightnessHelp": "Aplique ganho de brilho no mapeamento de tons VPP. Os valores recomendados e padrão são 16 e 0.",
"ThemeSong": "Música tema",
"Featurette": "Destaque",
"Scene": "Cena",
"Sample": "Amostra",
"ThemeVideo": "Vídeo temático",
"EnableSplashScreen": "Habilite a tela inicial",
"LabelVppTonemappingContrast": "Ganho de contraste do mapeamento de tom VPP",
"LabelVppTonemappingContrastHelp": "Aplique ganho de contraste no mapeamento de tons VPP. Os valores recomendados e padrão são 1.",
"Unknown": "Desconhecido",
"EnableIntelLowPowerHevcHwEncoder": "Habilitar codificador de hardware HEVC Intel Baixa-Potência",
"AudioIsExternal": "O fluxo de áudio é externo",
"MediaInfoElPresentFlag": "Sinalizador predefinido DV el",
"AudioProfileNotSupported": "O perfil do codec de áudio não é compatível",
"MediaInfoDvVersionMinor": "Versão DV menor",
"Select": "Selecione",
"VideoProfileNotSupported": "O perfil do codec de vídeo não é compatível",
"MediaInfoRpuPresentFlag": "Sinalizador predefinido DV rpu",
"HearingImpairedShort": "Alta Definição/SDH",
"ForeignPartsOnly": "Apenas partes forçadas/estrangeiras",
"LabelIsHearingImpaired": "Para deficientes auditivos (SDH)",
"AudioBitrateNotSupported": "A taxa de bits do áudio não é suportada",
"AudioSampleRateNotSupported": "A taxa de amostragem do áudio não é suportada",
"VideoResolutionNotSupported": "A resolução do vídeo não é suportada",
"VideoBitDepthNotSupported": "A profundidade de bits do vídeo não é suportada",
"VideoFramerateNotSupported": "A taxa de quadros do vídeo não é compatível",
"UnknownVideoStreamInfo": "As informações do stream de vídeo são desconhecidas",
"SelectAll": "Selecionar tudo",
"LabelTonemappingMode": "Modo de mapeamento de tons",
"TonemappingModeHelp": "Selecione o modo de mapeamento de tons. Se ocorrerem erros de sobre-exposição, experimente o modo RGB.",
"VideoRangeTypeNotSupported": "O tipo de intervalo do vídeo não é compatível"
}

View file

@ -1781,5 +1781,7 @@
"LabelAlbumGain": "Zosilnenie pre album",
"LabelSelectAudioNormalization": "Normalizácia hlasitosti",
"LabelTrackGain": "Zosilnenie stopy",
"SearchResultsEmpty": "Prepáčte! Nenašli sa žiadne výsledky pre \"{0}\""
"SearchResultsEmpty": "Prepáčte! Nenašli sa žiadne výsledky pre \"{0}\"",
"ButtonEditUser": "Upraviť používateľa",
"HeaderAllRecordings": "Všetky nahrávky"
}

View file

@ -10,7 +10,7 @@
"HeaderUser": "Uporabnik",
"LabelArtists": "Izvajalci",
"LabelContentType": "Tip vsebine",
"LabelCountry": "Država",
"LabelCountry": "Država/Regija",
"LabelCurrentPassword": "Trenutno geslo",
"LabelFinish": "Zaključi",
"LabelLanguage": "Jezik",
@ -1453,7 +1453,7 @@
"LabelSelectAudioChannels": "Kanali",
"LabelAllowedAudioChannels": "Največje število dovoljenih zvočnih kanalov",
"AllowHevcEncoding": "Dovoli kodiranje v HEVC format",
"PreferFmp4HlsContainerHelp": "Prioritiziraj uporabo fMP4 kot privzeti kontejner za HLS. Omogoča neposredno pretakanje HEVC vsebin na podprtih napravah.",
"PreferFmp4HlsContainerHelp": "Prioritiziraj uporabo fMP4 kot privzeti kontejner za HLS. Omogoča neposredno pretakanje HEVC in AV1 vsebin na podprtih napravah.",
"PreferFmp4HlsContainer": "Prioritiziraj fMP4-HLS kontejner",
"LabelSyncPlayInfo": "Informacije o SyncPlay",
"LabelOriginalMediaInfo": "Informacije o izvorni predstavnosti",
@ -1770,5 +1770,7 @@
"SubtitleLightGray": "Svetlo siva",
"SubtitleMagenta": "Magenta",
"LabelParallelImageEncodingLimitHelp": "Največje dovoljeno število vzporednih kodiranj slik. Nastavite na 0 za samodejno omejitev glede na zmogljivost vašega sistema.",
"AiTranslated": "AI prevedeno"
"AiTranslated": "AI prevedeno",
"ButtonEditUser": "Uredi uporabnika",
"HeaderAllRecordings": "Vsi posnetki"
}

View file

@ -581,5 +581,7 @@
"HeaderAutoDiscovery": "Zbulim ne rrjet",
"HeaderStopRecording": "Ndaloni regjistrimin",
"HeaderSubtitleProfilesHelp": "Profilet e titrave përshkruajnë formatin e titrave që suportohen nga paisja.",
"HeaderSyncPlaySelectGroup": "Bashkohuni një grupi"
"HeaderSyncPlaySelectGroup": "Bashkohuni një grupi",
"DirectStreamHelp2": "Energjia e konsumuar nga transmetimi i drejtpërdrejtë zakonisht varet nga profili i audios. Vetëm transmetimi i videove është pa humbje.",
"EnableDetailsBannerHelp": "Shfaqni një imazh baneri në krye të faqes së artikullit."
}

View file

@ -415,7 +415,7 @@
"LabelCollection": "Samling",
"LabelCommunityRating": "Användaromdöme",
"LabelContentType": "Innehållstyp",
"LabelCountry": "Land",
"LabelCountry": "Land/Region",
"LabelCriticRating": "Kritikerbetyg",
"LabelCurrentPassword": "Nuvarande lösenord",
"LabelCustomCertificatePath": "Sökväg för anpassat SSL-certifikat",
@ -1781,5 +1781,9 @@
"LabelAlbumGain": "Albumförstärkning",
"LabelSelectAudioNormalization": "Ljudnormalisering",
"LabelTrackGain": "Spårförstärkning",
"HeaderAllRecordings": "Alla inspelningar"
"HeaderAllRecordings": "Alla inspelningar",
"LabelBuildVersion": "Byggversion",
"ButtonEditUser": "redigera användare",
"LabelServerVersion": "Serverversion",
"LabelWebVersion": "Webbversion"
}

View file

@ -68,7 +68,7 @@
"LabelCachePath": "Önbellek yolu",
"LabelCachePathHelp": "Sunucunun resim vb. önbellek dosyalarını tutması için bir yer belirt. Öntanımlı yeri kullanmak için boş bırak.",
"LabelContentType": "İçerik türü",
"LabelCountry": "Ülke",
"LabelCountry": "Ülke/Bölge",
"LabelCurrentPassword": "Kullanımdaki parola",
"LabelDay": "Haftanın günü",
"LabelEnableDlnaServer": "DLNA sunucusunu etkinleştir",
@ -248,7 +248,7 @@
"ChannelNameOnly": "Yalnızca {0} kanalı",
"ConfirmDeleteItems": "Bu ögeleri silmek, onları hem dosya sisteminden hem de medya kütüphanenizden siler. Devam etmek istediğinize emin misiniz?",
"ConfirmDeletion": "Silme İşlemini Onayla",
"ConfirmEndPlayerSession": "Jellyfin'i {0} tarihinde kapatmak ister misiniz?",
"ConfirmEndPlayerSession": "{0} üzerindeki Jellyfin'i kapatmak ister misiniz?",
"Connect": "Bağlan",
"Disconnect": "Bağlantıyı Kes",
"Display": "Görünüm",
@ -1774,5 +1774,9 @@
"LabelAlbumGain": "Albüm Ses Kazancı",
"LabelTrackGain": "Parça Ses Kazancı",
"SearchResultsEmpty": "Üzgünüz! \"{0}\" için sonuç bulunamadı",
"HeaderAllRecordings": "Tüm Kayıtlar"
"HeaderAllRecordings": "Tüm Kayıtlar",
"LabelBuildVersion": "Yapı sürümü",
"LabelServerVersion": "Sunucu sürümü",
"LabelWebVersion": "Web sürümü",
"ButtonEditUser": "Kullanıcıyı düzenle"
}

View file

@ -17,7 +17,7 @@
"HeaderUsers": "Користувачі",
"LabelBirthDate": "Дата народження",
"LabelBirthYear": "Рік народження",
"LabelCountry": "Країна",
"LabelCountry": "Країна/Регіон",
"LabelCurrentPassword": "Поточний пароль",
"LabelDeathDate": "Дата смерті",
"LabelLanguage": "Мова",
@ -1779,5 +1779,10 @@
"LabelSelectAudioNormalization": "Нормалізація звуку",
"LabelTrackGain": "Посилення треку",
"SearchResultsEmpty": "Вибачте! Нічого не знайдено за запитом «{0}»",
"HeaderAllRecordings": "Всі записи"
"HeaderAllRecordings": "Всі записи",
"LabelBuildVersion": "Версія збірки",
"LabelServerVersion": "Версія сервера",
"LabelWebVersion": "Версія вебу",
"ButtonEditUser": "Редагувати користувача",
"DlnaMovedMessage": "Функціональність DLNA перенесено у плагін."
}

View file

@ -24,7 +24,7 @@
"HeaderSystemDlnaProfiles": "Cấu Hình Hệ Thống",
"HeaderUsers": "Người dùng",
"LabelAudioLanguagePreference": "Ngôn ngữ thoại ưa thích",
"LabelCountry": "Quốc gia",
"LabelCountry": "Quốc gia/Vùng",
"LabelCurrentPassword": "Mật khẩu hiện tại",
"LabelDay": "Ngày trong tuần",
"LabelEnableDlnaPlayTo": "Bật tính năng 'Phát Với' DLNA",
@ -35,7 +35,7 @@
"LabelNewPassword": "Mật khẩu mới",
"LabelNewPasswordConfirm": "Xác nhận mật khẩu mới",
"LabelSaveLocalMetadata": "Lưu các ảnh bìa minh họa và dữ liệu mô tả vào trong các thư mục phương tiện",
"LabelSaveLocalMetadataHelp": "Lưu ảnh bìa minh họa vào các thư mục phương tiện giúp đưa chúng vào một nơi dễ chỉnh sửa hơn.",
"LabelSaveLocalMetadataHelp": "Lưu ảnh bìa minh họa vào thư mục phương tiện để đưa chúng vào nơi dễ chỉnh sửa hơn.",
"LabelTime": "Thời gian",
"LabelYoureDone": "Bạn đã hoàn thành!",
"MaxParentalRatingHelp": "Nội dung với đánh giá cao hơn sẽ được ẩn đi từ người dùng này.",
@ -99,7 +99,7 @@
"ButtonGotIt": "Hiểu rồi",
"ButtonFullscreen": "Toàn màn hình",
"ButtonForgotPassword": "Quên mật khẩu",
"ButtonEditOtherUserPreferences": "Chỉnh sửa hồ sơ, ảnh và sở thích cá nhân của người dùng này.",
"ButtonEditOtherUserPreferences": "Sửa hồ sơ, ảnh và sở thích cá nhân của người dùng này.",
"ButtonChangeServer": "Đổi máy chủ",
"ButtonBack": "Quay lại",
"ButtonAudioTracks": "Bản Ghi Âm Thanh",
@ -206,7 +206,7 @@
"BoxSet": "",
"Box": "Hộp",
"Banner": "Ảnh bìa",
"Art": "",
"Art": "Minh họa",
"Artist": "Ca Sĩ",
"AllowFfmpegThrottlingHelp": "Tạm dừng quá trình chuyển mã hoặc chuyển đổi định dạng để tiết kiệm tài nguyên máy chủ khi việc này đã đủ để phát so với vị trí hiện tại. Nó hữu ích khi xem mà không cần tua. Tắt tính năng này nếu bạn gặp sự cố phát lại.",
"AllowFfmpegThrottling": "Điều tiết sự chuyển mã",
@ -235,7 +235,7 @@
"DeleteDeviceConfirmation": "Bạn có chắc chắn muốn xoá thiết bị này? Nó sẽ xuất hiện lại khi người dùng đăng nhập bằng thiết bị đó.",
"DeinterlaceMethodHelp": "Chọn cách khử xen kẽ để dùng khi phần mềm chuyển mã xen kẽ nội dung. Khi hỗ trợ tăng tốc phần cứng khử xen kẽ được bật, trình khử xen kẽ phần cứng sẽ dùng cài đặt này.",
"DefaultSubtitlesHelp": "Phụ đề được tải dựa trên mặc định và bắt buộc gắn cờ trong dữ liệu mô tả. Ngôn ngữ ưa thích được xem xét khi có nhiều lựa chọn.",
"DefaultMetadataLangaugeDescription": "Đây là thiết lập mặc định chung, bạn có thể tuỳ chỉnh thiết lập riêng cho từng thư viện.",
"DefaultMetadataLangaugeDescription": "Đây là mặc định của bạn và có thể tùy chỉnh trên cơ sở từng thư viện.",
"DisplayModeHelp": "Chọn kiểu bố trí giao diện mà bạn muốn.",
"Download": "Tải về",
"Down": "Xuống",
@ -243,9 +243,9 @@
"EnableCinemaMode": "Chế độ rạp phim",
"EnableBackdropsHelp": "Hiển thị phông nền trong nền của một số trang trong khi duyệt thư viện.",
"EditSubtitles": "Chỉnh sửa phụ đề",
"EditMetadata": "Chỉnh sửa thông tin",
"EditImages": "Chỉnh sửa hình ảnh",
"Edit": "Chỉnh sửa",
"EditMetadata": "Sửa dữ liệu mô tả",
"EditImages": "Sửa ảnh",
"Edit": "Sửa",
"EasyPasswordHelp": "Mã PIN Tiện Lợi dùng cho việc truy cập ngoại tuyến trên thiết bị hỗ trợ và cũng dùng để đăng nhập trong mạng dễ dàng.",
"DropShadow": "Bóng đổ",
"DrmChannelsNotImported": "Những kênh được bảo vệ bản quyền sẽ không được nhập vào.",
@ -405,7 +405,7 @@
"HeaderError": "Lỗi",
"HeaderEnabledFieldsHelp": "Bỏ chọn một mục để khóa nó và ngăn dữ liệu của nó bị thay đổi.",
"HeaderEnabledFields": "Những Mục Được Kích Hoạt",
"HeaderEditImages": "Chỉnh Sửa Hình Ảnh",
"HeaderEditImages": "Sửa Ảnh",
"HeaderEasyPinCode": "Mã PIN Tiện Lợi",
"HeaderDownloadSync": "Tải Xuống Và Đồng Bộ",
"HeaderDirectPlayProfileHelp": "Thêm cấu hình phát lại trực tiếp để chỉ ra những định dạng mà thiết bị có thể xử lý nguyên bản.",
@ -439,7 +439,7 @@
"HeaderScenes": "Phân Cảnh",
"HeaderRunningTasks": "Những Tác Vụ Hoạt Động",
"HeaderRevisionHistory": "Lịch Sử Chỉnh Sửa",
"HeaderResponseProfileHelp": "Hồ sơ phản hồi là phương thức tuỳ chỉnh thông tin gửi về thiết bị phát khi phát một số nội dung nhất định.",
"HeaderResponseProfileHelp": "Cấu hình phản hồi là phương thức tuỳ chỉnh thông tin gửi về thiết bị phát khi phát một số loại phương tiện.",
"HeaderResponseProfile": "Hồ Sơ Phản Hồi",
"HeaderRemoveMediaLocation": "Xoá Đường Dẫn Nội Dung",
"HeaderRemoveMediaFolder": "Xoá Thư Mục Phương Tiện",
@ -605,7 +605,7 @@
"HeaderDVR": "DVR",
"LabelExtractChaptersDuringLibraryScanHelp": "Tạo hình ảnh phân cảnh khi video được nhập trong quá trình quét thư viện. Nếu không chúng sẽ được trích xuất thông qua những tác vụ định kì, giúp cho quá trình quét thư viện diễn ra nhanh hơn.",
"LabelExtractChaptersDuringLibraryScan": "Trích xuất hình ảnh phân đoạn khi quét thư viện",
"LabelBaseUrlHelp": "Thêm một thư mục con tùy chỉnh vào đường dẫn máy chủ. Ví dụ: <code>http://example.com/<b>&lt;baseurl&gt;</b></code>",
"LabelBaseUrlHelp": "Thêm một thư mục con tùy chỉnh vào URL máy chủ. Ví dụ: <code>http://example.com/<b>&lt;baseurl&gt;</b></code>",
"LabelLoginDisclaimerHelp": "Một thông báo sẽ được hiển thị ở cuối trang đăng nhập.",
"LabelLoginDisclaimer": "Hiển thị khi đăng nhập",
"LabelLockItemToPreventChanges": "Khoá mục này để ngăn những thay đổi trong tương lai",
@ -954,7 +954,7 @@
"LiveBroadcasts": "Chương trình phát sóng trực tiếp",
"Live": "Trực tiếp",
"List": "Danh sách",
"LibraryAccessHelp": "Chọn thư viện để chia sẻ với người dùng này. Quản trị viên có thể chỉnh sửa các thư mục bằng trình quản lý dữ liệu mô tả.",
"LibraryAccessHelp": "Chọn thư viện chia sẻ với người dùng này. Quản trị viên có thể sửa các thư mục bằng trình quản lý dữ liệu mô tả.",
"LeaveBlankToNotSetAPassword": "Bạn có thể để trống trường này để không đặt mật khẩu.",
"LearnHowYouCanContribute": "Tìm hiểu cách bạn có thể đóng góp.",
"LatestFromLibrary": "{0} Đã Thêm Vào Gần Đây",
@ -999,7 +999,7 @@
"LabelTranscodingProgress": "Tiến trình chuyển mã",
"LabelTranscodingFramerate": "Tốc độ khung hình chuyển mã",
"LabelTranscodes": "Chuyển mã",
"LabelTranscodingTempPathHelp": "Chỉ định một đường dẫn tùy chỉnh cho các tệp chuyển mã được cung cấp cho máy khách. Để trống để sử dụng mặc định của máy chủ.",
"LabelTranscodingTempPathHelp": "Tùy chỉnh một đường dẫn cho các tệp chuyển mã để cung cấp cho máy khách. Để trống để dùng mặc định.",
"LabelTranscodePath": "Đường dẫn chuyển mã",
"LabelTrackNumber": "Số bản ghi",
"LabelTitle": "Tiêu đề",
@ -1271,7 +1271,7 @@
"Transcoding": "Chuyển mã",
"Trailers": "Đoạn giới thiệu",
"TabAccess": "Truy cập",
"SystemDlnaProfilesHelp": "Cấu hình hệ thống ở chế độ chỉ đọc. Thay đổi cấu hình hệ thống sẽ được lưu vào cấu hình tùy chỉnh mới.",
"SystemDlnaProfilesHelp": "Cấu hình hệ thống chỉ đọc. Thay đổi cấu hình hệ thống sẽ được lưu vào cấu hình tùy chỉnh mới.",
"Sports": "Thể thao",
"SpecialFeatures": "Các tính năng đặc biệt",
"SortName": "Sắp xếp tên",
@ -1322,7 +1322,7 @@
"UnsupportedPlayback": "Jellyfin không thể giải mã nội dung bảo vệ DRM nhưng sẽ cố gắng thử với tất cả nội dung, bao gồm cả các tiêu đề được bảo vệ. Một số tệp có thể xuất hiện toàn màu đen do mã hóa hoặc tính năng không được hỗ trợ, chẳng hạn như tiêu đề tương tác.",
"EnableBlurHashHelp": "Hình ảnh đang được tải sẽ được hiển thị với một trình giữ chỗ duy nhất.",
"ButtonPlayer": "Trình Phát",
"LabelOpenclDeviceHelp": "Đây là thiết bị OpenCL dùng để chỉnh tông màu. Phía bên trái của dấu chấm là số nền tảng và phía bên phải là số thiết bị trên nền tảng. Mặc định là 0.0. Bắt buộc tệp ứng dụng FFmpeg phải có tính năng tăng tốc phần cứng OpenCL.",
"LabelOpenclDeviceHelp": "Đây là thiết bị OpenCL dùng để chỉnh tông màu. Phía bên trái của dấu chấm là số nền tảng và phía bên phải là số thiết bị trên nền tảng. Mặc định là 0.0. Cần có tệp ứng dụng FFmpeg chứa phương pháp tăng tốc phần cứng OpenCL.",
"LabelMaxMuxingQueueSizeHelp": "Số gói tối đa có thể được lưu vào bộ đệm trong khi chờ tất cả các luồng khởi tạo. Hãy thử tăng lên nếu bạn vẫn gặp lỗi \"Quá nhiều gói được lưu vào bộ đệm cho luồng đầu ra\" trong nhật ký FFmpeg. Giá trị đề xuất là 2048.",
"ClearQueue": "Xóa hàng đợi",
"LabelTonemappingParamHelp": "Điều chỉnh thuật toán ánh xạ tông màu. Các giá trị được đề xuất và mặc định là NaN. Nói chung là để trống.",
@ -1427,7 +1427,7 @@
"HeaderDeleteDevices": "Xóa Tất Cả Thiết Bị",
"DeleteDevicesConfirmation": "Bạn có chắc muốn xóa hết các thiết bị không? Tất cả các phiên khác sẽ được đăng xuất. Thiết bị sẽ xuất hiện lại vào lần tiếp theo khi người dùng đăng nhập.",
"DeleteAll": "Xóa Hết",
"EnableFallbackFontHelp": "Bật phông chữ thay thế tùy chỉnh. Điều này có thể tránh sự cố hiển thị phụ đề không chính xác.",
"EnableFallbackFontHelp": "Bật phông chữ thay thế tùy chỉnh. Nó có thể tránh lỗi kết xuất phụ đề không chính xác.",
"EnableFallbackFont": "Bật phông chữ dự phòng",
"LabelFallbackFontPathHelp": "Những phông chữ này được một số máy khách sử dụng để hiển thị phụ đề. Vui lòng tham khảo tài liệu để biết thêm thông tin.",
"LabelFallbackFontPath": "Đường dẫn thư mục phông chữ dự phòng",
@ -1665,7 +1665,7 @@
"VideoRangeTypeNotSupported": "Không hỗ trợ loại dải động của video",
"Interview": "Phỏng vấn",
"Sample": "Mẫu",
"Trailer": "",
"Trailer": "Đoạn giới thiệu",
"TypeOptionPluralVideo": "Videos",
"ButtonSpace": "Cách",
"ButtonBackspace": "Xóa",
@ -1722,7 +1722,7 @@
"EnableAudioNormalization": "Chuẩn hóa âm thanh",
"GetThePlugin": "Nhận phần bổ sung",
"Notifications": "Thông báo",
"NotificationsMovedMessage": "Chức năng thông báo đã chuyển sang plugin Webhook.",
"NotificationsMovedMessage": "Chức năng thông báo đã được chuyển sang plugin Webhook.",
"LabelEnableLUFSScan": "Bật tính năng quét LUFS",
"LabelEnableLUFSScanHelp": "Khách hàng có thể bình thường hóa việc phát lại âm thanh để có được âm lượng như nhau trên các bản nhạc. Điều này sẽ khiến việc quét thư viện lâu hơn và tốn nhiều tài nguyên hơn.",
"PasswordRequiredForAdmin": "Cần có mật khẩu cho tài khoản quản trị viên.",
@ -1761,9 +1761,14 @@
"LabelSelectAudioNormalization": "Chuẩn Hóa Âm Thanh",
"SearchResultsEmpty": "Rất tiếc! Không có kết quả tìm thấy cho \"{0}\"",
"HeaderAllRecordings": "Tất Cả Bản Ghi",
"LabelTrackGain": "Chỉnh Nhạc",
"LabelTrackGain": "Điều Chỉnh Nhạc",
"GridView": "Xem Lưới",
"ListView": "Xem Danh Sách",
"SelectAudioNormalizationHelp": "Chỉnh nhạc - điều chỉnh âm lượng của mỗi bản nhạc để chúng phát lại với cùng một độ to. Chỉnh Album - điều chỉnh âm lượng của tất cả các bản nhạc trong một album, giữ nguyên dải động của album.",
"LabelAlbumGain": "Chỉnh Album"
"SelectAudioNormalizationHelp": "Điều chỉnh nhạc - điều chỉnh âm lượng của mỗi bản nhạc để chúng phát lại với cùng một độ to. Điều chỉnh album - điều chỉnh âm lượng tất cả bản nhạc trong album, giữ nguyên dải động của album.",
"LabelAlbumGain": "Điều Chỉnh Album",
"LabelSegmentKeepSecondsHelp": "Thời gian tính bằng giây mà các phân đoạn cần được giữ lại trước khi chúng bị ghi đè. Phải lớn hơn \"Throle after\". Chỉ hoạt động nếu tính năng xóa phân đoạn được bật.",
"MenuClose": "Đóng Menu",
"MenuOpen": "Mở Menu",
"UserMenu": "Menu Người Dùng",
"LogLevel.Trace": "Dấu vết"
}

View file

@ -272,7 +272,7 @@
"HeaderImageSettings": "图片设置",
"HeaderInstall": "安装",
"HeaderInstantMix": "速成合辑",
"HeaderKodiMetadataHelp": "要启用或禁用 NFO 元数据,请编辑库并找到“元数据保护程序”部分。",
"HeaderKodiMetadataHelp": "要启用或禁用 NFO 元数据,请编辑库并找到“元数据储存方式”部分。",
"HeaderLatestEpisodes": "新增剧集",
"HeaderLatestMedia": "新增媒体",
"HeaderLatestMovies": "新增电影",
@ -1782,5 +1782,9 @@
"SelectAudioNormalizationHelp": "音轨增益 - 调整每个音轨的音量,使它们播放时具有相同的响度。专辑增益 - 只调整专辑中所有音轨的音量,保持专辑的动态范围。",
"LabelAlbumGain": "专辑增益",
"LabelSelectAudioNormalization": "音频标准化",
"HeaderAllRecordings": "所有录制的节目"
"HeaderAllRecordings": "所有录制的节目",
"LabelServerVersion": "服务器版本",
"LabelWebVersion": "网页版本",
"LabelBuildVersion": "构建版本",
"ButtonEditUser": "编辑用户"
}

View file

@ -1179,5 +1179,76 @@
"NoNewDevicesFound": "未偵測到調諧器。請關閉此對話框,手動輸入裝置資料。",
"OptionEnableForAllTuners": "為所有調諧器啟用",
"MessageConfirmDeleteTunerDevice": "你確定要刪除此裝置嗎?",
"LabelDate": "日期"
"LabelDate": "日期",
"LabelDeveloper": "開發者",
"Select": "選擇",
"MediaInfoProfile": "設定檔",
"MessageConfirmShutdown": "你確定要關閉這個服務器嗎?",
"SeriesYearToPresent": "{0} - 現在",
"SubtitleBlue": "藍",
"SaveChanges": "儲存變更",
"RepeatOne": "重複播放一次",
"Yesterday": "昨天",
"ButtonEditUser": "更改用戶",
"Whitelist": "白名單",
"Movie": "電影",
"Settings": "設定",
"OptionMax": "最大",
"Quality": "品質",
"PlayCount": "播放次數",
"SelectServer": "選擇服務器",
"OptionWeekdays": "平日",
"RepeatAll": "重複播放所有",
"ShowYear": "顯示年份",
"NumLocationsValue": "{0} 資料夾",
"PlayFromBeginning": "從頭播放",
"Profile": "設定檔",
"QuickConnect": "快速連結",
"MySubtitles": "我的字幕",
"Subtitles": "字幕",
"Repeat": "重複播放",
"Subtitle": "字幕",
"Rewind": "倒回",
"ResetPassword": "重置密碼",
"SubtitleBlack": "黑",
"SubtitleGray": "灰",
"Bold": "粗體",
"LabelWebVersion": "網站版本",
"SecondarySubtitles": "副字幕",
"Played": "已播放",
"TabPlugins": "插件",
"TagsValue": "標籤: {0}",
"Trailers": "預告片",
"Transcoding": "轉碼",
"Unmute": "取消靜音",
"ValueCodec": "編解碼器: {0}",
"Writer": "編劇",
"Yes": "是",
"OptionWeekends": "周末",
"Print": "列印",
"Share": "分享",
"TabCodecs": "編解碼器",
"Tags": "標籤",
"Series": "系列",
"SearchResults": "搜索結果",
"OptionRandom": "隨機",
"Preview": "預覽",
"Suggestions": "建議",
"SubtitleYellow": "黃",
"Menu": "菜單",
"RememberMe": "保持登入狀態",
"Search": "搜索",
"HeaderAllRecordings": "所有錄影的節目",
"MenuOpen": "打開菜單",
"TitleHardwareAcceleration": "硬件加速",
"Upload": "上傳",
"Watched": "已觀看",
"Writers": "編劇",
"SubtitleRed": "紅",
"SubtitleWhite": "白",
"LabelSystem": "系統",
"ShowMore": "顯示更多",
"YoutubeBadRequest": "錯誤的請求。",
"MillisecondsUnit": "毫秒",
"MediaInfoTitle": "標題"
}

View file

@ -67,7 +67,7 @@
"LabelCachePath": "快取路徑",
"LabelCollection": "收藏櫃",
"LabelContentType": "內容類型",
"LabelCountry": "國家",
"LabelCountry": "國家/地區",
"LabelCurrentPassword": "當前的密碼",
"LabelDay": "星期",
"LabelEnableDlnaClientDiscoveryInterval": "尋找用戶端時間間隔",
@ -203,7 +203,7 @@
"WelcomeToProject": "歡迎使用 Jellyfin",
"WizardCompleted": "這就是我們所需的全部資訊Jellyfin 現在正在收集你的媒體櫃的資料,在這段時間內,不妨參考我們推出的應用程式。按一下<b>完成</b>進入<b>控制台</b>。",
"Actor": "演員",
"AddToPlayQueue": "加入播放佇列",
"AddToPlayQueue": "加入播放佇列",
"AddToPlaylist": "加入播放清單",
"Absolute": "絕對",
"AccessRestrictedTryAgainLater": "目前存取受限,請稍後再試。",
@ -264,7 +264,7 @@
"ButtonMore": "更多",
"ButtonNetwork": "網路",
"ButtonNextTrack": "下一首",
"ButtonOpen": "開",
"ButtonOpen": "開",
"ButtonParentalControl": "家長控制",
"ButtonPause": "暫停",
"ButtonPreviousTrack": "上一首",
@ -757,7 +757,7 @@
"No": "不要",
"Off": "關閉",
"OptionAdminUsers": "管理員",
"OptionAllowRemoteControlOthers": "允許其他使用者遠端控制",
"OptionAllowRemoteControlOthers": "允許遠端控制其他用戶的裝置",
"OptionReleaseDate": "釋出日期",
"OptionWeekends": "假日",
"PlayNextEpisodeAutomatically": "自動播放下一集",
@ -887,7 +887,7 @@
"PerfectMatch": "最佳配對",
"PictureInPicture": "浮窗播放",
"PlayFromBeginning": "從頭開始播放",
"PlayNext": "播放下一個",
"PlayNext": "下一個播放",
"Next": "下一個",
"OptionCaptionInfoExSamsung": "CaptionInfoEx三星",
"People": "人物",
@ -1781,5 +1781,9 @@
"LabelSelectAudioNormalization": "音量標準化",
"SelectAudioNormalizationHelp": "音軌增益:調整個別音軌的增益以獲得一致的音量。專輯增益:調整專輯中全部音軌的音量。",
"LabelAlbumGain": "專輯增益",
"HeaderAllRecordings": "所有錄製的節目"
"HeaderAllRecordings": "所有錄製的節目",
"ButtonEditUser": "編輯用戶",
"LabelServerVersion": "伺服器版本",
"LabelBuildVersion": "組建版本",
"LabelWebVersion": "網頁版本"
}

View file

@ -47,6 +47,10 @@ const config = {
},
plugins: [
new DefinePlugin({
__JF_BUILD_VERSION__: JSON.stringify(
process.env.WEBPACK_SERVE ?
'Dev Server' :
process.env.JELLYFIN_VERSION || 'Release'),
__USE_SYSTEM_FONTS__: JSON.stringify(!!process.env.USE_SYSTEM_FONTS),
__WEBPACK_SERVE__: JSON.stringify(!!process.env.WEBPACK_SERVE)
}),
@ -204,6 +208,7 @@ const config = {
path.resolve(__dirname, 'node_modules/screenfull'),
path.resolve(__dirname, 'node_modules/ssr-window'),
path.resolve(__dirname, 'node_modules/swiper'),
path.resolve(__dirname, 'node_modules/usehooks-ts'),
path.resolve(__dirname, 'src')
],
use: [{