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 create-subtitles-keyboard-shortcut

This commit is contained in:
Baker Ousley 2024-10-14 11:31:33 -05:00 committed by GitHub
commit 6102f94f02
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
86 changed files with 3369 additions and 2371 deletions

View file

@ -1,20 +1,12 @@
name: Automation
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: Automation 🎛️
on:
push:
branches:
- master
pull_request_target:
workflow_call:
jobs:
conflicts:
name: Merge conflict labeling
name: Merge conflict labeling 🏷️
runs-on: ubuntu-latest
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
steps:
- uses: eps1lon/actions-label-merge-conflict@1b1b1fcde06a9b3d089f3464c96417961dde1168 # v3.0.2
with:

40
.github/workflows/__codeql.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: GitHub CodeQL 🔬
on:
workflow_call:
inputs:
commit:
required: true
type: string
jobs:
analyze:
name: Analyze ${{ matrix.language }} 🔬
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language:
- javascript-typescript
steps:
- name: Checkout repository ⬇️
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
ref: ${{ inputs.commit }}
show-progress: false
- name: Initialize CodeQL 🛠️
uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12
with:
queries: security-and-quality
languages: ${{ matrix.language }}
- name: Autobuild 📦
uses: github/codeql-action/autobuild@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12
- name: Perform CodeQL Analysis 🧪
uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12
with:
category: '/language:${{matrix.language}}'

59
.github/workflows/__deploy.yml vendored Normal file
View file

@ -0,0 +1,59 @@
name: Deploy 🏗️
on:
workflow_call:
inputs:
branch:
required: true
type: string
commit:
required: false
type: string
comment:
required: false
type: boolean
artifact_name:
required: false
type: string
default: frontend
jobs:
cf-pages:
name: CloudFlare Pages 📃
runs-on: ubuntu-latest
environment:
name: ${{ inputs.branch == 'master' && 'Production' || 'Preview' }}
url: ${{ steps.cf.outputs.deployment-url }}
outputs:
url: ${{ steps.cf.outputs.deployment-url }}
steps:
- name: Download workflow artifact ⬇️
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: ${{ inputs.artifact_name }}
path: dist
- name: Publish to Cloudflare Pages 📃
uses: cloudflare/wrangler-action@9681c2997648301493e78cacbfb790a9f19c833f # v3.9.0
id: cf
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=jellyfin-web --branch=${{ inputs.branch }}
compose-comment:
name: Compose and push comment 📝
# Always run so the comment is composed for the workflow summary
if: ${{ always() }}
uses: ./.github/workflows/__job_messages.yml
secrets: inherit
needs:
- cf-pages
with:
branch: ${{ inputs.branch }}
commit: ${{ inputs.commit }}
preview_url: ${{ needs.cf-pages.outputs.url }}
in_progress: false
comment: ${{ inputs.comment }}

65
.github/workflows/__job_messages.yml vendored Normal file
View file

@ -0,0 +1,65 @@
name: Job messages ⚙️
on:
workflow_call:
inputs:
branch:
required: false
type: string
commit:
required: true
type: string
preview_url:
required: false
type: string
in_progress:
required: true
type: boolean
comment:
required: false
type: boolean
marker:
description: Hidden marker to detect PR comments composed by the bot
required: false
type: string
default: "CFPages-deployment"
jobs:
cf_pages_msg:
name: CloudFlare Pages deployment 📃🚀
runs-on: ubuntu-latest
steps:
- name: Compose message 📃
if: ${{ always() }}
id: compose
env:
COMMIT: ${{ inputs.commit }}
PREVIEW_URL: ${{ inputs.preview_url != '' && (inputs.branch != 'master' && inputs.preview_url || format('https://jellyfin-web.pages.dev ({0})', inputs.preview_url)) || 'Not available' }}
DEPLOY_STATUS: ${{ inputs.in_progress && '🔄 Deploying...' || (inputs.preview_url != '' && '✅ Deployed!' || '❌ Failure. Check workflow logs for details') }}
DEPLOYMENT_TYPE: ${{ inputs.branch != 'master' && '🔀 Preview' || '⚙️ Production' }}
WORKFLOW_RUN: ${{ !inputs.in_progress && format('**[View build logs](https://github.com/{0}/actions/runs/{1})**', github.repository, github.run_id) || '' }}
# EOF is needed for multiline environment variables in a GitHub Actions context
run: |
echo "## Cloudflare Pages deployment" > $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| **Latest commit** | <code>${COMMIT::7}</code> |" >> $GITHUB_STEP_SUMMARY
echo "|------------------------- |:----------------------------: |" >> $GITHUB_STEP_SUMMARY
echo "| **Status** | $DEPLOY_STATUS |" >> $GITHUB_STEP_SUMMARY
echo "| **Preview URL** | $PREVIEW_URL |" >> $GITHUB_STEP_SUMMARY
echo "| **Type** | $DEPLOYMENT_TYPE |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "$WORKFLOW_RUN" >> $GITHUB_STEP_SUMMARY
COMPOSED_MSG=$(cat $GITHUB_STEP_SUMMARY)
echo "msg<<EOF" >> $GITHUB_ENV
echo "$COMPOSED_MSG" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Push comment to Pull Request 🔼
uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0
if: ${{ inputs.comment && steps.compose.conclusion == 'success' }}
with:
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
message: ${{ env.msg }}
comment_tag: ${{ inputs.marker }}

45
.github/workflows/__package.yml vendored Normal file
View file

@ -0,0 +1,45 @@
name: Packaging 📦
on:
workflow_call:
inputs:
commit:
required: false
type: string
jobs:
run-build-prod:
name: Run production build 🏗️
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
ref: ${{ inputs.commit || github.sha }}
- name: Setup node environment
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
node-version: 20
cache: npm
check-latest: true
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run a production build
env:
JELLYFIN_VERSION: ${{ inputs.commit || github.sha }}
run: npm run build:production
- name: Update config.json for testing
run: |
jq '.multiserver=true | .servers=["https://demo.jellyfin.org/unstable"]' dist/config.json > dist/config.tmp.json
mv dist/config.tmp.json dist/config.json
- name: Upload artifact
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: frontend
path: dist

61
.github/workflows/__quality_checks.yml vendored Normal file
View file

@ -0,0 +1,61 @@
name: Quality checks 👌🧪
on:
workflow_call:
inputs:
commit:
required: true
type: string
workflow_dispatch:
jobs:
dependency-review:
name: Vulnerable dependencies 🔎
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
ref: ${{ inputs.commit }}
show-progress: false
- name: Scan
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
with:
## Workaround from https://github.com/actions/dependency-review-action/issues/456
## TODO: Remove when necessary
base-ref: ${{ github.event.pull_request.base.sha || 'master' }}
head-ref: ${{ github.event.pull_request.head.sha || github.ref }}
quality:
name: Run ${{ matrix.command }} 🕵️‍♂️
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
command:
- build:es-check
- lint
- stylelint
- build:check
- test
steps:
- name: Checkout ⬇️
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
ref: ${{ inputs.commit }}
show-progress: false
- name: Setup node environment ⚙️
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
node-version: 20
cache: npm
check-latest: true
- name: Install dependencies 📦
run: npm ci --no-audit
- name: Run ${{ matrix.command }} ⚙️
run: npm run ${{ matrix.command }}

View file

@ -1,129 +0,0 @@
name: Build
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
on:
push:
branches: [ master, release* ]
pull_request_target:
branches: [ master, release* ]
workflow_dispatch:
jobs:
run-build-prod:
name: Run production build
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node environment
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 20
check-latest: true
cache: npm
- name: Install Node.js dependencies
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
run: |
jq '.multiserver=true | .servers=["https://demo.jellyfin.org/unstable"]' dist/config.json > dist/config.tmp.json
mv dist/config.tmp.json dist/config.json
- name: Upload artifact
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: jellyfin-web__prod
path: dist
publish:
name: Deploy to Cloudflare Pages
runs-on: ubuntu-latest
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
needs:
- run-build-prod
permissions:
contents: read
deployments: write
steps:
- name: Add comment
uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0
if: ${{ github.event_name == 'pull_request_target' }}
with:
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
message: |
## Cloudflare Pages deployment
| **Latest commit** | <code>${{ github.event.pull_request.head.sha || github.sha }}</code> |
|-------------------|:-:|
| **Status** | 🔄 Deploying... |
| **Preview URL** | Not available |
| **Type** | 🔀 Preview |
pr_number: ${{ github.event.pull_request.number }}
comment_tag: CFPages-deployment
mode: recreate
- name: Download workflow artifact
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: jellyfin-web__prod
path: dist
- name: Publish to Cloudflare
id: cf
uses: cloudflare/wrangler-action@f84a562284fc78278ff9052435d9526f9c718361 # v3.7.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=jellyfin-web --branch=${{
(github.event_name != 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository)
&& (github.event.pull_request.head.ref || github.ref_name)
|| format('{0}/{1}', github.event.pull_request.head.repo.full_name, github.event.pull_request.head.ref)
}} --commit-hash=${{ github.event.pull_request.head.sha || github.sha }}
- name: Update status comment (Success)
if: ${{ github.event_name == 'pull_request_target' && success() }}
uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0
with:
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
message: |
## Cloudflare Pages deployment
| **Latest commit** | <code>${{ github.event.pull_request.head.sha || github.sha }}</code> |
|-------------------|:-:|
| **Status** | ✅ Deployed! |
| **Preview URL** | ${{ steps.cf.outputs.deployment-url != '' && steps.cf.outputs.deployment-url || 'Not available' }} |
| **Type** | 🔀 Preview |
pr_number: ${{ github.event.pull_request.number }}
comment_tag: CFPages-deployment
mode: recreate
- name: Update status comment (Failure)
if: ${{ github.event_name == 'pull_request_target' && failure() }}
uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0
with:
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
message: |
## Cloudflare Pages deployment
| **Latest commit** | <code>${{ github.event.pull_request.head.sha || github.sha }}</code> |
|-------------------|:-:|
| **Status** | ❌ Failure. Check workflow logs for details |
| **Preview URL** | Not available |
| **Type** | 🔀 Preview |
pr_number: ${{ github.event.pull_request.number }}
comment_tag: CFPages-deployment
mode: recreate

View file

@ -1,34 +0,0 @@
name: CodeQL
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
on:
push:
branches: [ master, release* ]
pull_request:
branches: [ master, release* ]
schedule:
- cron: '30 7 * * 6'
jobs:
codeql:
name: Run CodeQL
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Initialize CodeQL
uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
with:
languages: javascript
queries: +security-extended
- name: Autobuild
uses: github/codeql-action/autobuild@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6

View file

@ -1,36 +0,0 @@
name: Commands
on:
issue_comment:
types:
- created
- edited
jobs:
rebase:
name: Rebase
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '@jellyfin-bot rebase') && github.event.comment.author_association == 'MEMBER'
runs-on: ubuntu-latest
steps:
- name: Notify as seen
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
with:
token: ${{ secrets.JF_BOT_TOKEN }}
comment-id: ${{ github.event.comment.id }}
reactions: '+1'
- name: Checkout the latest code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0
- name: Automatic Rebase
uses: cirrus-actions/rebase@b87d48154a87a85666003575337e27b8cd65f691 # 1.8
env:
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
- name: Comment on failure
if: failure()
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
with:
token: ${{ secrets.JF_BOT_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
I'm sorry @${{ github.event.comment.user.login }}, I'm afraid I can't do that.

View file

@ -1,36 +0,0 @@
name: PR suggestions
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.id || github.run_id }}
cancel-in-progress: true
on:
pull_request_target:
branches: [ master, release* ]
jobs:
run-eslint:
name: Run eslint suggestions
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup node environment
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 20
check-latest: true
cache: npm
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run eslint
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
uses: CatChen/eslint-suggestion-action@09aa3e557bafa4bebe3e026d8808bffff08e67a9 # v4.1.6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

100
.github/workflows/pull_request.yml vendored Normal file
View file

@ -0,0 +1,100 @@
name: Pull Request 📥
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
on:
pull_request_target:
branches:
- master
- release*
paths-ignore:
- '**/*.md'
merge_group:
jobs:
push-comment:
name: Create comments ✍️
if: ${{ always() && !cancelled() && github.repository == 'jellyfin/jellyfin-web' }}
uses: ./.github/workflows/__job_messages.yml
secrets: inherit
with:
commit: ${{ github.event.pull_request.head.sha }}
in_progress: true
comment: true
build:
name: Build 🏗️
if: ${{ always() && !cancelled() }}
uses: ./.github/workflows/__package.yml
with:
commit: ${{ github.event.pull_request.head.sha }}
automation:
name: Automation 🎛️
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
uses: ./.github/workflows/__automation.yml
secrets: inherit
quality_checks:
name: Quality checks 👌🧪
if: ${{ always() && !cancelled() }}
uses: ./.github/workflows/__quality_checks.yml
permissions: {}
with:
commit: ${{ github.event.pull_request.head.sha }}
codeql:
name: GitHub CodeQL 🔬
if: ${{ always() && !cancelled() }}
uses: ./.github/workflows/__codeql.yml
permissions:
actions: read
contents: read
security-events: write
with:
commit: ${{ github.event.pull_request.head.sha }}
deploy:
name: Deploy 🚀
uses: ./.github/workflows/__deploy.yml
if: ${{ always() && !cancelled() && needs.build.result == 'success' && github.repository == 'jellyfin/jellyfin-web' }}
needs:
- push-comment
- build
permissions:
contents: read
deployments: write
secrets: inherit
with:
# If the PR is from the master branch of a fork, append the fork's name to the branch name
branch: ${{ github.event.pull_request.head.repo.full_name != github.repository && github.event.pull_request.head.ref == 'master' && format('{0}/{1}', github.event.pull_request.head.repo.full_name, github.event.pull_request.head.ref) || github.event.pull_request.head.ref }}
comment: true
commit: ${{ github.event.pull_request.head.sha }}
run-eslint:
name: Run eslint suggestions
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup node environment
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
node-version: 20
cache: npm
check-latest: true
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run eslint
uses: CatChen/eslint-suggestion-action@09aa3e557bafa4bebe3e026d8808bffff08e67a9 # v4.1.6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

58
.github/workflows/push.yml vendored Normal file
View file

@ -0,0 +1,58 @@
name: Push & Release 🌍
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref }}
cancel-in-progress: true
on:
push:
branches:
- master
- release*
paths-ignore:
- '**/*.md'
jobs:
automation:
name: Automation 🎛️
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
uses: ./.github/workflows/__automation.yml
secrets: inherit
main:
name: 'Unstable release 🚀⚠️'
uses: ./.github/workflows/__package.yml
with:
commit: ${{ github.sha }}
quality_checks:
name: Quality checks 👌🧪
if: ${{ always() && !cancelled() }}
uses: ./.github/workflows/__quality_checks.yml
permissions: {}
with:
commit: ${{ github.sha }}
codeql:
name: GitHub CodeQL 🔬
uses: ./.github/workflows/__codeql.yml
permissions:
actions: read
contents: read
security-events: write
with:
commit: ${{ github.sha }}
deploy:
name: Deploy 🚀
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
uses: ./.github/workflows/__deploy.yml
needs:
- main
permissions:
contents: read
deployments: write
secrets: inherit
with:
branch: ${{ github.ref_name }}
comment:

View file

@ -1,123 +0,0 @@
name: Quality checks
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
on:
push:
branches: [ master, release* ]
pull_request:
branches: [ master, release* ]
jobs:
run-escheck:
name: Run es-check
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup node environment
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 20
check-latest: true
cache: npm
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run a production build
run: npm run build:production
- name: Run es-check
run: npm run escheck
run-eslint:
name: Run eslint
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup node environment
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 20
check-latest: true
cache: npm
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run eslint
run: npx eslint --quiet "."
run-stylelint:
name: Run stylelint
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup node environment
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 20
check-latest: true
cache: npm
- name: Set up stylelint matcher
uses: xt0rted/stylelint-problem-matcher@34db1b874c0452909f0696aedef70b723870a583 # tag=v1
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run stylelint
run: npm run stylelint
run-tsc:
name: Run TypeScript build check
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup node environment
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 20
check-latest: true
cache: npm
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run tsc
run: npm run build:check
run-test:
name: Run tests
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup node environment
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 20
check-latest: true
cache: npm
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run test suite
run: npm run test

View file

@ -1,10 +1,9 @@
name: Stale Check
name: Scheduled tasks 🕑
on:
schedule:
- cron: '30 1 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write

3305
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -5,24 +5,24 @@
"repository": "https://github.com/jellyfin/jellyfin-web",
"license": "GPL-2.0-or-later",
"devDependencies": {
"@babel/core": "7.25.2",
"@babel/plugin-transform-modules-umd": "7.24.7",
"@babel/preset-env": "7.25.4",
"@babel/preset-react": "7.24.7",
"@babel/core": "7.25.7",
"@babel/plugin-transform-modules-umd": "7.25.7",
"@babel/preset-env": "7.25.7",
"@babel/preset-react": "7.25.7",
"@eslint-community/eslint-plugin-eslint-comments": "4.4.0",
"@stylistic/eslint-plugin": "2.8.0",
"@stylistic/eslint-plugin": "2.9.0",
"@types/dompurify": "3.0.5",
"@types/escape-html": "1.0.4",
"@types/loadable__component": "5.13.9",
"@types/lodash-es": "4.17.12",
"@types/markdown-it": "14.1.2",
"@types/react": "18.3.8",
"@types/react": "18.3.11",
"@types/react-dom": "18.3.0",
"@types/sortablejs": "1.15.8",
"@typescript-eslint/eslint-plugin": "5.62.0",
"@typescript-eslint/parser": "5.62.0",
"@uupaa/dynamic-import-polyfill": "1.0.2",
"@vitest/coverage-v8": "2.1.1",
"@vitest/coverage-v8": "2.1.2",
"autoprefixer": "10.4.20",
"babel-loader": "9.2.1",
"clean-webpack-plugin": "4.0.0",
@ -34,22 +34,22 @@
"es-check": "7.2.1",
"eslint": "8.57.1",
"eslint-plugin-compat": "4.2.0",
"eslint-plugin-import": "2.30.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsx-a11y": "6.10.0",
"eslint-plugin-react": "7.36.1",
"eslint-plugin-react": "7.37.1",
"eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-sonarjs": "0.25.1",
"expose-loader": "5.0.0",
"fork-ts-checker-webpack-plugin": "9.0.2",
"html-loader": "5.1.0",
"html-webpack-plugin": "5.6.0",
"jsdom": "25.0.0",
"jsdom": "25.0.1",
"mini-css-extract-plugin": "2.9.1",
"postcss": "8.4.47",
"postcss-loader": "8.1.1",
"postcss-preset-env": "10.0.3",
"postcss-preset-env": "10.0.6",
"postcss-scss": "4.0.9",
"sass": "1.79.3",
"sass": "1.79.4",
"sass-loader": "16.0.2",
"source-map-loader": "5.0.0",
"speed-measure-webpack-plugin": "1.5.0",
@ -61,7 +61,7 @@
"stylelint-scss": "5.3.2",
"ts-loader": "9.5.1",
"typescript": "5.6.2",
"vitest": "2.1.1",
"vitest": "2.1.2",
"webpack": "5.94.0",
"webpack-bundle-analyzer": "4.10.2",
"webpack-cli": "5.1.4",
@ -79,7 +79,7 @@
"@fontsource/noto-sans-sc": "5.1.0",
"@fontsource/noto-sans-tc": "5.1.0",
"@jellyfin/libass-wasm": "4.2.3",
"@jellyfin/sdk": "0.0.0-unstable.202410020501",
"@jellyfin/sdk": "0.0.0-unstable.202410130501",
"@mui/icons-material": "5.16.7",
"@mui/material": "5.16.7",
"@mui/x-date-pickers": "7.18.0",
@ -127,7 +127,7 @@
"whatwg-fetch": "3.6.20"
},
"optionalDependencies": {
"sass-embedded": "1.79.3"
"sass-embedded": "1.79.4"
},
"browserslist": [
"last 2 Firefox versions",
@ -152,6 +152,7 @@
"build:development": "webpack --config webpack.dev.js",
"build:production": "cross-env NODE_ENV=\"production\" webpack --config webpack.prod.js",
"build:check": "tsc --noEmit",
"build:es-check": "npm run build:production && npm run escheck",
"escheck": "es-check",
"lint": "eslint \"./\"",
"test": "vitest --watch=false --config vite.config.ts",

View file

@ -9,7 +9,7 @@ $drawer-width: 240px;
// Fix dashboard pages layout to work with drawer
.dashboardDocument {
.mainAnimatedPage {
.mainAnimatedPage:not(.metadataEditorPage) {
@media all and (min-width: $mui-bp-md) {
left: $drawer-width;
}
@ -31,4 +31,8 @@ $drawer-width: 240px;
padding-top: 3.25rem;
}
}
.metadataEditorPage {
padding-top: 3.25rem !important;
}
}

View file

@ -91,7 +91,7 @@ const ServerDrawerSection = () => {
<ListItemText inset primary={globalize.translate('Display')} />
</ListItemLink>
<ListItemLink to='/dashboard/libraries/metadata' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('Metadata')} />
<ListItemText inset primary={globalize.translate('MetadataManager')} />
</ListItemLink>
<ListItemLink to='/dashboard/libraries/nfo' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('TabNfoSettings')} />

View file

@ -1,9 +1,8 @@
import type { BaseItemDto, DeviceInfoDto, UserDto } from '@jellyfin/sdk/lib/generated-client';
import React, { useCallback, useEffect, useState, useRef } from 'react';
import React, { useCallback, useEffect, useState, useRef, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import globalize from '../../../../lib/globalize';
import toast from '../../../../components/toast/toast';
import SectionTabs from '../../../../components/dashboard/users/SectionTabs';
@ -28,6 +27,7 @@ const UserLibraryAccess = () => {
const [channelsItems, setChannelsItems] = useState<ItemsArr[]>([]);
const [mediaFoldersItems, setMediaFoldersItems] = useState<ItemsArr[]>([]);
const [devicesItems, setDevicesItems] = useState<ItemsArr[]>([]);
const libraryMenu = useMemo(async () => ((await import('../../../../scripts/libraryMenu')).default), []);
const element = useRef<HTMLDivElement>(null);
@ -133,7 +133,7 @@ const UserLibraryAccess = () => {
const loadUser = useCallback((user: UserDto, mediaFolders: BaseItemDto[], channels: BaseItemDto[], devices: DeviceInfoDto[]) => {
setUserName(user.Name || '');
libraryMenu.setTitle(user.Name);
void libraryMenu.then(menu => menu.setTitle(user.Name));
loadChannels(user, channels);
loadMediaFolders(user, mediaFolders);
loadDevices(user, devices);

View file

@ -2,11 +2,10 @@ import type { AccessSchedule, ParentalRating, UserDto } from '@jellyfin/sdk/lib/
import { UnratedItem } from '@jellyfin/sdk/lib/generated-client/models/unrated-item';
import { DynamicDayOfWeek } from '@jellyfin/sdk/lib/generated-client/models/dynamic-day-of-week';
import escapeHTML from 'escape-html';
import React, { useCallback, useEffect, useState, useRef } from 'react';
import React, { useCallback, useEffect, useState, useRef, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import globalize from '../../../../lib/globalize';
import LibraryMenu from '../../../../scripts/libraryMenu';
import AccessScheduleList from '../../../../components/dashboard/users/AccessScheduleList';
import TagList from '../../../../components/dashboard/users/TagList';
import ButtonElement from '../../../../elements/ButtonElement';
@ -69,6 +68,7 @@ const UserParentalControl = () => {
const [ accessSchedules, setAccessSchedules ] = useState<AccessSchedule[]>([]);
const [ allowedTags, setAllowedTags ] = useState<string[]>([]);
const [ blockedTags, setBlockedTags ] = useState<string[]>([]);
const libraryMenu = useMemo(async () => ((await import('../../../../scripts/libraryMenu')).default), []);
const element = useRef<HTMLDivElement>(null);
@ -188,28 +188,6 @@ const UserParentalControl = () => {
}
}, []);
const renderAccessSchedule = useCallback((schedules: AccessSchedule[]) => {
const page = element.current;
if (!page) {
console.error('[userparentalcontrol] Unexpected null page reference');
return;
}
setAccessSchedules(schedules);
const accessScheduleList = page.querySelector('.accessScheduleList') as HTMLDivElement;
for (const btnDelete of accessScheduleList.querySelectorAll('.btnDelete')) {
btnDelete.addEventListener('click', function () {
const index = parseInt(btnDelete.getAttribute('data-index') ?? '0', 10);
schedules.splice(index, 1);
const newindex = schedules.filter((_, i) => i != index);
renderAccessSchedule(newindex);
});
}
}, []);
const loadUser = useCallback((user: UserDto, allParentalRatings: ParentalRating[]) => {
const page = element.current;
@ -219,7 +197,7 @@ const UserParentalControl = () => {
}
setUserName(user.Name || '');
LibraryMenu.setTitle(user.Name);
void libraryMenu.then(menu => menu.setTitle(user.Name));
loadUnratedItems(user);
loadAllowedTags(user.Policy?.AllowedTags || []);
@ -242,9 +220,9 @@ const UserParentalControl = () => {
} else {
(page.querySelector('.accessScheduleSection') as HTMLDivElement).classList.remove('hide');
}
renderAccessSchedule(user.Policy?.AccessSchedules || []);
setAccessSchedules(user.Policy?.AccessSchedules || []);
loading.hide();
}, [loadAllowedTags, loadBlockedTags, loadUnratedItems, populateRatings, renderAccessSchedule]);
}, [loadAllowedTags, loadBlockedTags, loadUnratedItems, populateRatings]);
const loadData = useCallback(() => {
if (!userId) {
@ -285,7 +263,7 @@ const UserParentalControl = () => {
}
schedules[index] = updatedSchedule;
renderAccessSchedule(schedules);
setAccessSchedules(schedules);
}).catch(() => {
// access schedule closed
});
@ -389,7 +367,26 @@ const UserParentalControl = () => {
});
(page.querySelector('.userParentalControlForm') as HTMLFormElement).addEventListener('submit', onSubmit);
}, [loadAllowedTags, loadBlockedTags, loadData, renderAccessSchedule]);
}, [loadAllowedTags, loadBlockedTags, loadData, userId]);
useEffect(() => {
const page = element.current;
if (!page) {
console.error('[userparentalcontrol] Unexpected null page reference');
return;
}
const accessScheduleList = page.querySelector('.accessScheduleList') as HTMLDivElement;
for (const btnDelete of accessScheduleList.querySelectorAll('.btnDelete')) {
btnDelete.addEventListener('click', function () {
const index = parseInt(btnDelete.getAttribute('data-index') ?? '0', 10);
const newindex = accessSchedules.filter((_e, i) => i != index);
setAccessSchedules(newindex);
});
}
}, [accessSchedules]);
const optionMaxParentalRating = () => {
let content = '';

View file

@ -1,11 +1,10 @@
import type { BaseItemDto, NameIdPair, SyncPlayUserAccessType, UserDto } from '@jellyfin/sdk/lib/generated-client';
import escapeHTML from 'escape-html';
import React, { useCallback, useEffect, useState, useRef } from 'react';
import React, { useCallback, useEffect, useState, useRef, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import Dashboard from '../../../../utils/dashboard';
import globalize from '../../../../lib/globalize';
import LibraryMenu from '../../../../scripts/libraryMenu';
import ButtonElement from '../../../../elements/ButtonElement';
import CheckBoxElement from '../../../../elements/CheckBoxElement';
import InputElement from '../../../../elements/InputElement';
@ -42,6 +41,7 @@ const UserEdit = () => {
const [ deleteFoldersAccess, setDeleteFoldersAccess ] = useState<ResetProvider[]>([]);
const [ authProviders, setAuthProviders ] = useState<NameIdPair[]>([]);
const [ passwordResetProviders, setPasswordResetProviders ] = useState<NameIdPair[]>([]);
const libraryMenu = useMemo(async () => ((await import('../../../../scripts/libraryMenu')).default), []);
const [ authenticationProviderId, setAuthenticationProviderId ] = useState('');
const [ passwordResetProviderId, setPasswordResetProviderId ] = useState('');
@ -147,7 +147,8 @@ const UserEdit = () => {
txtUserName.disabled = false;
txtUserName.removeAttribute('disabled');
LibraryMenu.setTitle(user.Name);
void libraryMenu.then(menu => menu.setTitle(user.Name));
setUserDto(user);
(page.querySelector('#txtUserName') as HTMLInputElement).value = user.Name || '';
(page.querySelector('.chkIsAdmin') as HTMLInputElement).checked = !!user.Policy?.IsAdministrator;

View file

@ -1,4 +1,4 @@
import React, { FC, useCallback, useMemo } from 'react';
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { IconButton } from '@mui/material';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import { useQueryClient } from '@tanstack/react-query';
@ -113,6 +113,7 @@ const MoreCommandsButton: FC<MoreCommandsButtonProps> = ({
itemId: selectedItemId || itemId || ''
});
const parentId = item?.SeasonId || item?.SeriesId || item?.ParentId;
const [ hasCommands, setHasCommands ] = useState(false);
const playlistItem = useMemo(() => {
let PlaylistItemId: string | null = null;
@ -198,10 +199,15 @@ const MoreCommandsButton: FC<MoreCommandsButtonProps> = ({
[defaultMenuOptions, item, itemId, items, parentId, queryClient, queryKey]
);
if (
item
&& itemContextMenu.getCommands(defaultMenuOptions).length
) {
useEffect(() => {
const getCommands = async () => {
const commands = await itemContextMenu.getCommands(defaultMenuOptions);
setHasCommands(commands.length > 0);
};
void getCommands();
}, [ defaultMenuOptions ]);
if (item && hasCommands) {
return (
<IconButton
className='button-flat btnMoreCommands'

View file

@ -0,0 +1,7 @@
/**
* Actions that are triggered for media segments.
*/
export enum MediaSegmentAction {
None = 'None',
Skip = 'Skip'
}

View file

@ -0,0 +1,131 @@
import type { Api } from '@jellyfin/sdk/lib/api';
import type { MediaSegmentDto } from '@jellyfin/sdk/lib/generated-client/models/media-segment-dto';
import { MediaSegmentType } from '@jellyfin/sdk/lib/generated-client/models/media-segment-type';
import { MediaSegmentsApi } from '@jellyfin/sdk/lib/generated-client/api/media-segments-api';
import type { PlaybackManager } from 'components/playback/playbackmanager';
import ServerConnections from 'components/ServerConnections';
import { TICKS_PER_MILLISECOND, TICKS_PER_SECOND } from 'constants/time';
import { currentSettings as userSettings } from 'scripts/settings/userSettings';
import type { PlayerState } from 'types/playbackStopInfo';
import type { Event } from 'utils/events';
import { toApi } from 'utils/jellyfin-apiclient/compat';
import { getMediaSegmentAction } from './mediaSegmentSettings';
import { findCurrentSegment } from './mediaSegments';
import { PlaybackSubscriber } from './playbackSubscriber';
import { MediaSegmentAction } from '../constants/mediaSegmentAction';
class MediaSegmentManager extends PlaybackSubscriber {
private hasSegments = false;
private isLastSegmentIgnored = false;
private lastSegmentIndex = 0;
private lastTime = -1;
private mediaSegmentTypeActions: Record<Partial<MediaSegmentType>, MediaSegmentAction> | undefined;
private mediaSegments: MediaSegmentDto[] = [];
private async fetchMediaSegments(api: Api, itemId: string, includeSegmentTypes: MediaSegmentType[]) {
// FIXME: Replace with SDK getMediaSegmentsApi function when available in stable
const mediaSegmentsApi = new MediaSegmentsApi(api.configuration, undefined, api.axiosInstance);
try {
const { data: mediaSegments } = await mediaSegmentsApi.getItemSegments({ itemId, includeSegmentTypes });
this.mediaSegments = mediaSegments.Items || [];
} catch (err) {
console.error('[MediaSegmentManager] failed to fetch segments', err);
this.mediaSegments = [];
}
}
private performAction(mediaSegment: MediaSegmentDto) {
if (!this.mediaSegmentTypeActions || !mediaSegment.Type || !this.mediaSegmentTypeActions[mediaSegment.Type]) {
console.error('[MediaSegmentManager] segment type missing from action map', mediaSegment, this.mediaSegmentTypeActions);
return;
}
const action = this.mediaSegmentTypeActions[mediaSegment.Type];
if (action === MediaSegmentAction.Skip) {
// Ignore segment if playback progress has passed the segment's start time
if (mediaSegment.StartTicks !== undefined && this.lastTime > mediaSegment.StartTicks) {
console.info('[MediaSegmentManager] ignoring skipping segment that has been seeked back into', mediaSegment);
this.isLastSegmentIgnored = true;
return;
} else if (mediaSegment.EndTicks) {
// If there is an end time, seek to it
// Do not skip if duration < 1s to avoid slow stream changes
if (mediaSegment.StartTicks && mediaSegment.EndTicks - mediaSegment.StartTicks < TICKS_PER_SECOND) {
console.info('[MediaSegmentManager] ignoring skipping segment with duration <1s', mediaSegment);
this.isLastSegmentIgnored = true;
return;
}
console.debug('[MediaSegmentManager] skipping to %s ms', mediaSegment.EndTicks / TICKS_PER_MILLISECOND);
this.playbackManager.seek(mediaSegment.EndTicks, this.player);
} else {
// If there is no end time, skip to the next track
console.debug('[MediaSegmentManager] skipping to next item in queue');
this.playbackManager.nextTrack(this.player);
}
}
}
onPlayerPlaybackStart(_e: Event, state: PlayerState) {
this.isLastSegmentIgnored = false;
this.lastSegmentIndex = 0;
this.lastTime = -1;
this.hasSegments = !!state.MediaSource?.HasSegments;
const itemId = state.MediaSource?.Id;
const serverId = state.NowPlayingItem?.ServerId || ServerConnections.currentApiClient()?.serverId();
if (!this.hasSegments || !serverId || !itemId) return;
// Get the user settings for media segment actions
this.mediaSegmentTypeActions = Object.values(MediaSegmentType)
.map(type => ({
type,
action: getMediaSegmentAction(userSettings, type)
}))
.filter(({ action }) => !!action && action !== MediaSegmentAction.None)
.reduce((acc, { type, action }) => {
if (action) acc[type] = action;
return acc;
}, {} as Record<Partial<MediaSegmentType>, MediaSegmentAction>);
if (!Object.keys(this.mediaSegmentTypeActions).length) {
console.info('[MediaSegmentManager] user has no media segment actions enabled');
return;
}
const api = toApi(ServerConnections.getApiClient(serverId));
void this.fetchMediaSegments(
api,
itemId,
Object.keys(this.mediaSegmentTypeActions).map(t => t as keyof typeof MediaSegmentType));
}
onPlayerTimeUpdate() {
if (this.hasSegments && this.mediaSegments.length) {
const time = this.playbackManager.currentTime(this.player) * TICKS_PER_MILLISECOND;
const currentSegmentDetails = findCurrentSegment(this.mediaSegments, time, this.lastSegmentIndex);
if (
// The current time falls within a segment
currentSegmentDetails
// and the last segment is not ignored or the segment index has changed
&& (!this.isLastSegmentIgnored || this.lastSegmentIndex !== currentSegmentDetails.index)
) {
console.debug(
'[MediaSegmentManager] found %s segment at %s ms',
currentSegmentDetails.segment.Type,
time / TICKS_PER_MILLISECOND,
currentSegmentDetails);
this.isLastSegmentIgnored = false;
this.performAction(currentSegmentDetails.segment);
this.lastSegmentIndex = currentSegmentDetails.index;
}
this.lastTime = time;
}
}
}
export const bindMediaSegmentManager = (playbackManager: PlaybackManager) => new MediaSegmentManager(playbackManager);

View file

@ -0,0 +1,14 @@
import { MediaSegmentType } from '@jellyfin/sdk/lib/generated-client/models/media-segment-type';
import { UserSettings } from 'scripts/settings/userSettings';
import { MediaSegmentAction } from '../constants/mediaSegmentAction';
const PREFIX = 'segmentTypeAction';
export const getId = (type: MediaSegmentType) => `${PREFIX}__${type}`;
export function getMediaSegmentAction(userSettings: UserSettings, type: MediaSegmentType): MediaSegmentAction | undefined {
const action = userSettings.get(getId(type), false);
return action ? action as MediaSegmentAction : undefined;
}

View file

@ -0,0 +1,68 @@
import type { MediaSegmentDto } from '@jellyfin/sdk/lib/generated-client/models/media-segment-dto';
import { MediaSegmentType } from '@jellyfin/sdk/lib/generated-client/models/media-segment-type';
import { describe, expect, it } from 'vitest';
import { findCurrentSegment } from './mediaSegments';
const TEST_SEGMENTS: MediaSegmentDto[] = [
{
Id: 'intro',
Type: MediaSegmentType.Intro,
StartTicks: 0,
EndTicks: 10
},
{
Id: 'preview',
Type: MediaSegmentType.Preview,
StartTicks: 20,
EndTicks: 30
},
{
Id: 'recap',
Type: MediaSegmentType.Recap,
StartTicks: 30,
EndTicks: 40
},
{
Id: 'commercial',
Type: MediaSegmentType.Commercial,
StartTicks: 40,
EndTicks: 50
},
{
Id: 'outro',
Type: MediaSegmentType.Outro,
StartTicks: 50,
EndTicks: 60
}
];
describe('findCurrentSegment()', () => {
it('Should return the current segment', () => {
let segmentDetails = findCurrentSegment(TEST_SEGMENTS, 23);
expect(segmentDetails).toBeDefined();
expect(segmentDetails?.index).toBe(1);
expect(segmentDetails?.segment?.Id).toBe('preview');
segmentDetails = findCurrentSegment(TEST_SEGMENTS, 5, 1);
expect(segmentDetails).toBeDefined();
expect(segmentDetails?.index).toBe(0);
expect(segmentDetails?.segment?.Id).toBe('intro');
segmentDetails = findCurrentSegment(TEST_SEGMENTS, 42, 3);
expect(segmentDetails).toBeDefined();
expect(segmentDetails?.index).toBe(3);
expect(segmentDetails?.segment?.Id).toBe('commercial');
});
it('Should return undefined if not in a segment', () => {
let segmentDetails = findCurrentSegment(TEST_SEGMENTS, 16);
expect(segmentDetails).toBeUndefined();
segmentDetails = findCurrentSegment(TEST_SEGMENTS, 10, 1);
expect(segmentDetails).toBeUndefined();
segmentDetails = findCurrentSegment(TEST_SEGMENTS, 100);
expect(segmentDetails).toBeUndefined();
});
});

View file

@ -0,0 +1,41 @@
import type { MediaSegmentDto } from '@jellyfin/sdk/lib/generated-client/models/media-segment-dto';
const isBeforeSegment = (segment: MediaSegmentDto, time: number, direction: number) => {
if (direction === -1) {
return (
typeof segment.EndTicks !== 'undefined'
&& segment.EndTicks <= time
);
}
return (
typeof segment.StartTicks !== 'undefined'
&& segment.StartTicks > time
);
};
const isInSegment = (segment: MediaSegmentDto, time: number) => (
typeof segment.StartTicks !== 'undefined'
&& segment.StartTicks <= time
&& (typeof segment.EndTicks === 'undefined' || segment.EndTicks > time)
);
export const findCurrentSegment = (segments: MediaSegmentDto[], time: number, lastIndex = 0) => {
const lastSegment = segments[lastIndex];
if (isInSegment(lastSegment, time)) {
return { index: lastIndex, segment: lastSegment };
}
let direction = 1;
if (lastIndex > 0 && lastSegment.StartTicks && lastSegment.StartTicks > time) {
direction = -1;
}
for (
let index = lastIndex, segment = segments[index];
index >= 0 && index < segments.length;
index += direction, segment = segments[index]
) {
if (isBeforeSegment(segment, time, direction)) return;
if (isInSegment(segment, time)) return { index, segment };
}
};

View file

@ -39,35 +39,35 @@ export interface PlaybackSubscriber {
}
export abstract class PlaybackSubscriber {
private player: Plugin | undefined;
protected player: Plugin | undefined;
private readonly playbackManagerEvents = {
[PlaybackManagerEvent.PlaybackCancelled]: this.onPlaybackCancelled,
[PlaybackManagerEvent.PlaybackError]: this.onPlaybackError,
[PlaybackManagerEvent.PlaybackStart]: this.onPlaybackStart,
[PlaybackManagerEvent.PlaybackStop]: this.onPlaybackStop,
[PlaybackManagerEvent.PlayerChange]: this.onPlayerChange,
[PlaybackManagerEvent.ReportPlayback]: this.onReportPlayback
[PlaybackManagerEvent.PlaybackCancelled]: this.onPlaybackCancelled?.bind(this),
[PlaybackManagerEvent.PlaybackError]: this.onPlaybackError?.bind(this),
[PlaybackManagerEvent.PlaybackStart]: this.onPlaybackStart?.bind(this),
[PlaybackManagerEvent.PlaybackStop]: this.onPlaybackStop?.bind(this),
[PlaybackManagerEvent.PlayerChange]: this.onPlayerChange?.bind(this),
[PlaybackManagerEvent.ReportPlayback]: this.onReportPlayback?.bind(this)
};
private readonly playerEvents = {
[PlayerEvent.Error]: this.onPlayerError,
[PlayerEvent.FullscreenChange]: this.onPlayerFullscreenChange,
[PlayerEvent.ItemStarted]: this.onPlayerItemStarted,
[PlayerEvent.ItemStopped]: this.onPlayerItemStopped,
[PlayerEvent.MediaStreamsChange]: this.onPlayerMediaStreamsChange,
[PlayerEvent.Pause]: this.onPlayerPause,
[PlayerEvent.PlaybackStart]: this.onPlayerPlaybackStart,
[PlayerEvent.PlaybackStop]: this.onPlayerPlaybackStop,
[PlayerEvent.PlaylistItemAdd]: this.onPlayerPlaylistItemAdd,
[PlayerEvent.PlaylistItemMove]: this.onPlayerPlaylistItemMove,
[PlayerEvent.PlaylistItemRemove]: this.onPlayerPlaylistItemRemove,
[PlayerEvent.RepeatModeChange]: this.onPlayerRepeatModeChange,
[PlayerEvent.ShuffleModeChange]: this.onPlayerShuffleModeChange,
[PlayerEvent.Stopped]: this.onPlayerStopped,
[PlayerEvent.TimeUpdate]: this.onPlayerTimeUpdate,
[PlayerEvent.Unpause]: this.onPlayerUnpause,
[PlayerEvent.VolumeChange]: this.onPlayerVolumeChange
[PlayerEvent.Error]: this.onPlayerError?.bind(this),
[PlayerEvent.FullscreenChange]: this.onPlayerFullscreenChange?.bind(this),
[PlayerEvent.ItemStarted]: this.onPlayerItemStarted?.bind(this),
[PlayerEvent.ItemStopped]: this.onPlayerItemStopped?.bind(this),
[PlayerEvent.MediaStreamsChange]: this.onPlayerMediaStreamsChange?.bind(this),
[PlayerEvent.Pause]: this.onPlayerPause?.bind(this),
[PlayerEvent.PlaybackStart]: this.onPlayerPlaybackStart?.bind(this),
[PlayerEvent.PlaybackStop]: this.onPlayerPlaybackStop?.bind(this),
[PlayerEvent.PlaylistItemAdd]: this.onPlayerPlaylistItemAdd?.bind(this),
[PlayerEvent.PlaylistItemMove]: this.onPlayerPlaylistItemMove?.bind(this),
[PlayerEvent.PlaylistItemRemove]: this.onPlayerPlaylistItemRemove?.bind(this),
[PlayerEvent.RepeatModeChange]: this.onPlayerRepeatModeChange?.bind(this),
[PlayerEvent.ShuffleModeChange]: this.onPlayerShuffleModeChange?.bind(this),
[PlayerEvent.Stopped]: this.onPlayerStopped?.bind(this),
[PlayerEvent.TimeUpdate]: this.onPlayerTimeUpdate?.bind(this),
[PlayerEvent.Unpause]: this.onPlayerUnpause?.bind(this),
[PlayerEvent.VolumeChange]: this.onPlayerVolumeChange?.bind(this)
};
constructor(

View file

@ -1,11 +1,10 @@
import type { UserDto } from '@jellyfin/sdk/lib/generated-client';
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
import React, { FunctionComponent, useEffect, useState, useRef, useCallback } from 'react';
import React, { FunctionComponent, useEffect, useState, useRef, useCallback, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import Dashboard from '../../../../utils/dashboard';
import globalize from '../../../../lib/globalize';
import LibraryMenu from '../../../../scripts/libraryMenu';
import { appHost } from '../../../../components/apphost';
import confirm from '../../../../components/confirm/confirm';
import ButtonElement from '../../../../elements/ButtonElement';
@ -18,6 +17,7 @@ const UserProfile: FunctionComponent = () => {
const [ searchParams ] = useSearchParams();
const userId = searchParams.get('userId');
const [ userName, setUserName ] = useState('');
const libraryMenu = useMemo(async () => ((await import('../../../../scripts/libraryMenu')).default), []);
const element = useRef<HTMLDivElement>(null);
@ -41,7 +41,7 @@ const UserProfile: FunctionComponent = () => {
}
setUserName(user.Name);
LibraryMenu.setTitle(user.Name);
void libraryMenu.then(menu => menu.setTitle(user.Name));
let imageUrl = 'assets/img/avatar.png';
if (user.PrimaryImageTag) {

View file

@ -149,19 +149,22 @@ const ConnectionRequired: FunctionComponent<ConnectionRequiredProps> = ({
useEffect(() => {
// Check connection status on initial page load
ServerConnections.connect()
.then(firstConnection => {
console.debug('[ConnectionRequired] connection state', firstConnection?.State);
const apiClient = ServerConnections.currentApiClient();
const firstConnection = ServerConnections.firstConnection;
console.debug('[ConnectionRequired] connection state', firstConnection?.State);
ServerConnections.firstConnection = null;
if (firstConnection && firstConnection.State !== ConnectionState.SignedIn) {
return handleIncompleteWizard(firstConnection);
} else {
return validateUserAccess();
}
})
.catch(err => {
console.error('[ConnectionRequired] failed to connect to server', err);
});
if (firstConnection && firstConnection.State !== ConnectionState.SignedIn && !apiClient?.isLoggedIn()) {
handleIncompleteWizard(firstConnection)
.catch(err => {
console.error('[ConnectionRequired] could not start wizard', err);
});
} else {
validateUserAccess()
.catch(err => {
console.error('[ConnectionRequired] could not validate user access', err);
});
}
}, [handleIncompleteWizard, validateUserAccess]);
if (isLoading) {

View file

@ -33,6 +33,7 @@ class ServerConnections extends ConnectionManager {
constructor() {
super(...arguments);
this.localApiClient = null;
this.firstConnection = null;
// Set the apiclient minimum version to match the SDK
this._minServerVersion = MINIMUM_VERSION;

View file

@ -1,8 +1,7 @@
import React, { FunctionComponent, useCallback, useEffect, useRef } from 'react';
import React, { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react';
import type { UserDto } from '@jellyfin/sdk/lib/generated-client';
import Dashboard from '../../../utils/dashboard';
import globalize from '../../../lib/globalize';
import LibraryMenu from '../../../scripts/libraryMenu';
import confirm from '../../confirm/confirm';
import loading from '../../loading/loading';
import toast from '../../toast/toast';
@ -16,6 +15,7 @@ type IProps = {
const UserPasswordForm: FunctionComponent<IProps> = ({ userId }: IProps) => {
const element = useRef<HTMLDivElement>(null);
const user = useRef<UserDto>();
const libraryMenu = useMemo(async () => ((await import('../../../scripts/libraryMenu')).default), []);
const loadUser = useCallback(async () => {
const page = element.current;
@ -37,7 +37,7 @@ const UserPasswordForm: FunctionComponent<IProps> = ({ userId }: IProps) => {
throw new Error('Unexpected null user policy or configuration');
}
LibraryMenu.setTitle(user.current.Name);
(await libraryMenu).setTitle(user.current.Name);
if (user.current.HasConfiguredPassword) {
if (!user.current.Policy?.IsAdministrator) {

View file

@ -91,9 +91,11 @@ function loadForm(context, user, userSettings) {
if (appHost.supports('screensaver')) {
context.querySelector('.selectScreensaverContainer').classList.remove('hide');
context.querySelector('.txtBackdropScreensaverIntervalContainer').classList.remove('hide');
context.querySelector('.txtScreensaverTimeContainer').classList.remove('hide');
} else {
context.querySelector('.selectScreensaverContainer').classList.add('hide');
context.querySelector('.txtBackdropScreensaverIntervalContainer').classList.add('hide');
context.querySelector('.txtScreensaverTimeContainer').classList.add('hide');
}
if (datetime.supportsLocalization()) {
@ -108,6 +110,7 @@ function loadForm(context, user, userSettings) {
loadScreensavers(context, userSettings);
context.querySelector('#txtBackdropScreensaverInterval').value = userSettings.backdropScreensaverInterval();
context.querySelector('#txtScreensaverTime').value = userSettings.screensaverTime();
context.querySelector('.chkDisplayMissingEpisodes').checked = user.Configuration.DisplayMissingEpisodes || false;
@ -152,6 +155,7 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
userSettingsInstance.dashboardTheme(context.querySelector('#selectDashboardTheme').value);
userSettingsInstance.screensaver(context.querySelector('.selectScreensaver').value);
userSettingsInstance.backdropScreensaverInterval(context.querySelector('#txtBackdropScreensaverInterval').value);
userSettingsInstance.screensaverTime(context.querySelector('#txtScreensaverTime').value);
userSettingsInstance.libraryPageSize(context.querySelector('#txtLibraryPageSize').value);

View file

@ -203,6 +203,12 @@
<select is="emby-select" class="selectScreensaver" label="${LabelScreensaver}"></select>
</div>
<div class="inputContainer hide txtScreensaverTimeContainer inputContainer-withDescription">
<input is="emby-input" type="number" id="txtScreensaverTime" pattern="[0-9]*" required="required" min="5" max="86400" step="1"
label="${LabelScreensaverTime}" />
<div class="fieldDescription">${LabelScreensaverTimeHelp}</div>
</div>
<div class="inputContainer hide txtBackdropScreensaverIntervalContainer inputContainer-withDescription">
<input is="emby-input" type="number" id="txtBackdropScreensaverInterval" pattern="[0-9]*" required="required" min="1" max="3600" step="1" label="${LabelBackdropScreensaverInterval}" />
<div class="fieldDescription">${LabelBackdropScreensaverIntervalHelp}</div>

View file

@ -5,7 +5,7 @@ import globalize from '../lib/globalize';
import actionsheet from './actionSheet/actionSheet';
import { appHost } from './apphost';
import { appRouter } from './router/appRouter';
import itemHelper from './itemHelper';
import itemHelper, { canEditPlaylist } from './itemHelper';
import { playbackManager } from './playback/playbackmanager';
import ServerConnections from './ServerConnections';
import toast from './toast/toast';
@ -29,7 +29,7 @@ function getDeleteLabel(type) {
}
}
export function getCommands(options) {
export async function getCommands(options) {
const item = options.item;
const user = options.user;
@ -209,6 +209,17 @@ export function getCommands(options) {
});
}
if (item.Type === BaseItemKind.Playlist) {
const _canEditPlaylist = await canEditPlaylist(user, item);
if (_canEditPlaylist) {
commands.push({
name: globalize.translate('Edit'),
id: 'editplaylist',
icon: 'edit'
});
}
}
const canEdit = itemHelper.canEdit(user, item);
if (canEdit && options.edit !== false && item.Type !== 'SeriesTimer') {
const text = (item.Type === 'Timer' || item.Type === 'SeriesTimer') ? globalize.translate('Edit') : globalize.translate('EditMetadata');
@ -466,6 +477,15 @@ function executeCommand(item, id, options) {
case 'edit':
editItem(apiClient, item).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
break;
case 'editplaylist':
import('./playlisteditor/playlisteditor').then(({ default: PlaylistEditor }) => {
const playlistEditor = new PlaylistEditor();
playlistEditor.show({
id: itemId,
serverId
}).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id));
});
break;
case 'editimages':
import('./imageeditor/imageeditor').then((imageEditor) => {
imageEditor.show({
@ -712,19 +732,19 @@ function refresh(apiClient, item) {
});
}
export function show(options) {
const commands = getCommands(options);
export async function show(options) {
const commands = await getCommands(options);
if (!commands.length) {
return Promise.reject();
throw new Error('No item commands present');
}
return actionsheet.show({
const id = await actionsheet.show({
items: commands,
positionTo: options.positionTo,
resolveOnClick: ['share']
}).then(function (id) {
return executeCommand(options.item, id, options);
});
return executeCommand(options.item, id, options);
}
export default {

View file

@ -1,10 +1,14 @@
import { appHost } from './apphost';
import globalize from 'lib/globalize';
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
import { LocationType } from '@jellyfin/sdk/lib/generated-client/models/location-type';
import { RecordingStatus } from '@jellyfin/sdk/lib/generated-client/models/recording-status';
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
import { appHost } from './apphost';
import globalize from 'lib/globalize';
import ServerConnections from './ServerConnections';
import { toApi } from 'utils/jellyfin-apiclient/compat';
export function getDisplayName(item, options = {}) {
if (!item) {
@ -159,6 +163,25 @@ export function canEditImages (user, item) {
return itemType !== 'Timer' && itemType !== 'SeriesTimer' && canEdit(user, item) && !isLocalItem(item);
}
export async function canEditPlaylist(user, item) {
const apiClient = ServerConnections.getApiClient(item.ServerId);
const api = toApi(apiClient);
try {
const { data: permissions } = await getPlaylistsApi(api)
.getPlaylistUser({
userId: user.Id,
playlistId: item.Id
});
return !!permissions.CanEdit;
} catch (err) {
console.error('Failed to get playlist permissions', err);
}
return false;
}
export function canEditSubtitles (user, item) {
if (item.MediaType !== MediaType.Video) {
return false;

View file

@ -1,4 +1,6 @@
import { PlaybackErrorCode } from '@jellyfin/sdk/lib/generated-client/models/playback-error-code.js';
import { getMediaInfoApi } from '@jellyfin/sdk/lib/utils/api/media-info-api';
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import merge from 'lodash-es/merge';
import Screenfull from 'screenfull';
@ -18,10 +20,12 @@ import { PluginType } from '../../types/plugin.ts';
import { includesAny } from '../../utils/container.ts';
import { getItems } from '../../utils/jellyfin-apiclient/getItems.ts';
import { getItemBackdropImageUrl } from '../../utils/jellyfin-apiclient/backdropImage';
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import { bindMediaSegmentManager } from 'apps/stable/features/playback/utils/mediaSegmentManager';
import { MediaError } from 'types/mediaError';
import { getMediaError } from 'utils/mediaError';
import { toApi } from 'utils/jellyfin-apiclient/compat';
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind.js';
const UNLIMITED_ITEMS = -1;
@ -401,9 +405,9 @@ function setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositio
});
}
function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, liveStreamId, options) {
async function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, liveStreamId, options) {
if (!itemHelper.isLocalItem(item) && item.MediaType === 'Audio' && !player.useServerPlaybackInfoForAudio) {
return Promise.resolve({
return {
MediaSources: [
{
StreamUrl: getAudioStreamUrlFromDeviceProfile(item, deviceProfile, options.maxBitrate, apiClient, options.startPosition),
@ -411,13 +415,13 @@ function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId,
MediaStreams: [],
RunTimeTicks: item.RunTimeTicks
}]
});
};
}
if (item.PresetMediaSource) {
return Promise.resolve({
return {
MediaSources: [item.PresetMediaSource]
});
};
}
const itemId = item.Id;
@ -427,6 +431,9 @@ function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId,
StartTimeTicks: options.startPosition || 0
};
const api = toApi(apiClient);
const mediaInfoApi = getMediaInfoApi(api);
if (options.isPlayback) {
query.IsPlayback = true;
query.AutoOpenLiveStream = true;
@ -480,7 +487,12 @@ function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId,
query.DirectPlayProtocols = player.getDirectPlayProtocols();
}
return apiClient.getPlaybackInfo(itemId, query, deviceProfile);
query.AlwaysBurnInSubtitleWhenTranscoding = appSettings.alwaysBurnInSubtitleWhenTranscoding();
query.DeviceProfile = deviceProfile;
const res = await mediaInfoApi.getPostedPlaybackInfo({ itemId: itemId, playbackInfoDto: query });
return res.data;
}
function getOptimalMediaSource(apiClient, item, versions) {
@ -2573,8 +2585,15 @@ export class PlaybackManager {
}
const apiClient = ServerConnections.getApiClient(item.ServerId);
const mediaSourceId = playOptions.mediaSourceId || item.Id;
const getMediaStreams = apiClient.getItem(apiClient.getCurrentUserId(), mediaSourceId)
let mediaSourceId;
const isLiveTv = [BaseItemKind.TvChannel, BaseItemKind.LiveTvChannel].includes(item.Type);
if (!isLiveTv) {
mediaSourceId = playOptions.mediaSourceId || item.Id;
}
const getMediaStreams = isLiveTv ? Promise.resolve([]) : apiClient.getItem(apiClient.getCurrentUserId(), mediaSourceId)
.then(fullItem => {
return fullItem.MediaStreams;
});
@ -3645,6 +3664,8 @@ export class PlaybackManager {
Events.on(serverNotifications, 'ServerRestarting', self.setDefaultPlayerActive.bind(self));
});
}
bindMediaSegmentManager(self);
}
getCurrentPlayer() {

View file

@ -1,3 +1,9 @@
import { MediaSegmentType } from '@jellyfin/sdk/lib/generated-client/models/media-segment-type';
import escapeHTML from 'escape-html';
import { MediaSegmentAction } from 'apps/stable/features/playback/constants/mediaSegmentAction';
import { getId, getMediaSegmentAction } from 'apps/stable/features/playback/utils/mediaSegmentSettings';
import appSettings from '../../scripts/settings/appSettings';
import { appHost } from '../apphost';
import browser from '../../scripts/browser';
@ -6,12 +12,12 @@ import qualityoptions from '../qualityOptions';
import globalize from '../../lib/globalize';
import loading from '../loading/loading';
import Events from '../../utils/events.ts';
import '../../elements/emby-select/emby-select';
import '../../elements/emby-checkbox/emby-checkbox';
import ServerConnections from '../ServerConnections';
import toast from '../toast/toast';
import template from './playbackSettings.template.html';
import escapeHTML from 'escape-html';
import '../../elements/emby-select/emby-select';
import '../../elements/emby-checkbox/emby-checkbox';
function fillSkipLengths(select) {
const options = [5, 10, 15, 20, 25, 30];
@ -40,6 +46,42 @@ function populateLanguages(select, languages) {
select.innerHTML = html;
}
function populateMediaSegments(container, userSettings) {
const selectedValues = {};
const actionOptions = Object.values(MediaSegmentAction)
.map(action => {
const actionLabel = globalize.translate(`MediaSegmentAction.${action}`);
return `<option value='${action}'>${actionLabel}</option>`;
})
.join('');
const segmentSettings = [
// List the types in a logical order (and exclude "Unknown" type)
MediaSegmentType.Intro,
MediaSegmentType.Preview,
MediaSegmentType.Recap,
MediaSegmentType.Commercial,
MediaSegmentType.Outro
].map(segmentType => {
const segmentTypeLabel = globalize.translate('LabelMediaSegmentsType', globalize.translate(`MediaSegmentType.${segmentType}`));
const id = getId(segmentType);
selectedValues[id] = getMediaSegmentAction(userSettings, segmentType) || MediaSegmentAction.None;
return `<div class="selectContainer">
<select is="emby-select" id="${id}" class="segmentTypeAction" label="${segmentTypeLabel}">
${actionOptions}
</select>
</div>`;
}).join('');
container.innerHTML = segmentSettings;
Object.entries(selectedValues)
.forEach(([id, value]) => {
const field = container.querySelector(`#${id}`);
if (field) field.value = value;
});
}
function fillQuality(select, isInNetwork, mediatype, maxVideoWidth) {
const options = mediatype === 'Audio' ? qualityoptions.getAudioQualityOptions({
@ -219,6 +261,9 @@ function loadForm(context, user, userSettings, systemInfo, apiClient) {
fillSkipLengths(selectSkipBackLength);
selectSkipBackLength.value = userSettings.skipBackLength();
const mediaSegmentContainer = context.querySelector('.mediaSegmentActionContainer');
populateMediaSegments(mediaSegmentContainer, userSettings);
loading.hide();
}
@ -257,6 +302,11 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
userSettingsInstance.skipForwardLength(context.querySelector('.selectSkipForwardLength').value);
userSettingsInstance.skipBackLength(context.querySelector('.selectSkipBackLength').value);
const segmentTypeActions = context.querySelectorAll('.segmentTypeAction') || [];
Array.prototype.forEach.call(segmentTypeActions, actionEl => {
userSettingsInstance.set(actionEl.id, actionEl.value, false);
});
return apiClient.updateUserConfiguration(user.Id, user.Configuration);
}

View file

@ -156,6 +156,9 @@
<div class="selectContainer">
<select is="emby-select" class="selectSkipBackLength" label="${LabelSkipBackLength}"></select>
</div>
<h3 class="sectionTitle">${HeaderMediaSegmentActions}</h3>
<div class="mediaSegmentActionContainer"></div>
</div>
<div class="verticalSection verticalSection-extrabottompadding">

View file

@ -2,6 +2,7 @@ import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-ite
import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api/items-api';
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api/user-library-api';
import escapeHtml from 'escape-html';
import toast from 'components/toast/toast';
@ -10,6 +11,7 @@ import globalize from 'lib/globalize';
import { currentSettings as userSettings } from 'scripts/settings/userSettings';
import { PluginType } from 'types/plugin';
import { toApi } from 'utils/jellyfin-apiclient/compat';
import { isBlank } from 'utils/string';
import dialogHelper from '../dialogHelper/dialogHelper';
import loading from '../loading/loading';
@ -28,11 +30,13 @@ import 'material-design-icons-iconfont';
import '../formdialog.scss';
interface DialogElement extends HTMLDivElement {
playlistId?: string
submitted?: boolean
}
interface PlaylistEditorOptions {
items: string[],
id?: string,
serverId: string,
enableAddToPlayQueue?: boolean,
defaultValue?: string
@ -56,6 +60,13 @@ function onSubmit(this: HTMLElement, e: Event) {
toast(globalize.translate('PlaylistError.AddFailed'));
})
.finally(loading.hide);
} else if (panel.playlistId) {
updatePlaylist(panel)
.catch(err => {
console.error('[PlaylistEditor] Failed to update to playlist %s', panel.playlistId, err);
toast(globalize.translate('PlaylistError.UpdateFailed'));
})
.finally(loading.hide);
} else {
createPlaylist(panel)
.catch(err => {
@ -73,6 +84,9 @@ function onSubmit(this: HTMLElement, e: Event) {
}
function createPlaylist(dlg: DialogElement) {
const name = dlg.querySelector<HTMLInputElement>('#txtNewPlaylistName')?.value;
if (isBlank(name)) return Promise.reject(new Error('Playlist name should not be blank'));
const apiClient = ServerConnections.getApiClient(currentServerId);
const api = toApi(apiClient);
@ -81,7 +95,7 @@ function createPlaylist(dlg: DialogElement) {
return getPlaylistsApi(api)
.createPlaylist({
createPlaylistDto: {
Name: dlg.querySelector<HTMLInputElement>('#txtNewPlaylistName')?.value,
Name: name,
IsPublic: dlg.querySelector<HTMLInputElement>('#chkPlaylistPublic')?.checked,
Ids: itemIds?.split(','),
UserId: apiClient.getCurrentUserId()
@ -99,6 +113,29 @@ function redirectToPlaylist(id: string | undefined) {
appRouter.showItem(id, currentServerId);
}
function updatePlaylist(dlg: DialogElement) {
if (!dlg.playlistId) return Promise.reject(new Error('Missing playlist ID'));
const name = dlg.querySelector<HTMLInputElement>('#txtNewPlaylistName')?.value;
if (isBlank(name)) return Promise.reject(new Error('Playlist name should not be blank'));
const apiClient = ServerConnections.getApiClient(currentServerId);
const api = toApi(apiClient);
return getPlaylistsApi(api)
.updatePlaylist({
playlistId: dlg.playlistId,
updatePlaylistDto: {
Name: name,
IsPublic: dlg.querySelector<HTMLInputElement>('#chkPlaylistPublic')?.checked
}
})
.then(() => {
dlg.submitted = true;
dialogHelper.close(dlg);
});
}
function addToPlaylist(dlg: DialogElement, id: string) {
const apiClient = ServerConnections.getApiClient(currentServerId);
const api = toApi(apiClient);
@ -210,7 +247,7 @@ function populatePlaylists(editorOptions: PlaylistEditorOptions, panel: DialogEl
});
}
function getEditorHtml(items: string[]) {
function getEditorHtml(items: string[], options: PlaylistEditorOptions) {
let html = '';
html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">';
@ -232,7 +269,7 @@ function getEditorHtml(items: string[]) {
html += `
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkPlaylistPublic" checked />
<input type="checkbox" is="emby-checkbox" id="chkPlaylistPublic" />
<span>${globalize.translate('PlaylistPublic')}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">
@ -244,7 +281,7 @@ function getEditorHtml(items: string[]) {
html += '</div>';
html += '<div class="formDialogFooter">';
html += `<button is="emby-button" type="submit" class="raised btnSubmit block formDialogFooterItem button-submit">${globalize.translate('Add')}</button>`;
html += `<button is="emby-button" type="submit" class="raised btnSubmit block formDialogFooterItem button-submit">${options.id ? globalize.translate('Save') : globalize.translate('Add')}</button>`;
html += '</div>';
html += '<input type="hidden" class="fldSelectedItemIds" />';
@ -281,6 +318,34 @@ function initEditor(content: DialogElement, options: PlaylistEditorOptions, item
console.error('[PlaylistEditor] failed to populate playlists', err);
})
.finally(loading.hide);
} else if (options.id) {
content.querySelector('.fldSelectPlaylist')?.classList.add('hide');
const panel = dom.parentWithClass(content, 'dialog') as DialogElement | null;
if (!panel) {
console.error('[PlaylistEditor] could not find dialog element');
return;
}
const apiClient = ServerConnections.getApiClient(currentServerId);
const api = toApi(apiClient);
Promise.all([
getUserLibraryApi(api)
.getItem({ itemId: options.id }),
getPlaylistsApi(api)
.getPlaylist({ playlistId: options.id })
])
.then(([ { data: playlistItem }, { data: playlist } ]) => {
panel.playlistId = options.id;
const nameField = panel.querySelector<HTMLInputElement>('#txtNewPlaylistName');
if (nameField) nameField.value = playlistItem.Name || '';
const publicField = panel.querySelector<HTMLInputElement>('#chkPlaylistPublic');
if (publicField) publicField.checked = !!playlist.OpenAccess;
})
.catch(err => {
console.error('[playlistEditor] failed to get playlist details', err);
});
} else {
content.querySelector('.fldSelectPlaylist')?.classList.add('hide');
@ -325,17 +390,21 @@ export class PlaylistEditor {
dlg.classList.add('formDialog');
let html = '';
const title = globalize.translate('HeaderAddToPlaylist');
html += '<div class="formDialogHeader">';
html += `<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1" title="${globalize.translate('ButtonBack')}"><span class="material-icons arrow_back" aria-hidden="true"></span></button>`;
html += '<h3 class="formDialogHeaderTitle">';
html += title;
if (items.length) {
html += globalize.translate('HeaderAddToPlaylist');
} else if (options.id) {
html += globalize.translate('HeaderEditPlaylist');
} else {
html += globalize.translate('HeaderNewPlaylist');
}
html += '</h3>';
html += '</div>';
html += getEditorHtml(items);
html += getEditorHtml(items, options);
dlg.innerHTML = html;

View file

@ -39,6 +39,9 @@ class AppRouter {
constructor() {
document.addEventListener('viewshow', () => this.onViewShow());
this.lastPath = history.location.pathname + history.location.search;
this.listen();
// TODO: Can this baseRoute logic be simplified?
this.baseRoute = window.location.href.split('?')[0].replace(this.#getRequestFile(), '');
// support hashbang
@ -100,6 +103,20 @@ class AppRouter {
return this.promiseShow;
}
listen() {
history.listen(({ location }) => {
const normalizedPath = location.pathname.replace(/^!/, '');
const fullPath = normalizedPath + location.search;
if (fullPath === this.lastPath) {
console.debug('[appRouter] path did not change, resolving promise');
this.onViewShow();
}
this.lastPath = fullPath;
});
}
baseUrl() {
return this.baseRoute;
}

View file

@ -64,6 +64,7 @@ function loadForm(context, user, userSettings, appearanceSettings, apiClient) {
context.querySelector('#chkSubtitleRenderPgs').checked = appSettings.get('subtitlerenderpgs') === 'true';
context.querySelector('#selectSubtitleBurnIn').dispatchEvent(new CustomEvent('change', {}));
context.querySelector('#chkAlwaysBurnInSubtitleWhenTranscoding').checked = appSettings.alwaysBurnInSubtitleWhenTranscoding();
onAppearanceFieldChange({
target: context.querySelector('#selectTextSize')
@ -90,6 +91,7 @@ function save(instance, context, userId, userSettings, apiClient, enableSaveConf
appSettings.set('subtitleburnin', context.querySelector('#selectSubtitleBurnIn').value);
appSettings.set('subtitlerenderpgs', context.querySelector('#chkSubtitleRenderPgs').checked);
appSettings.alwaysBurnInSubtitleWhenTranscoding(context.querySelector('#chkAlwaysBurnInSubtitleWhenTranscoding').checked);
apiClient.getUser(userId).then(function (user) {
saveUser(context, user, userSettings, instance.appearanceKey, apiClient).then(function () {

View file

@ -42,6 +42,14 @@
</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input is="emby-checkbox" type="checkbox" id="chkAlwaysBurnInSubtitleWhenTranscoding" />
<span>${AlwaysBurnInSubtitleWhenTranscoding}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${AlwaysBurnInSubtitleWhenTranscodingHelp}</div>
</div>
<div class="verticalSection subtitleAppearanceSection hide">
<h2 class="sectionTitle">
${HeaderSubtitleAppearance}

8
src/constants/time.ts Normal file
View file

@ -0,0 +1,8 @@
/** The number of ticks per millisecond */
export const TICKS_PER_MILLISECOND = 10_000;
/** The number of ticks per second */
export const TICKS_PER_SECOND = 1_000 * TICKS_PER_MILLISECOND;
/** The number of ticks per minute */
export const TICKS_PER_MINUTE = 60 * TICKS_PER_SECOND;

View file

@ -6,7 +6,7 @@ import Dashboard from '../../../utils/dashboard';
import { getParameterByName } from '../../../utils/url.ts';
function load(page, device, deviceOptions) {
page.querySelector('#txtCustomName', page).value = deviceOptions.CustomName || '';
page.querySelector('#txtCustomName', page).value = deviceOptions?.CustomName || '';
page.querySelector('.reportedName', page).innerText = device.Name || '';
}
@ -14,13 +14,13 @@ function loadData() {
const page = this;
loading.show();
const id = getParameterByName('id');
const promise1 = ApiClient.getJSON(ApiClient.getUrl('Devices/Info', {
const device = ApiClient.getJSON(ApiClient.getUrl('Devices/Info', {
Id: id
}));
const promise2 = ApiClient.getJSON(ApiClient.getUrl('Devices/Options', {
const deviceOptions = ApiClient.getJSON(ApiClient.getUrl('Devices/Options', {
Id: id
}));
Promise.all([promise1, promise2]).then(function (responses) {
})).catch(() => undefined);
Promise.all([device, deviceOptions]).then(function (responses) {
load(page, responses[0], responses[1]);
loading.hide();
});

View file

@ -30,6 +30,11 @@
<div class="fieldDescription">${LabelVaapiDeviceHelp}</div>
</div>
<div class="inputContainer hide fldQsvDevice">
<input is="emby-input" type="text" id="txtQsvDevice" label="${LabelQsvDevice}" />
<div class="fieldDescription">${LabelQsvDeviceHelp}</div>
</div>
<div class="hardwareAccelerationOptions hide">
<div class="checkboxListContainer decodingCodecsList">
<h3 class="checkboxListLabel">${LabelEnableHardwareDecodingFor}</h3>

View file

@ -31,6 +31,7 @@ function loadPage(page, config, systemInfo) {
page.querySelector('#txtFallbackFontPath').value = config.FallbackFontPath || '';
page.querySelector('#chkEnableFallbackFont').checked = config.EnableFallbackFont;
$('#txtVaapiDevice', page).val(config.VaapiDevice || '');
page.querySelector('#txtQsvDevice').value = config.QsvDevice || '';
page.querySelector('#chkTonemapping').checked = config.EnableTonemapping;
page.querySelector('#chkVppTonemapping').checked = config.EnableVppTonemapping;
page.querySelector('#chkVideoToolboxTonemapping').checked = config.EnableVideoToolboxTonemapping;
@ -93,6 +94,7 @@ function onSubmit() {
config.EncodingThreadCount = $('#selectThreadCount', form).val();
config.HardwareAccelerationType = $('#selectVideoDecoder', form).val();
config.VaapiDevice = $('#txtVaapiDevice', form).val();
config.QsvDevice = form.querySelector('#txtQsvDevice').value;
config.EnableTonemapping = form.querySelector('#chkTonemapping').checked;
config.EnableVppTonemapping = form.querySelector('#chkVppTonemapping').checked;
config.EnableVideoToolboxTonemapping = form.querySelector('#chkVideoToolboxTonemapping').checked;
@ -235,8 +237,10 @@ $(document).on('pageinit', '#encodingSettingsPage', function () {
if (this.value == 'qsv') {
page.querySelector('.fldSysNativeHwDecoder').classList.remove('hide');
page.querySelector('.fldQsvDevice').classList.remove('hide');
} else {
page.querySelector('.fldSysNativeHwDecoder').classList.add('hide');
page.querySelector('.fldQsvDevice').classList.add('hide');
}
if (this.value == 'nvenc') {

View file

@ -1,4 +1,4 @@
<div id="metadataImagesConfigurationPage" data-role="page" class="page type-interior metadataConfigurationPage" data-title="${Metadata}">
<div id="metadataImagesConfigurationPage" data-role="page" class="page type-interior metadataConfigurationPage" data-title="${LabelMetadata}">
<div>

View file

@ -11,7 +11,7 @@
</div>
</div>
<div>
<div class="editPageInnerContent padded-top padded-bottom-page">
<div class="editPageInnerContent padded-bottom-page">
</div>
</div>
</div>

View file

@ -582,11 +582,13 @@ function reloadFromItem(instance, page, params, item, user) {
page.querySelector('.btnSplitVersions').classList.add('hide');
}
if (itemContextMenu.getCommands(getContextMenuOptions(item, user)).length) {
hideAll(page, 'btnMoreCommands', true);
} else {
hideAll(page, 'btnMoreCommands');
}
itemContextMenu.getCommands(getContextMenuOptions(item, user)).then(commands => {
if (commands.length) {
hideAll(page, 'btnMoreCommands', true);
} else {
hideAll(page, 'btnMoreCommands');
}
});
const itemBirthday = page.querySelector('#itemBirthday');

View file

@ -89,7 +89,7 @@
<div class="listItem">
<span class="material-icons listItemIcon listItemIcon-transparent mode_edit" aria-hidden="true"></span>
<div class="listItemBody">
<div class="listItemBodyText">${Metadata}</div>
<div class="listItemBodyText">${MetadataManager}</div>
</div>
</div>
</a>

View file

@ -19,7 +19,7 @@ export default function (view, params) {
} else {
settingsInstance = new PlaybackSettings({
serverId: ApiClient.serverId(),
userId: userId,
userId,
element: view.querySelector('.settingsContainer'),
userSettings: currentSettings,
enableSaveButton: true,

View file

@ -37,7 +37,7 @@ const IconButtonElement: FunctionComponent<IProps> = ({ is, id, className, title
className: className,
title: title ? `title="${globalize.translate(title)}"` : '',
icon: icon,
dataIndex: dataIndex ? `data-index="${dataIndex}"` : '',
dataIndex: (dataIndex || dataIndex === 0) ? `data-index="${dataIndex}"` : '',
dataTag: dataTag ? `data-tag="${dataTag}"` : '',
dataProfileid: dataProfileid ? `data-profileid="${dataProfileid}"` : ''
})}

View file

@ -110,6 +110,9 @@ build: ${__JF_BUILD_VERSION__}`);
Events.on(apiClient, 'requestfail', appRouter.onRequestFail);
});
// Connect to server
ServerConnections.firstConnection = await ServerConnections.connect();
// Render the app
await renderApp();

View file

@ -1,6 +1,7 @@
import DOMPurify from 'dompurify';
import browser from '../../scripts/browser';
import appSettings from '../../scripts/settings/appSettings';
import { appHost } from '../../components/apphost';
import loading from '../../components/loading/loading';
import dom from '../../scripts/dom';
@ -1566,16 +1567,53 @@ export class HtmlVideoPlayer {
return t.Index === streamIndex;
})[0];
this.setTrackForDisplay(this.#mediaElement, track, targetTextTrackIndex);
if (enableNativeTrackSupport(this._currentPlayOptions?.mediaSource, track)) {
if (streamIndex !== -1) {
this.setCueAppearance();
}
// This play method can only check if it is real direct play, and will mark Remux as Transcode as well
const isDirectPlay = this._currentPlayOptions.playMethod === 'DirectPlay';
const burnInWhenTranscoding = appSettings.alwaysBurnInSubtitleWhenTranscoding();
let sessionPromise;
if (!isDirectPlay && burnInWhenTranscoding) {
const apiClient = ServerConnections.getApiClient(this._currentPlayOptions.item.ServerId);
sessionPromise = apiClient.getSessions({
deviceId: apiClient.deviceId()
}).then(function (sessions) {
return sessions[0] || {};
}, function () {
return Promise.resolve({});
});
} else {
// null these out to disable the player's native display (handled below)
streamIndex = -1;
track = null;
sessionPromise = Promise.resolve({});
}
const player = this;
sessionPromise.then((s) => {
if (!s.TranscodingInfo || s.TranscodingInfo.IsVideoDirect) {
// restore recorded delivery method if any
mediaStreamTextTracks.forEach((t) => {
t.DeliveryMethod = t.realDeliveryMethod ?? t.DeliveryMethod;
});
player.setTrackForDisplay(player.#mediaElement, track, targetTextTrackIndex);
if (enableNativeTrackSupport(player._currentPlayOptions?.mediaSource, track)) {
if (streamIndex !== -1) {
player.setCueAppearance();
}
} else {
// null these out to disable the player's native display (handled below)
streamIndex = -1;
track = null;
}
} else {
// record the original delivery method and set all delivery method to encode
// this is needed for subtitle track switching to properly reload the video stream
mediaStreamTextTracks.forEach((t) => {
t.realDeliveryMethod = t.DeliveryMethod;
t.DeliveryMethod = 'Encode';
});
// unset stream when switching to transcode
player.setTrackForDisplay(player.#mediaElement, null, -1);
}
});
}
/**

View file

@ -290,22 +290,16 @@ export class PdfPlayer {
}
renderPage(canvas, number) {
const devicePixelRatio = window.devicePixelRatio || 1;
this.book.getPage(number).then(page => {
const width = dom.getWindowSize().innerWidth;
const height = dom.getWindowSize().innerHeight;
const scale = Math.ceil(window.devicePixelRatio || 1);
const original = page.getViewport({ scale: 1 });
const scale = Math.max((window.screen.height / original.height), (window.screen.width / original.width)) * devicePixelRatio;
const viewport = page.getViewport({ scale });
const context = canvas.getContext('2d');
canvas.width = viewport.width;
canvas.height = viewport.height;
if (width < height) {
canvas.style.width = '100%';
canvas.style.height = 'auto';
} else {
canvas.style.height = '100%';
canvas.style.width = 'auto';
}
const context = canvas.getContext('2d');
const renderContext = {
canvasContext: context,

View file

@ -333,7 +333,7 @@ function refreshLibraryInfoInDrawer(user) {
html += globalize.translate('HeaderAdmin');
html += '</h3>';
html += `<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder lnkManageServer" data-itemid="dashboard" href="#/dashboard"><span class="material-icons navMenuOptionIcon dashboard" aria-hidden="true"></span><span class="navMenuOptionText">${globalize.translate('TabDashboard')}</span></a>`;
html += `<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder editorViewMenu" data-itemid="editor" href="#/metadata"><span class="material-icons navMenuOptionIcon mode_edit" aria-hidden="true"></span><span class="navMenuOptionText">${globalize.translate('Metadata')}</span></a>`;
html += `<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder editorViewMenu" data-itemid="editor" href="#/metadata"><span class="material-icons navMenuOptionIcon mode_edit" aria-hidden="true"></span><span class="navMenuOptionText">${globalize.translate('MetadataManager')}</span></a>`;
html += '</div>';
}

View file

@ -11,7 +11,7 @@ import './screensavermanager.scss';
function getMinIdleTime() {
// Returns the minimum amount of idle time required before the screen saver can be displayed
//time units used Millisecond
return 180000;
return userSettings.screensaverTime() * 1000;
}
let lastFunctionalEvent = 0;
@ -129,7 +129,7 @@ function ScreenSaverManager() {
this.show();
};
setInterval(onInterval, 10000);
setInterval(onInterval, 5000);
}
export default new ScreenSaverManager;

View file

@ -156,6 +156,19 @@ class AppSettings {
return this.get('preferredTranscodeVideoAudioCodec') || '';
}
/**
* Get or set 'Always burn in subtitle when transcoding' state.
* @param {boolean|undefined} val - Flag to enable 'Always burn in subtitle when transcoding' or undefined.
* @return {boolean} 'Always burn in subtitle when transcoding' state.
*/
alwaysBurnInSubtitleWhenTranscoding(val) {
if (val !== undefined) {
return this.set('alwaysBurnInSubtitleWhenTranscoding', val.toString());
}
return toBoolean(this.get('alwaysBurnInSubtitleWhenTranscoding'), false);
}
/**
* Get or set 'Enable DTS' state.
* @param {boolean|undefined} val - Flag to enable 'Enable DTS' or undefined.

View file

@ -91,7 +91,7 @@ export class UserSettings {
* Get value of setting.
* @param {string} name - Name of setting.
* @param {boolean} [enableOnServer] - Flag to return preferences from server (cached).
* @return {string} Value of setting.
* @return {string | null} Value of setting.
*/
get(name, enableOnServer) {
const userId = this.currentUserId;
@ -431,6 +431,19 @@ export class UserSettings {
return parseInt(this.get('backdropScreensaverInterval', false), 10) || 5;
}
/**
* Get or set the amount of time it takes to activate the screensaver in seconds. Default 3 minutes.
* @param {number|undefined} [val] - The amount of time it takes to activate the screensaver in seconds.
* @return {number} The amount of time it takes to activate the screensaver in seconds.
*/
screensaverTime(val) {
if (val !== undefined) {
return this.set('screensaverTime', val.toString(), false);
}
return parseInt(this.get('screensaverTime', false), 10) || 180;
}
/**
* Get or set library page size.
* @param {number|undefined} [val] - Library page size.
@ -650,6 +663,7 @@ export const skin = currentSettings.skin.bind(currentSettings);
export const theme = currentSettings.theme.bind(currentSettings);
export const screensaver = currentSettings.screensaver.bind(currentSettings);
export const backdropScreensaverInterval = currentSettings.backdropScreensaverInterval.bind(currentSettings);
export const screensaverTime = currentSettings.screensaverTime.bind(currentSettings);
export const libraryPageSize = currentSettings.libraryPageSize.bind(currentSettings);
export const maxDaysForNextUp = currentSettings.maxDaysForNextUp.bind(currentSettings);
export const enableRewatchingInNextUp = currentSettings.enableRewatchingInNextUp.bind(currentSettings);

View file

@ -311,7 +311,7 @@
"HeaderYears": "Гады",
"LabelValue": "Значэнне",
"Image": "Малюнак",
"LastSeen": "Апошні раз бачылі {0}",
"LastSeen": "Апошняе дзеянне {0}",
"List": "Спіс",
"Live": "Трансляцыя",
"MediaInfoAnamorphic": "Анаморфны",
@ -1760,7 +1760,7 @@
"LogLevel.Error": "Памылка",
"LabelTrackGain": "Узмацненне дарожкі",
"GoHome": "На галоўную",
"SelectAudioNormalizationHelp": "Узмацненне дарожкі - рэгулюе гучнасць кожнай дарожкі, каб яны прайграваліся з аднолькавай гучнасцю. Узмацненне альбома - рэгулюе гучнасць толькі ўсіх дарожак у альбоме, захоўваючы дынамічны дыяпазон альбома.",
"SelectAudioNormalizationHelp": "Узмацненне дарожкі - рэгулюе гучнасць кожнай дарожкі, каб яны прайграваліся з аднолькавай гучнасцю. Узмацненне альбома - рэгулюе гучнасць толькі ўсіх дарожак у альбоме, захоўваючы дынамічны дыяпазон альбома. Пераключэнне паміж \"Выкл\" і іншымі опцыямі патрабуе перазапуску бягучага прайгравання.",
"SearchResultsEmpty": "Выбачайце! Няма вынікаў для \"{0}\"",
"UnknownError": "Адбылася невядомая памылка.",
"LabelIsHearingImpaired": "Для людзей з парушэннем слыху (SDH)",
@ -1893,5 +1893,15 @@
"PluginEnableError": "Адбылася памылка пры ўключэнні плагіна.",
"PluginLoadConfigError": "Адбылася памылка падчас атрымання старонак канфігурацыі плагіна.",
"PluginLoadRepoError": "Адбылася памылка падчас атрымання дэталяў плагіна з рэпазітара.",
"PluginUninstallError": "Адбылася памылка пры выдаленні плагіна."
"PluginUninstallError": "Адбылася памылка пры выдаленні плагіна.",
"HeaderAudioAdvanced": "Пашыраны аўдыё",
"EnableHi10p": "Уключыць профіль H.264 High 10",
"HeaderUploadLyrics": "Спампаваць тэксты песень",
"DateModified": "Дата змяненняў",
"DisableVbrAudioEncodingHelp": "Забараніць серверу кадаваць аўдыя з VBR для гэтага кліента.",
"HeaderPreviewLyrics": "Папярэдні прагляд тэкстаў песень",
"LabelIsSynced": "Сінхранізавана",
"LabelDuration": "Працягласць",
"NoLyricsSearchResultsFound": "Тэксты песень не знойдзены.",
"FallbackMaxStreamingBitrateHelp": "Максімальны бітрэйт плыні выкарыстоўваецца ў якасці запаснога, калі ffprobe не можа вызначыць бітрэйт зыходнага патоку. Гэта дапамагае прадухіліць кліенты ад запытаў празмерна высокага бітрэйту транскадавання, які можа прывесці да збою прайгравальніка і перагрузкі кадавальніка."
}

View file

@ -4,7 +4,7 @@
"Add": "Přidat",
"AddToCollection": "Přidat do kolekce",
"AddToPlayQueue": "Přidat do fronty k přehrání",
"AddToPlaylist": "Přidat do playlistu",
"AddToPlaylist": "Přidat do seznamu skladeb",
"AddedOnValue": "Přidáno {0}",
"AdditionalNotificationServices": "Pro instalaci dalších oznamovacích služeb si prohlédněte katalog zásuvných modulů.",
"Albums": "Alba",
@ -181,7 +181,7 @@
"HeaderActiveRecordings": "Aktivní nahrávání",
"HeaderActivity": "Aktivita",
"HeaderAddToCollection": "Přidat do Kolekce",
"HeaderAddToPlaylist": "Přidat do playlistu",
"HeaderAddToPlaylist": "Přidat do seznamu skladeb",
"HeaderAddUpdateImage": "Přidat/aktualizovat obrázek",
"HeaderAdditionalParts": "Další součásti",
"HeaderAdmin": "Správa",
@ -876,7 +876,7 @@
"ReleaseDate": "Datum vydání",
"RememberMe": "Zapamatuj si mě",
"RemoveFromCollection": "Odebrat z kolekce",
"RemoveFromPlaylist": "Odebrat z playlistu",
"RemoveFromPlaylist": "Odebrat ze seznam skladeb",
"Repeat": "Opakovat",
"RepeatAll": "Opakovat vše",
"RepeatEpisodes": "Opakovaní epizod",
@ -1023,7 +1023,7 @@
"Banner": "Výřez plakátu",
"Blacklist": "Zakázat vše kromě výjimek",
"Browse": "Procházet",
"BurnSubtitlesHelp": "Zda má server při překódování videa vypálit titulky do obrazu. Tato funkce má velký negativní vliv na výkon. Chcete-li vypálit grafické formáty titulků (VobSub, PGS, SUB, IDX, atd.) a některé titulky ASS nebo SSA, vyberte možnost Automaticky.",
"BurnSubtitlesHelp": "Zda má server vypálit titulky do obrazu. Tato funkce má velký negativní vliv na výkon. Chcete-li vypálit grafické formáty titulků (VobSub, PGS, SUB, IDX, atd.) a některé titulky ASS nebo SSA, vyberte možnost Automaticky.",
"ButtonInfo": "Info",
"ButtonOk": "Ok",
"ButtonScanAllLibraries": "Skenovat všechny knihovny",
@ -1103,7 +1103,7 @@
"LabelMetadata": "Metadata",
"LabelOptionalNetworkPathHelp": "Pokud je tato složka sdílena ve vaší síti, zadání cesty ke sdílené složce umožní klientům na jiných zařízeních přímý přístup k souborům s médii. Například {0} nebo {1}.",
"LabelPersonRole": "Úloha",
"LabelPlaylist": "Playlist",
"LabelPlaylist": "Seznam skladeb",
"LabelReasonForTranscoding": "Důvod pro překódování",
"LabelRemoteClientBitrateLimitHelp": "Volitelný limit datového toku pro všechna síťová zařízení. To je užitečné, aby se zabránilo požadavkům na vyšší přenosovou rychlost než zvládne vaše připojení k internetu. To může mít za následek zvýšení zátěže procesoru, aby bylo možné převádět videa za běhu na nižší datový tok.",
"LabelServerHost": "Host",
@ -1275,7 +1275,7 @@
"UnsupportedPlayback": "Jellyfin nedokáže dešifrovat obsah chráněný Správou digitálních práv (DRM), ale pokusí se zobrazit veškerý obsah, včetně toho chráněného. Některé soubory se nemusí vůbec zobrazit kvůli šifrování nebo jiným nepodporovaným funkcím, např.: interaktivním názvům.",
"Filter": "Filtr",
"New": "Nový",
"ButtonTogglePlaylist": "Playlist",
"ButtonTogglePlaylist": "Seznam skladeb",
"LabelStable": "Stabilní",
"LabelChromecastVersion": "Verze Google Cast",
"ApiKeysCaption": "Seznam povolených klíčů k API",
@ -1462,7 +1462,7 @@
"LabelUDPPortRange": "Rozsah pro komunikaci UDP",
"LabelSSDPTracingFilterHelp": "Nepovinná IP adresa, pomocí které se má filtrovat zaznamenaná komunikace SSDP.",
"LabelSSDPTracingFilter": "Filtr SSDP",
"LabelPublishedServerUriHelp": "Přepíše URI používanou serverem Jellyfin v závislosti na rozhraní nebo IP adrese klienta.",
"LabelPublishedServerUriHelp": "Přepíše URI používanou serverem Jellyfin v závislosti na rozhraní nebo IP adrese klienta. Například: internal=http://jellyfin.example.com, external=https://jellyfin.example.com nebo all=https://jellyfin.example.com",
"LabelPublishedServerUri": "Veřejné URI serveru",
"LabelIsForced": "Vynucené",
"LabelHDHomerunPortRangeHelp": "Omezí rozsah UDP portů HDHomeRun na tuto hodnotu. (Výchozí hodnota je 1024-65535).",
@ -1780,7 +1780,7 @@
"LabelSelectAudioNormalization": "Normalizace hlasitosti",
"LabelAlbumGain": "Na úrovni alba",
"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.",
"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. Vypnutí a následné zapnutí vyžaduje restart aktuálně hrající skladby.",
"SearchResultsEmpty": "Pro “{0}” nebylo nic nalezeno",
"HeaderAllRecordings": "Všechny nahrávky",
"LabelBuildVersion": "Verze sestavení",
@ -1967,5 +1967,23 @@
"RenderPgsSubtitle": "Experimentální rendrování titulků ve formátu PGS",
"RenderPgsSubtitleHelp": "Zda má klient zobrazit titulky ve formátu PGS místo jejich vypálení do obrazu. Můžete se tak vyhnout překódování na serveru výměnou za horší výkon klienta.",
"UseCustomTagDelimiters": "Použít vlastní oddělovač značek",
"UseCustomTagDelimitersHelp": "Rozdělit značky pro umělce a žánry pomocí vlastních znaků."
"UseCustomTagDelimitersHelp": "Rozdělit značky pro umělce a žánry pomocí vlastních znaků.",
"LabelScreensaverTime": "Prodleva spořiče obrazovky",
"LabelScreensaverTimeHelp": "Po jak dlouhé neaktivitě (v sekundách) se má spustit spořič obrazovky.",
"AlwaysBurnInSubtitleWhenTranscoding": "Vždy vypálit titulky do obrazu při překódování",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Vypálit všechny titulky do obrazu při překódování. To zaručí, že budou titulky po překódování synchronizovány, ale překódování bude pomalejší.",
"LabelQsvDevice": "Zařízení QSV",
"LabelQsvDeviceHelp": "Určuje zařízení pro Intel QSV na systémech s více grafickými kartami. Na Linuxu se jedná o rendrovací uzel, např. /dev/dri/renderD128. Na Windows se jedná o index zařízení, který začíná od 0. Pokud nevíte, k čemu tato možnost je, doporučujeme nic nevyplňovat.",
"HeaderEditPlaylist": "Upravit seznam skladeb",
"HeaderNewPlaylist": "Nový seznam skladeb",
"MediaSegmentAction.None": "Žádné",
"MediaSegmentAction.Skip": "Přeskočit",
"MediaSegmentType.Commercial": "Komerční",
"MediaSegmentType.Intro": "Úvod",
"MediaSegmentType.Outro": "Závěrečné titulky",
"MediaSegmentType.Preview": "Ukázka",
"MediaSegmentType.Recap": "Rekapitulace",
"PlaylistError.UpdateFailed": "Aktualizace seznamu skladeb se nezdařila",
"HeaderMediaSegmentActions": "Akce segmentu médií",
"LabelMediaSegmentsType": "{0} segmentů"
}

View file

@ -1085,7 +1085,7 @@
"LearnHowYouCanContribute": "Lær hvordan du kan bidrage.",
"LeaveBlankToNotSetAPassword": "Du kan lade dette felt være tomt hvis du ikke ønsker adgangskode.",
"List": "Liste",
"Live": "Live",
"Live": "Direkte",
"LiveTV": "Direkte TV",
"Logo": "Logo",
"ManageRecording": "Håndtér optagelse",
@ -1095,7 +1095,7 @@
"MediaInfoInterlaced": "Interlaceret",
"MediaInfoLayout": "Udlæg",
"MediaInfoRefFrames": "Ref billeder",
"MediaInfoSampleRate": "Sample rate",
"MediaInfoSampleRate": "Samplingsfrekvens",
"MediaIsBeingConverted": "Mediet bliver konverteret til et format der er kompatibel med enheden der afspiller mediet.",
"Menu": "Menu",
"MessageImageFileTypeAllowed": "Kun JPEG og PNG filer er understøttet.",
@ -1341,9 +1341,9 @@
"MessagePlaybackError": "Der opstod en fejl under afspilning af denne fil på din Google Cast modtager.",
"MessageChromecastConnectionError": "Din Google Cast modtager kan ikke komme i kontakt med Jellyfin serveren. Undersøg venligst forbindelsen og prøv igen.",
"LabelPlaybackInfo": "Afspilningsinformation",
"Remuxing": "Remuxing",
"Remuxing": "Ompakning",
"AspectRatioFill": "Udfyld",
"AspectRatioCover": "Cover",
"AspectRatioCover": "Omslag",
"EnableFallbackFont": "Aktiver fald tilbage på skrifttype",
"Writers": "Forfattere",
"ViewAlbumArtist": "Vis album kunstner",
@ -1369,7 +1369,7 @@
"QuickConnectAuthorizeFail": "Ukendt Quick Connect kode",
"QuickConnectAuthorizeCode": "Angiv kode {0} for at logge ind",
"QuickConnectActivationSuccessful": "Aktivering blev gennemført",
"QuickConnect": "Quick Connect",
"QuickConnect": "Hurtigtilkobling",
"PluginFromRepo": "{0} fra arkiv {1}",
"Profile": "Profil",
"PreviousTrack": "Gå til forrige",
@ -1603,7 +1603,7 @@
"Remixer": "Remixer",
"ReleaseGroup": "Udgivelsesgruppe",
"Production": "Produktion",
"Print": "Print",
"Print": "Udskriv",
"PreviousChapter": "Sidste kapitel",
"AllowEmbeddedSubtitlesAllowTextOption": "Tillad tekst",
"AllowEmbeddedSubtitlesAllowImageOption": "Tillad billede",
@ -1749,7 +1749,7 @@
"Notifications": "Notifikationer",
"PleaseConfirmRepositoryInstallation": "Venligst klik OK for at bekræfte at du har læst ovenstående og ønsker at fortsætte med installationen af plugin repository.",
"SaveRecordingImagesHelp": "Gem billeder fra EPG listings udbyder samt billeder fra side media.",
"SelectAudioNormalizationHelp": "Track gain - justerer volumen for hvert track så de afspiller med samme volume. Album gain - justerer volumen kun volumen for alle tracks i et album, samt bevarer albummets dynamic range.",
"SelectAudioNormalizationHelp": "Track gain - justerer volumen for hvert track så de afspiller med samme volume. Album gain - justerer volumen kun volumen for alle tracks i et album, samt bevarer albummets dynamic range. Skift mellem \"Fra\" og andre muligheder kræver genstart af den indeværende afspilning.",
"Select": "Vælg",
"LabelLevel": "Niveau",
"LabelMediaDetails": "Media detaljer",
@ -1875,7 +1875,7 @@
"Translator": "Oversætter",
"ViewLyrics": "Se sangtekst",
"LabelTrickplayAccelEncoding": "Aktivér hardware acceleret MJPEG kodning",
"LabelTrickplayAccelEncodingHelp": "På nuværende tidspunkt kun tilgængeligt på QSV, VAAPI og VideoToolbox; denne indstilling har ingen effekt på andre hardware accelerationsmetoder.",
"LabelTrickplayAccelEncodingHelp": "På nuværende tidspunkt kun tilgængeligt på QSV, VA-API, VideoToolbox og RKMPP; denne indstilling har ingen effekt på andre hardware accelerationsmetoder.",
"HeaderNoLyrics": "Ingen sangtekst blev fundet",
"HeaderVideoAdvanced": "Video Avanceret",
"PlaylistPublic": "Tillad offentlig adgang",
@ -1924,9 +1924,12 @@
"PreviewLyrics": "Gennemse sangtekster",
"SearchForLyrics": "Find sangtekster",
"LabelRepository": "Arkiv",
"AllowTonemappingSoftwareHelp": "Tone-mapping kan transformere det dynamiske område af en video fra HDR til SDR, uden tab af billeddetalje og farver, hvilket er meget vigtige informationer for at representere den originale scene. Virker lige nu kun med 10bit HDR10 og HLG videoer.",
"AllowTonemappingSoftwareHelp": "Tone-mapping kan transformere det dynamiske område af en video fra HDR til SDR, uden tab af billeddetalje og farver, hvilket er meget vigtige informationer for at repræsentere den originale scene. Virker lige nu kun med 10bit HDR10-, HLG- og DoVi-videoer.",
"HeaderAddLyrics": "Tilføj sangtekst",
"LabelNoChangelog": "Ingen ændringer fortaget for denne udgivelse.",
"LabelSelectPreferredTranscodeVideoCodec": "Fforetrukne video codec til omkodning",
"PluginLoadConfigError": "Der opstod en fejl imens vi hentede konfigurationssiderne til pluginet."
"PluginLoadConfigError": "Der opstod en fejl imens vi hentede konfigurationssiderne til pluginet.",
"AllowFmp4TranscodingContainerHelp": "Tillad fMP4-transkodningscontainer for denne tuner for at aktivere HEVC og HDR-indhold. Ikke alle tunere er kompatible med denne container. Deaktiver dette, hvis du oplever afspilningsproblemer.",
"AllowStreamSharingHelp": "Tillad Jellyfin at kopiere mpegts-streamen fra tuner og dele denne kopierede stream til dens klienter. Dette er brugbart når tunerens totale stream-antal er begrænset, men kan også skabe afspilningsproblemer.",
"AlwaysBurnInSubtitleWhenTranscoding": "Brænd altid undertekster ind under transkodning"
}

View file

@ -44,7 +44,7 @@
"BoxRear": "Box (Rückseite)",
"Browse": "Durchsuchen",
"MessageBrowsePluginCatalog": "Durchsuche unseren Katalog, um alle verfügbaren Plugins anzuzeigen.",
"BurnSubtitlesHelp": "Legt fest, ob der Server die Untertitel während der Videotranskodierung einbrennen soll. Deaktivieren verbessert die Serverperformance immens. Wähle Auto, um bildbasierte Formate (z.B. VobSub, PGS, SUB, IDX, etc.) sowie bestimmte ASS- oder SSA-Untertitel einbrennen zu lassen.",
"BurnSubtitlesHelp": "Legt fest, ob der Server die Untertitel einbrennen soll. Deaktivieren verbessert die Serverperformance immens. Wähle Auto, um bildbasierte Formate (z.B. VobSub, PGS, SUB, IDX, etc.) sowie bestimmte ASS- oder SSA-Untertitel einbrennen zu lassen.",
"ButtonAddMediaLibrary": "Medienbibliothek hinzufügen",
"ButtonAddScheduledTaskTrigger": "Auslöser hinzufügen",
"ButtonAddServer": "Server hinzufügen",
@ -1463,7 +1463,7 @@
"HeaderAddUpdateSubtitle": "Untertitel hinzufügen/aktualisieren",
"LabelSSDPTracingFilterHelp": "Optionale IP-Adresse zum Filtern des protokollierten SSDP-Verkehrs.",
"LabelSSDPTracingFilter": "SSDP-Filter",
"LabelPublishedServerUriHelp": "Überschreibt die von Jellyfin genutzte URI auf Basis des Interfaces oder der IP-Adresse des Clients.",
"LabelPublishedServerUriHelp": "Überschreibt die von Jellyfin verwendeten URI, basierend auf der Schnittstelle oder der Client-IP-Adresse. Zum Beispiel: internal=http://jellyfin.example.com, external=https://jellyfin.example.com, oder all=https://jellyfin.example.com",
"LabelPublishedServerUri": "Veröffentlichte Server-URIs",
"LabelEnableSSDPTracingHelp": "Aktiviere detaillierte SSDP-Netzwerkverfolgungslogs.<br/><b>WARNUNG:</b> Dies wird erhebliche Leistungseinbußen verursachen.",
"LabelEnableSSDPTracing": "SSDP-Verfolgung aktivieren",
@ -1779,7 +1779,7 @@
"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, 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.",
"SelectAudioNormalizationHelp": "Track Gain - passt die Lautstärke der einzelnen Tracks an, so dass sie mit der gleichen Lautstärke wiedergegeben werden. Albumverstärkung - passt nur die Lautstärke aller Titel eines Albums an, wobei der Dynamikbereich des Albums erhalten bleibt. Um zwischen „Aus“ und anderen Optionen zu wechseln, muss die aktuelle Wiedergabe neu gestartet werden.",
"LabelAlbumGain": "Albumlautstärke",
"LabelSelectAudioNormalization": "Audio Normalisierung",
"HeaderAllRecordings": "Alle Aufnahmen",
@ -1956,12 +1956,20 @@
"LabelSaveTrickplayLocallyHelp": "Wenn Sie Trickplay-Bilder in Medienordnern speichern, werden sie bei Ihren Medien abgelegt und ermöglichen so eine einfache Migration und den Zugriff.",
"RenderPgsSubtitle": "Experimentelles PGS-Untertitel-Rendering",
"LabelAudioTagSettings": "Audio-Tag Einstellungen",
"LabelCustomTagDelimitersHelp": "Zeichen die zur Trennung von Tags verwendet werden sollen.",
"LabelDelimiterWhitelistHelp": "Artikel, die vom Tag-Splitting ausgeschlossen werden sollen. Ein Artikel pro Zeile.",
"PreferNonstandardArtistsTag": "Bevorzuge KÜNSTLER-Tag, falls vorhanden",
"PreferNonstandardArtistsTagHelp": "Verwenden Sie den nicht standardisierten ARTISTS-Tag anstelle des ARTIST-Tags, sofern verfügbar.",
"UseCustomTagDelimitersHelp": "Trennen Sie Künstler-/Genre-Tags mit benutzerdefinierten Zeichen.",
"LabelCustomTagDelimitersHelp": "Zeichen, die als Begrenzungszeichen zur Trennung von Tags zu behandeln sind.",
"LabelDelimiterWhitelistHelp": "Positionen, die vom Tag-Splitting ausgeschlossen werden sollen. Eine Position pro Zeile.",
"PreferNonstandardArtistsTag": "Bevorzuge den ARTISTS-Tag, falls vorhanden",
"PreferNonstandardArtistsTagHelp": "Verwende das nicht standardisierte ARTISTS-Tag anstelle des ARTIST-Tags, sofern verfügbar.",
"UseCustomTagDelimitersHelp": "Splitte Künstler-/Genre-Tags mit benutzerdefinierten Zeichen.",
"LabelCustomTagDelimiters": "Benutzerdefinierte Tag-Begrenzung",
"LabelDelimiterWhitelist": "Trennzeichen Whitelist",
"UseCustomTagDelimiters": "Benutzerdefinierte Tag-Begrenzung verwenden"
"UseCustomTagDelimiters": "Benutzerdefinierte Tag-Begrenzung verwenden",
"DateModified": "Datum geändert",
"AlwaysBurnInSubtitleWhenTranscoding": "Beim Transkodieren immer Untertitel einbrennen",
"MessageSplitVersionsError": "Beim Splitten von Versionen ist ein Fehler aufgetreten",
"LabelScreensaverTime": "Bildschirmschoner-Zeit",
"MessageCancelSeriesTimerError": "Beim Abbrechen des Serien-Timers ist ein Fehler aufgetreten",
"MessageCancelTimerError": "Beim Abbrechen des Timers ist ein Fehler aufgetreten",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Brenne alle Untertitel ein, wenn die Transcodierung ausgelöst wird. Dies gewährleistet die Synchronisierung der Untertitel nach der Transcodierung auf Kosten einer geringeren Transcodierungsgeschwindigkeit.",
"LabelScreensaverTimeHelp": "Die Zeitspanne in Sekunden der Inaktivität, die zum Starten des Bildschirmschoners erforderlich ist."
}

View file

@ -1761,5 +1761,6 @@
"PreferEmbeddedExtrasTitlesOverFileNames": "Προτιμήστε τους ενσωματωμένους τίτλους από τα ονόματα αρχείων για πρόσθετα",
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Τα πρόσθετα έχουν συχνά το ίδιο ενσωματωμένο όνομα με τον γονέα, ελέγξτε αυτό για να χρησιμοποιήσετε ενσωματωμένους τίτλους για αυτά ούτως ή άλλως.",
"LabelSegmentKeepSecondsHelp": "Χρονικό διάστημα σε δευτερόλεπτα, για το οποίο τα τμήματα θα διατηρούνται έπειτα από την λήψη τους από τον χρήστη. Λειτουργεί μόνο αν η ανίχνευση τμημάτων είναι ενεργοποιημένη.",
"AllowSubtitleManagement": "Επίτρεψε στον χρήστη να επεξεργάζεται υποτίτλους"
"AllowSubtitleManagement": "Επίτρεψε στον χρήστη να επεξεργάζεται υποτίτλους",
"AllowContentWithTagsHelp": "Μόνο εμφάνιση πολυμέσων με τουλάχιστον μία από τις καθορισμένες ετικέτες."
}

View file

@ -94,7 +94,7 @@
"Box": "Box",
"BoxRear": "Box (rear)",
"Browse": "Browse",
"BurnSubtitlesHelp": "Determine if the server should burn in subtitles while transcoding videos. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VobSub, PGS, SUB, IDX, etc.) and certain ASS or SSA subtitles.",
"BurnSubtitlesHelp": "Determine if the server should burn in subtitles. Avoiding this will greatly improve performance. Select Auto to burn image-based formats (VobSub, PGS, SUB, IDX, etc.) and certain ASS or SSA subtitles.",
"ButtonAddMediaLibrary": "Add Media Library",
"ButtonAddScheduledTaskTrigger": "Add Trigger",
"ButtonAddServer": "Add Server",
@ -1455,7 +1455,7 @@
"LabelHDHomerunPortRange": "HDHomeRun port range",
"LabelHDHomerunPortRangeHelp": "Restricts the HDHomeRun UDP port range to this value. (Default is 1024 - 65535).",
"LabelPublishedServerUri": "Published Server URIs",
"LabelPublishedServerUriHelp": "Override the URI used by Jellyfin, based on the interface, or client IP address.",
"LabelPublishedServerUriHelp": "Override the URI used by Jellyfin, based on the interface, or client IP address. For example: internal=http://jellyfin.example.com, external=https://jellyfin.example.com, or all=https://jellyfin.example.com",
"HeaderDebugging": "Debugging and Tracing",
"LabelEnableSSDPTracing": "Enable SSDP Tracing",
"LabelEnableSSDPTracingHelp": "Enable details SSDP network tracing to be logged.<br/><b>WARNING:</b> This will cause serious performance degradation.",
@ -1777,7 +1777,7 @@
"LabelIsHearingImpaired": "For hearing impaired (SDH)",
"BackdropScreensaver": "Backdrop Screensaver",
"LogoScreensaver": "Logo Screensaver",
"SelectAudioNormalizationHelp": "Track gain - adjusts the volume of each track so they playback with the same loudness. Album gain - adjusts the volume of all the tracks in an album only, keeping the album's dynamic range.",
"SelectAudioNormalizationHelp": "Track gain - adjusts the volume of each track so they playback with the same loudness. Album gain - adjusts the volume of all the tracks in an album only, keeping the album's dynamic range. Switching between \"Off\" and other options requires restarting the current playback.",
"LabelAlbumGain": "Album Gain",
"SearchResultsEmpty": "Sorry! No results found for \"{0}\"",
"LabelSelectAudioNormalization": "Audio Normalisation",
@ -1967,5 +1967,9 @@
"DateModified": "Date modified",
"MessageCancelSeriesTimerError": "An error occurred while cancelling the series timer",
"MessageCancelTimerError": "An error occurred while cancelling the timer",
"MessageSplitVersionsError": "An error occurred while splitting versions"
"MessageSplitVersionsError": "An error occurred while splitting versions",
"LabelScreensaverTime": "Screensaver Time",
"AlwaysBurnInSubtitleWhenTranscoding": "Always burn in subtitle when transcoding",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Burn in all subtitles when transcoding is triggered. This ensures subtitle synchronisation after transcoding at the cost of reduced transcoding speed.",
"LabelScreensaverTimeHelp": "The amount of time in seconds of inactivity required to start the screensaver."
}

View file

@ -34,6 +34,8 @@
"AllowStreamSharingHelp": "Allow Jellyfin to duplicate the mpegts stream from tuner and share this duplicated stream to its clients. This is useful when the tuner has total stream count limit but may also cause playback issues.",
"Alternate" : "Alternate",
"AlternateDVD" : "Alternate DVD",
"AlwaysBurnInSubtitleWhenTranscoding": "Always burn in subtitle when transcoding",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Burn in all subtitles when transcoding is triggered. This ensures subtitle synchronization after transcoding at the cost of reduced transcoding speed.",
"LabelThrottleDelaySeconds": "Throttle after",
"LabelThrottleDelaySecondsHelp": "Time in seconds after which the transcoder will be throttled. Must be large enough for the client to maintain a healthy buffer. Only works if throttling is enabled.",
"LabelSegmentKeepSeconds": "Time to keep segments",
@ -84,7 +86,7 @@
"BoxRear": "Box (rear)",
"BoxSet": "Box Set",
"Browse": "Browse",
"BurnSubtitlesHelp": "Determine if the server should burn in subtitles while transcoding videos. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VobSub, PGS, SUB, IDX, etc.) and certain ASS or SSA subtitles.",
"BurnSubtitlesHelp": "Determine if the server should burn in subtitles. Avoiding this will greatly improve performance. Select Auto to burn image based formats (VobSub, PGS, SUB, IDX, etc.) and certain ASS or SSA subtitles.",
"ButtonActivate": "Activate",
"ButtonAddImage": "Add Image",
"ButtonAddMediaLibrary": "Add Media Library",
@ -409,6 +411,7 @@
"HeaderDummyChapter": "Chapter Images",
"HeaderDVR": "DVR",
"HeaderEditImages": "Edit Images",
"HeaderEditPlaylist": "Edit Playlist",
"HeaderEnabledFields": "Enabled Fields",
"HeaderEnabledFieldsHelp": "Uncheck a field to lock it and prevent its data from being changed.",
"HeaderEpisodesStatus": "Episodes Status",
@ -444,6 +447,7 @@
"HeaderLyricDownloads": "Lyric Downloads",
"HeaderMedia": "Media",
"HeaderMediaFolders": "Media Folders",
"HeaderMediaSegmentActions": "Media Segment Actions",
"HeaderMetadataSettings": "Metadata Settings",
"HeaderMoreLikeThis": "More Like This",
"HeaderMusicQuality": "Music Quality",
@ -454,6 +458,7 @@
"HeaderNetworking": "IP Protocols",
"HeaderNewApiKey": "New API Key",
"HeaderNewDevices": "New Devices",
"HeaderNewPlaylist": "New Playlist",
"HeaderNewRepository": "New Repository",
"HeaderNextItem": "Next {0}",
"HeaderNextItemPlayingInValue": "Next {0} Playing in {1}",
@ -749,6 +754,7 @@
"LabelMaxDaysForNextUpHelp": "Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.",
"LabelMaxVideoResolution": "Maximum Allowed Video Transcoding Resolution",
"LabelMediaDetails": "Media details",
"LabelMediaSegmentsType": "{0} Segments",
"LabelLineup": "Lineup",
"LabelLocalCustomCss": "Custom CSS code for styling which applies to this client only. You may want to disable server custom CSS code.",
"LabelLocalHttpServerPortNumber": "Local HTTP port number",
@ -837,7 +843,9 @@
"LabelPublicHttpsPort": "Public HTTPS port number",
"LabelPublicHttpsPortHelp": "The public port number that should be mapped to the local HTTPS port.",
"LabelPublishedServerUri": "Published Server URIs",
"LabelPublishedServerUriHelp": "Override the URI used by Jellyfin, based on the interface, or client IP address.",
"LabelPublishedServerUriHelp": "Override the URI used by Jellyfin, based on the interface, or client IP address. For example: internal=http://jellyfin.example.com, external=https://jellyfin.example.com, or all=https://jellyfin.example.com",
"LabelQsvDevice": "QSV Device",
"LabelQsvDeviceHelp": "Specify the device for Intel QSV on a multi-GPU system. On Linux, this is the render node, e.g., /dev/dri/renderD128. On Windows, this is the device index starting from 0. Leave blank unless you know what you are doing.",
"LabelQuickConnectCode": "Quick Connect code",
"LabelReasonForTranscoding": "Reason for transcoding",
"LabelRecord": "Record",
@ -861,6 +869,8 @@
"LabelSaveTrickplayLocallyHelp": "Saving trickplay images into media folders will put them next to your media for easy migration and access.",
"LabelScheduledTaskLastRan": "Last ran {0}, taking {1}.",
"LabelScreensaver": "Screensaver",
"LabelScreensaverTime": "Screensaver Time",
"LabelScreensaverTimeHelp": "The amount of time in seconds of inactivity required to start the screensaver.",
"LabelSeasonNumber": "Season number",
"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.",
@ -1061,6 +1071,13 @@
"MediaInfoTitle": "Title",
"MediaInfoVideoRange": "Video range",
"MediaIsBeingConverted": "The media is being converted into a format that is compatible with the device that is playing the media.",
"MediaSegmentAction.None": "None",
"MediaSegmentAction.Skip": "Skip",
"MediaSegmentType.Commercial": "Commercial",
"MediaSegmentType.Intro": "Intro",
"MediaSegmentType.Outro": "Outro",
"MediaSegmentType.Preview": "Preview",
"MediaSegmentType.Recap": "Recap",
"Menu": "Menu",
"MenuOpen": "Open Menu",
"MenuClose": "Close Menu",
@ -1143,7 +1160,6 @@
"MessageUnableToConnectToServer": "We're unable to connect to the selected server right now. Please ensure it is running and try again.",
"MessageUnauthorizedUser": "You are not authorized to access the server at this time. Please contact your server administrator for more information.",
"MessageUnsetContentHelp": "Content will be displayed as plain folders. For best results use the metadata manager to set the content types of sub-folders.",
"Metadata": "Metadata",
"MetadataManager": "Metadata Manager",
"MetadataSettingChangeHelp": "Changing metadata settings will affect new content added going forward. To refresh existing content, open the detail screen and click the 'Refresh' button, or do bulk refreshes using the 'Metadata Manager'.",
"MillisecondsUnit": "ms",
@ -1321,6 +1337,7 @@
"PlayFromBeginning": "Play from beginning",
"PlaylistError.AddFailed": "Error adding to playlist",
"PlaylistError.CreateFailed": "Error creating playlist",
"PlaylistError.UpdateFailed": "Error updating playlist",
"PlaylistPublic": "Allow public access",
"PlaylistPublicDescription": "Allow this playlist to be viewed by any logged in user.",
"Playlists": "Playlists",

View file

@ -48,7 +48,7 @@
"BoxRear": "Caja (parte trasera)",
"Browse": "Explorar",
"MessageBrowsePluginCatalog": "Explora nuestro catálogo de complementos para ver los complementos disponibles.",
"BurnSubtitlesHelp": "Determina si el servidor debe quemar subtítulos mientras transcodifica videos. Evitar esto mejorará enormemente el rendimiento. Seleccione Auto para grabar formatos basados en imágenes (VobSub, PGS, SUB, IDX, etc.) y ciertos subtítulos ASS o SSA.",
"BurnSubtitlesHelp": "Determine si el servidor debe grabar los subtítulos. Evitar esto mejorará enormemente el rendimiento. Seleccione Auto para grabar formatos basados en imágenes (VobSub, PGS, SUB, IDX, etc.) y ciertos subtítulos ASS o SSA.",
"ButtonAddMediaLibrary": "Agregar biblioteca de medios",
"ButtonAddScheduledTaskTrigger": "Agregar activador",
"ButtonAddServer": "Agregar servidor",
@ -1432,7 +1432,7 @@
"LabelUDPPortRange": "Rango de Comunicación UDP",
"LabelSSDPTracingFilterHelp": "Dirección IP opcional que se utilizará como filtro para el registro del tráfico SSDP.",
"LabelSSDPTracingFilter": "Filtro SSDP",
"LabelPublishedServerUriHelp": "Omite la URL utilizada por Jellyfin, basado en la interfaz o dirección IP del cliente.",
"LabelPublishedServerUriHelp": "Anule el URI utilizado por Jellyfin, según la interfaz o la dirección IP del cliente. Por ejemplo: interno=http://jellyfin.example.com, externo=https://jellyfin.example.com o todos=https://jellyfin.example.com",
"LabelPublishedServerUri": "URLs publicadas del servidor",
"LabelIsForced": "Forzado",
"LabelHDHomerunPortRangeHelp": "Restringir el rango de puertos UDP para HDHomerun a este valor. (El valor por defecto es 1024 - 65535).",
@ -1821,7 +1821,7 @@
"LimitSupportedVideoResolutionHelp": "Utilice la 'Resolución de transcodificación de vídeo máxima permitida' como resolución de vídeo máxima admitida.",
"LabelTrickplayAccel": "Habilitar la decodificación por hardware",
"LabelTrickplayAccelEncoding": "Habilite la codificación MJPEG acelerada por hardware",
"LabelTrickplayAccelEncodingHelp": "Actualmente solo está disponible en QSV, VAAPI y VideoToolbox; esta opción no tiene ningún efecto en otros métodos de aceleración de hardware.",
"LabelTrickplayAccelEncodingHelp": "Actualmente solo está disponible en QSV, VA-API, VideoToolbox y RKMPP; esta opción no tiene ningún efecto en otros métodos de aceleración de hardware.",
"PriorityHigh": "Alta",
"PriorityBelowNormal": "Debajo de lo normal",
"PriorityIdle": "Inactiva",
@ -1835,7 +1835,7 @@
"ChannelResolutionSDPAL": "Resolución SD (PAL)",
"ChannelResolutionHD": "HD",
"ChannelResolutionFullHD": "Full HD",
"SelectAudioNormalizationHelp": "Ganancia de pista: ajusta el volumen de cada pista para que se reproduzcan con el mismo volumen. Ganancia de álbum: ajusta el volumen de todas las pistas de un álbum únicamente, manteniendo el rango dinámico del álbum.",
"SelectAudioNormalizationHelp": "Ganancia de pista: ajusta el volumen de cada pista para que se reproduzcan con el mismo volumen. Ganancia de álbum: ajusta el volumen de todas las pistas de un álbum únicamente, manteniendo el rango dinámico del álbum. Para cambiar entre \"Desactivado\" y otras opciones es necesario reiniciar la reproducción actual.",
"ConfirmDeleteSeries": "Al eliminar esta serie, se eliminarán TODOS los {0} episodios tanto del sistema de archivos como de su biblioteca multimedia. ¿Seguro que desea continuar?",
"DeleteEntireSeries": "Eliminar {0} episodios",
"DeleteSeries": "Eliminar serie",
@ -1896,5 +1896,74 @@
"HeaderAddLyrics": "Añadir letra",
"LabelIsSynced": "Está sincronizado",
"LabelDuration": "Duración",
"Lyric": "Lírica"
"Lyric": "Lírica",
"AllowFmp4TranscodingContainerHelp": "Permita que el contenedor de transcodificación fMP4 para este sintonizador habilite contenidos HEVC y HDR. No todos los sintonizadores son compatibles con este contenedor. Desactívelo si tiene problemas de reproducción.",
"AllowStreamSharingHelp": "Permita que Jellyfin duplique la transmisión mpegts del sintonizador y comparta esta transmisión duplicada con sus clientes. Esto es útil cuando el sintonizador tiene un límite de recuento total de transmisiones, pero también puede causar problemas de reproducción.",
"LibraryInvalidItemIdError": "La biblioteca está en un estado no válido y no se puede editar. Posiblemente esté encontrando un error: la ruta en la base de datos no es la ruta correcta en el sistema de archivos.",
"PreferNonstandardArtistsTagHelp": "Utilice la etiqueta ARTISTAS no estándar en lugar de la etiqueta ARTISTA cuando esté disponible.",
"MediaInfoRotation": "Rotación",
"Regional": "Región",
"HeaderAudioAdvanced": "Audio avanzado",
"LabelScreensaverTime": "Tiempo del salvapantallas",
"LabelScreensaverTimeHelp": "La cantidad de tiempo en segundos de inactividad necesaria para iniciar el protector de pantalla.",
"LabelSelectPreferredTranscodeVideoCodec": "Códec de vídeo de transcodificación preferido",
"MoveToBottom": "Mover hacia abajo",
"LabelTrickplayKeyFrameOnlyExtraction": "Generar imágenes únicamente a partir de fotogramas clave",
"VideoCodecTagNotSupported": "La etiqueta de códec de vídeo no es compatible",
"AndOtherArtists": "{0} y {1} otros artistas.",
"AlwaysRemuxFlacAudioFilesHelp": "Si tiene archivos que su navegador rechaza reproducir o en los que calcula incorrectamente las marcas de tiempo, habilítelo como solución alternativa.",
"AlwaysRemuxMp3AudioFilesHelp": "Si tiene archivos cuyas marcas de tiempo su navegador calcula de manera incorrecta, habilítelo como solución alternativa.",
"PluginDisableError": "Se produjo un error al deshabilitar el plugin.",
"PreviewLyrics": "Vista previa de la letra",
"Alternate": "Alternar",
"AlternateDVD": "DVD alternativo",
"AllowTonemappingSoftwareHelp": "El mapeo de tonos puede transformar el rango dinámico de un video de HDR a SDR manteniendo los detalles y los colores de la imagen, que son información muy importante para representar la escena original. Actualmente solo funciona con vídeos HDR10, HLG y DoVi de 10 bits.",
"Colorist": "colorista",
"AlwaysBurnInSubtitleWhenTranscoding": "Grabe siempre los subtítulos al transcodificar",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Grabe todos los subtítulos cuando se active la transcodificación. Esto garantiza la sincronización de los subtítulos después de la transcodificación a costa de una velocidad de transcodificación reducida.",
"DateModified": "Fecha de modificación",
"DisableVbrAudioEncodingHelp": "Evite que el servidor codifique audio con VBR para este cliente.",
"EnableHi10p": "Habilitar perfil H.264 alto 10",
"EnableHi10pHelp": "Habilite esta opción para evitar la transcodificación de vídeos H.264 de 10 bits. Desactive esta opción si el vídeo muestra fotogramas en blanco.",
"LabelCustomTagDelimiters": "Delimitador de etiquetas personalizado",
"LabelCustomTagDelimitersHelp": "Los caracteres se tratarán como delimitadores para separar etiquetas.",
"LabelDelimiterWhitelist": "Lista blanca de delimitadores",
"LabelDelimiterWhitelistHelp": "Artículos que se excluirán de la división de etiquetas. Un artículo por línea.",
"LabelDisableVbrAudioEncoding": "Deshabilitar la codificación de audio VBR",
"LabelEnablePlugin": "Activar Plugin",
"LabelFallbackMaxStreamingBitrate": "Velocidad de bits de transmisión máxima alternativa (Mbps)",
"LabelNoChangelog": "No se proporcionó ningún registro de cambios para esta versión.",
"LabelNotInstalled": "No instalado",
"MoveToTop": "Mover hacia arriba",
"FallbackMaxStreamingBitrateHelp": "La tasa de bits de transmisión máxima se utiliza como alternativa cuando ffprobe no puede determinar la tasa de bits de la transmisión de origen. Esto ayuda a evitar que los clientes soliciten una tasa de bits de transcodificación excesivamente alta, lo que podría provocar que el reproductor falle y sobrecargue el codificador.",
"HeaderPreviewLyrics": "Vista previa de la letra",
"LabelAllowFmp4TranscodingContainer": "Permitir contenedor de transcodificación fMP4",
"LabelAlwaysRemuxFlacAudioFiles": "Remux siempre archivos de audio FLAC",
"LabelAlwaysRemuxMp3AudioFiles": "Remux siempre archivos de audio MP3",
"LabelAllowStreamSharing": "Permitir compartir transmisiones",
"LabelAudioTagSettings": "Configuración de etiquetas de audio",
"LabelDropLyricsHere": "Suelta la letra aquí o haz clic para explorar.",
"LabelInstalled": "Instalado",
"LabelLyricDownloaders": "Descargadores de letras",
"LabelRepository": "Repositorio",
"LabelSaveTrickplayLocally": "Guarde imágenes de trickplay junto a los medios",
"LabelSaveTrickplayLocallyHelp": "Guardar imágenes de trucos en carpetas de medios las colocará junto a sus medios para facilitar la migración y el acceso.",
"LyricDownloadersHelp": "Habilite y clasifique sus descargadores de subtítulos preferidos en orden de prioridad.",
"MessageCancelSeriesTimerError": "Se produjo un error al cancelar el temporizador de la serie",
"MessageCancelTimerError": "Se produjo un error al cancelar el temporizador",
"MessageSplitVersionsError": "Se produjo un error al dividir las versiones",
"Penciller": "dibujante",
"PluginEnableError": "Se produjo un error al habilitar el plugin.",
"PluginLoadConfigError": "Se produjo un error al obtener las páginas de configuración del plugin.",
"PluginLoadRepoError": "Se produjo un error al obtener los detalles del plugin del repositorio.",
"PluginUninstallError": "Se produjo un error al desinstalar el plugin.",
"PreferNonstandardArtistsTag": "Prefiere la etiqueta ARTISTAS si está disponible",
"RenderPgsSubtitle": "Representación experimental de subtítulos PGS",
"RenderPgsSubtitleHelp": "Determine si el cliente debe representar subtítulos PGS en lugar de utilizar subtítulos grabados. Esto puede evitar la transcodificación del lado del servidor a cambio del rendimiento de renderizado del lado del cliente.",
"ReplaceTrickplayImages": "Reemplazar imágenes de trickplay existentes",
"SelectPreferredTranscodeVideoCodecHelp": "Seleccione el códec de vídeo preferido para transcodificar. Si el códec preferido no es compatible, el servidor utilizará el siguiente mejor códec disponible.",
"UseCustomTagDelimiters": "Usar delimitador de etiquetas personalizado",
"UseCustomTagDelimitersHelp": "Divida etiquetas de artista/género con caracteres personalizados.",
"Trickplay": "truco",
"LabelTrickplayKeyFrameOnlyExtractionHelp": "Extraiga fotogramas clave solo para un procesamiento significativamente más rápido con una sincronización menos precisa. Si el decodificador de hardware configurado no admite este modo, utilizará el decodificador de software en su lugar."
}

View file

@ -9,7 +9,7 @@
"Alerts": "Alertas",
"All": "Todo",
"AllChannels": "Todos los canales",
"AllComplexFormats": "Todos los formatos ((ASS, SSA, VobSub, PGS, SUB, IDX, …)",
"AllComplexFormats": "Todos los formatos (ASS, SSA, VobSub, PGS, SUB, IDX, …)",
"AllEpisodes": "Todos los episodios",
"AllLanguages": "Todos los idiomas",
"AllLibraries": "Todas las bibliotecas",
@ -374,7 +374,7 @@
"LabelAllowedRemoteAddresses": "Filtro de dirección IP remota",
"LabelAllowedRemoteAddressesMode": "Modo de filtro de dirección IP remota",
"LabelAppName": "Nombre de la aplicación",
"LabelAppNameExample": "Un nombre leible por humanos para identificar llaves de API. Esta opcion no afectara funcionalidad. Ejemplo: Sickbeard, Sonarr",
"LabelAppNameExample": "Un nombre legible para identificar las claves API. Esta configuración no afectará la funcionalidad.",
"LabelArtists": "Artistas",
"LabelArtistsHelp": "Separar múltiples artistas utilizando punto y coma.",
"LabelAudioLanguagePreference": "Idioma de audio preferido",
@ -1060,7 +1060,7 @@
"Audio": "Audio",
"Auto": "Automático",
"Banner": "Pancarta",
"BurnSubtitlesHelp": "Determina si el servidor debe grabar los subtítulos en el vídeo al transcodificar. Desactivar esta opción puede mejorar el rendimiento. Seleccione 'Auto' para grabar formatos basados en imágenes (VobSub, PGS, SUB, IDX, etc.) y ciertos subtítulos ASS o SSA.",
"BurnSubtitlesHelp": "Determina si el servidor debe incrustar los subtítulos. Evitarlo mejorará considerablemente el rendimiento. Selecciona Auto para incrustar formatos basados en imágenes (VobSub, PGS, SUB, IDX, etc.) y ciertos subtítulos ASS o SSA.",
"ButtonInfo": "Información",
"ChangingMetadataImageSettingsNewContent": "Los cambios a la configuración de descarga de metadatos e imágenes sólo se aplicará al nuevo contenido que se añada a la biblioteca. Para aplicar los cambios a los elementos existentes necesitarás actualizar los metadatos manualmente.",
"ColorPrimaries": "Colores primarios",
@ -1069,7 +1069,7 @@
"CommunityRating": "Puntuación de la comunidad",
"ContinueWatching": "Seguir viendo",
"CriticRating": "Puntuación de la crítica",
"DateAdded": "Añadido el",
"DateAdded": "Fecha de incorporación",
"DatePlayed": "Reproducido el",
"Descending": "Descendiente",
"DirectStreamHelp1": "La transmisión de video es compatible con el dispositivo, pero tiene un formato de audio incompatible (DTS, Dolby TrueHD, etc.) o un número de canales de audio. La transmisión de video se volverá a empaquetar sin pérdidas sobre la marcha antes de enviarse al dispositivo. Solo se transcodificará la transmisión de audio.",
@ -1472,7 +1472,7 @@
"LabelUDPPortRange": "Intervalo de comunicación UDP",
"LabelSSDPTracingFilterHelp": "Dirección IP opcional sobre la cual filtrar el registro de tráfico SSDP.",
"LabelSSDPTracingFilter": "Filtro SSDP",
"LabelPublishedServerUriHelp": "Anula la URI usada por Jellyfin en función a la interfaz o la dirección IP del cliente.",
"LabelPublishedServerUriHelp": "Anula la URI usada por Jellyfin, basada en la interfaz o en la dirección IP del cliente. Por ejemplo: internal=http://jellyfin.example.com, external=https://jellyfin.example.com, o all=https://jellyfin.example.com",
"LabelPublishedServerUri": "URI del servidor publicadas",
"LabelIsForced": "Forzados",
"LabelEnableIP6Help": "Activa la funcionalidad IPv6.",
@ -1778,7 +1778,7 @@
"ForeignPartsOnly": "Partes Forzadas/Foráneas solamente",
"LabelIsHearingImpaired": "Para personas con discapacidad auditiva (SDH)",
"SearchResultsEmpty": "Perdón! No se han encontrado resultados para \"{0}\"",
"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.",
"SelectAudioNormalizationHelp": "Ganancia de pista: ajusta el volumen de cada pista para que se reproduzcan con la misma intensidad. Ganancia de álbum: ajusta el volumen de todas las pistas en un álbum, manteniendo el rango dinámico del álbum. Cambiar entre 'Desactivado' y otras opciones requiere reiniciar la reproducción actual.",
"LabelAlbumGain": "Ganancia de Álbum",
"LabelSelectAudioNormalization": "Normalización de audio",
"LabelTrackGain": "Ganancia de pista",
@ -1835,7 +1835,7 @@
"ErrorDeletingLyrics": "Ha ocurrido un error al borrar las letras del servidor. Por favor asegúrate que Jellyfin tiene permisos de escritura para la carpeta de medios e inténtalo otra vez.",
"HeaderDeleteLyrics": "Borrar letras",
"Lyrics": "Letras",
"LabelTrickplayAccelEncodingHelp": "Actualmente solo disponible en QSV, VAAPI y VideoToolbox, esta opción no tiene ningún efecto en otros métodos de aceleración por hardware.",
"LabelTrickplayAccelEncodingHelp": "Actualmente disponible solo en QSV, VA-API, VideoToolbox y RKMPP. Esta opción no tiene efecto en otros métodos de aceleración de hardware.",
"LabelProcessPriorityHelp": "Configurar esto más bajo o alto determinará cómo la CPU priorizará el proceso de generación de ffmpeg para trickplay en relación con otros procesos. Si notas bajadas de rendimiento mientras las imágenes de trickplay se generan pero no quieres parar su generación por completo, intenta reducir tanto esto como el número de hilos.",
"EncodingFormatHelp": "Selecciona la codificación de vídeo a la que Jellyfin debe transcodificar. Jellyfin usará codificación por software cuando la aceleración por hardware para el formato seleccionado no esté disponible. La codificación H264 siempre estará habilitada.",
"NonBlockingScan": "No bloqueante - encola la generación, después devuelve",
@ -1918,7 +1918,7 @@
"LabelInstalled": "Instalado",
"Regional": "Regional",
"SelectPreferredTranscodeVideoCodecHelp": "Selecciona el codec de video preferido al cual transcodificar. Si el codec seleccionado no está soportado el servidor utilizará el siguiente mejor codec disponible.",
"AllowTonemappingSoftwareHelp": "El mapeo de tonos puede transformar el rango dinámico de un video de HDR a SDR manteniendo los detalles y colores de la imagen, los cuales son una información muy importante para representar la imagen original. Actualmente solo funciona con videos 10bit HDR10 y HLG.",
"AllowTonemappingSoftwareHelp": "El mapeo de tonos puede transformar el rango dinámico de un video de HDR a SDR, manteniendo los detalles de la imagen y los colores, los cuales son información muy importante para representar la escena original. Actualmente, solo funciona con videos HDR10 de 10 bits, HLG y DoVi.",
"HeaderPreviewLyrics": "Previsualizar letras",
"HeaderUploadLyrics": "Subir letras",
"LabelDuration": "Duración",
@ -1942,5 +1942,34 @@
"LyricDownloadersHelp": "Habilite y clasifique sus descargadores de subtítulos preferidos en orden de prioridad.",
"DisableVbrAudioEncodingHelp": "Evitar que el servidor codifique audio con VBR para este cliente.",
"EnableHi10p": "Habilitar perfil H.264 High 10",
"EnableHi10pHelp": "Habilite esta opción para evitar la transcodificación de videos H.264 de 10 bits. Desactive esta opción si el video muestra fotogramas en blanco."
"EnableHi10pHelp": "Habilite esta opción para evitar la transcodificación de videos H.264 de 10 bits. Desactive esta opción si el video muestra fotogramas en blanco.",
"LabelSaveTrickplayLocally": "Guardar imágenes trickplay junto a los medios",
"LabelSaveTrickplayLocallyHelp": "Al guardar imágenes trickplay en las carpetas de medios, estarán junto a tu contenido para facilitar la migración y el acceso.",
"MessageCancelSeriesTimerError": "Ocurrió un error al cancelar el temporizador de la serie",
"RenderPgsSubtitleHelp": "Determina si el cliente debe renderizar los subtítulos PGS en lugar de usar subtítulos incrustados. Esto puede evitar la transcodificación del lado del servidor a cambio del rendimiento de renderización del lado del cliente.",
"LabelAlwaysRemuxFlacAudioFiles": "Siempre remuxar archivos de audio FLAC",
"LabelAlwaysRemuxMp3AudioFiles": "Siempre remuxar archivos de audio MP3",
"LabelScreensaverTime": "Tiempo del salvapantallas",
"LabelScreensaverTimeHelp": "La cantidad de tiempo en segundos de inactividad necesaria para que se inicie el salvapantallas.",
"MessageCancelTimerError": "Ocurrió un error al cancelar el temporizador",
"RenderPgsSubtitle": "Renderizado experimental de subtítulos PGS",
"PreferNonstandardArtistsTag": "Preferir la etiqueta ARTISTS si está disponible",
"PreferNonstandardArtistsTagHelp": "Usar la etiqueta no estándar ARTISTS en lugar de la etiqueta ARTIST cuando esté disponible.",
"ReplaceTrickplayImages": "Reemplazar imágenes trickplay existentes",
"AlwaysBurnInSubtitleWhenTranscoding": "Siempre incrustar subtítulos durante la transcodificación",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Incrustar todos los subtítulos cuando se activa la transcodificación. Esto garantiza la sincronización de los subtítulos, aunque puede reducir la velocidad de transcodificación.",
"LabelAudioTagSettings": "Configuración de Etiquetas de Audio",
"DateModified": "Fecha de modificación",
"FallbackMaxStreamingBitrateHelp": "El bitrate máximo de streaming se utiliza como alternativa cuando ffprobe no puede determinar el bitrate del flujo de origen. Esto ayuda a prevenir que los clientes soliciten un bitrate de transcodificación excesivamente alto, lo que podría causar fallos en el reproductor y sobrecargar el codificador.",
"HeaderAudioAdvanced": "Configuración Avanzada de Audio",
"LabelAllowFmp4TranscodingContainer": "Permitir contenedor de transcodificación fMP4",
"LabelCustomTagDelimiters": "Delimitador de Etiqueta Personalizada",
"LabelCustomTagDelimitersHelp": "Caracteres considerados como delimitadores para separar etiquetas.",
"LabelDelimiterWhitelist": "Lista Blanca de Delimitadores",
"LabelDelimiterWhitelistHelp": "Elementos excluidos de la división de etiquetas. Un elemento por línea.",
"LabelFallbackMaxStreamingBitrate": "Bitrate máximo de transmisión de respaldo (Mbps)",
"MessageSplitVersionsError": "Ocurrió un error al dividir las versiones",
"UseCustomTagDelimiters": "Usar delimitador de etiqueta personalizado",
"UseCustomTagDelimitersHelp": "Separar etiquetas de artista/género con caracteres personalizados.",
"VideoCodecTagNotSupported": "La etiqueta del códec de video no es compatible"
}

View file

@ -592,7 +592,7 @@
"ButtonAddMediaLibrary": "Lisa meediakogu",
"ButtonAddImage": "Lisa pilt",
"ButtonActivate": "Aktiveeri",
"BurnSubtitlesHelp": "Määrab, kas server peaks videote transkoodimisel subtiitrid sisse põlema. Selle vältimine parandab jõudlust oluliselt. Pildipõhiste vormingute (VobSub, PGS, SUB, IDX jne.) ja teatud ASS- või SSA -subtiitrite põletamiseks vali 'automaatne'.",
"BurnSubtitlesHelp": "Määrab, kas server peaks subtiitrid sisse põletama. Selle vältimine parandab oluliselt jõudlust. Pildipõhiste vormingute (VobSub, PGS, SUB, IDX jne.) ja teatud ASS- või SSA -subtiitrite põletamiseks vali 'automaatne'.",
"Browse": "Sirvi",
"BoxSet": "Karbikomplekt",
"BoxRear": "Karp (taga)",
@ -806,7 +806,7 @@
"LabelRecord": "Salvesta",
"LabelReasonForTranscoding": "Transkoodimise põhjus",
"LabelQuickConnectCode": "Kiirühendumise kood",
"LabelPublishedServerUriHelp": "Alistage Jellyfini kasutatav URI liidese või kliendi IP aadressi alusel.",
"LabelPublishedServerUriHelp": "Jellyfin'i poolt kasutatava URI muutmine liidesest või kliendi IP-aadressist lähtuvalt. Näiteks: sisemine=http://jellyfin.example.com, väline=https://jellyfin.example.com või kõik=https://jellyfin.example.com",
"LabelPublishedServerUri": "Avaldatud serveri URId",
"LabelPublicHttpsPortHelp": "Avaliku pordi number, mis liidetakse kohaliku HTTPS pordiga.",
"LabelPublicHttpsPort": "Avaliku HTTPS pordi number",
@ -1661,7 +1661,7 @@
"EnableRewatchingNextUp": "Luba uuesti vaatamine Järgmises",
"IntelLowPowerEncHelp": "Madala võimsusega kodeering võib hoida tarbetut CPU-GPU sünkroonimist. Linuxis tuleb need keelata, kui i915 HuC püsivara pole konfigureeritud.",
"BackdropScreensaver": "Tausta ekraanisäästja",
"SelectAudioNormalizationHelp": "Raja võimendus reguleerib iga loo helitugevust nii, et neid esitataks sama valjusega. Albumi võimendus reguleerib ainult albumi kõigi lugude helitugevust, säilitades albumi dünaamilise ulatuse.",
"SelectAudioNormalizationHelp": "Raja võimendus reguleerib iga loo helitugevust nii, et neid esitataks sama valjususega. Albumi võimendus reguleerib ainult albumi kõigi lugude helitugevust, säilitades albumi dünaamilise ulatuse. Muutes valikuid tuleb jooksev taasesitus uuesti käivitada.",
"EnableRewatchingNextUpHelp": "Luba jaotistes „Järgmine” juba vaadatud jagude kuvamine.",
"PreferSystemNativeHwDecoder": "Eelistage OS-i DXVA või VA-API riistvaradekoodereid",
"AllowCollectionManagement": "Luba sellel kasutajal kogusid hallata",
@ -1951,5 +1951,9 @@
"MessageSplitVersionsError": "Versioonideks jagamisel tekkis viga",
"PreferNonstandardArtistsTagHelp": "Kasuta ARTIST sildi asemel ebastandartset ARTISTS silti kui see saadaval on.",
"UseCustomTagDelimiters": "Kasuta kohandatud siltide eraldajat",
"UseCustomTagDelimitersHelp": "Kasuta siltideks jagamisel kohandatud tähemärke."
"UseCustomTagDelimitersHelp": "Kasuta siltideks jagamisel kohandatud tähemärke.",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Põletage kõik subtiitrid sisse, kui transkodeerimine käivitub. See tagab subtiitrite sünkroonis olemise peale transkodeerimist, kuid teeb selle aeglasemaks.",
"AlwaysBurnInSubtitleWhenTranscoding": "Transkodeerimisel põleta alati subtiitrid sisse",
"LabelScreensaverTime": "Ekraanisäästja aeg",
"LabelScreensaverTimeHelp": "Mitteaktiivsuse aeg sekundites mille järel käivitatakse ekraanisäästja."
}

View file

@ -1926,5 +1926,6 @@
"LabelTrickplayAccelEncoding": "Käyttöönota rautakiihdytetty MJPEG enkoodaus",
"LabelTrickplayAccelEncodingHelp": "Tällä hetkellä ainoastaan käytettävissä QSV, VAAPI ja VideoToolbox, tällä valinnalla ei ole vaikutusta muihin rautakiihdytysmetodeihin.",
"HeaderVideoAdvanced": "Edistynyt video",
"PlaylistPublicDescription": "Salli tämän soittolistan katsominen jokaiselle kirjautuneelle käyttäjälle."
"PlaylistPublicDescription": "Salli tämän soittolistan katsominen jokaiselle kirjautuneelle käyttäjälle.",
"DateModified": "Muokkauspäivämäärä"
}

View file

@ -1709,5 +1709,7 @@
"RemuxHelp2": "Remultiplexer utilise une très faible puissance de calcul sans aucune perte de qualité du média.",
"LabelPlaybackInfo": "Informations de lecture",
"LabelAudioInfo": "Informations audio",
"LabelTranscodingInfo": "Informations de transcodage"
"LabelTranscodingInfo": "Informations de transcodage",
"AlwaysBurnInSubtitleWhenTranscoding": "Toujours incruster les sous-titres lors du transcodage",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Incruster tous les sous-titres lorsque le transcodage se déclenche. Cela garantit la synchronisation des sous-titres après le transcodage, au prix d'une vitesse de transcodage réduite."
}

View file

@ -47,7 +47,7 @@
"BoxRear": "Dos de boîtier",
"Browse": "Parcourir",
"MessageBrowsePluginCatalog": "Explorer notre catalogue pour voir les extensions disponibles.",
"BurnSubtitlesHelp": "Déterminer si le serveur doit incruster les sous-titres lors du transcodage de la vidéo. Les performances seront grandement améliorées sans incrustation. Sélectionner 'Auto' pour incruster les formats graphiques (VobSub, PGS, SUB, IDX, etc.) ainsi que certains sous-titres ASS ou SSA.",
"BurnSubtitlesHelp": "Déterminer si le serveur doit incruster les sous-titres. Les performances seront grandement améliorées sans incrustation. Sélectionner 'Auto' pour incruster les formats graphiques (VobSub, PGS, SUB, IDX, etc.) ainsi que certains sous-titres ASS ou SSA.",
"ButtonAddMediaLibrary": "Ajouter une médiathèque",
"ButtonAddScheduledTaskTrigger": "Ajouter un déclencheur",
"ButtonAddServer": "Ajouter un serveur",
@ -769,16 +769,16 @@
"MediaInfoTimestamp": "Horodatage",
"MediaIsBeingConverted": "Le média est converti en un format compatible avec l'appareil qui lit le média.",
"MessageAlreadyInstalled": "Cette version est déjà installée.",
"MessageAreYouSureDeleteSubtitles": "Voulez-vous vraiment supprimer ce fichier de sous-titres ?",
"MessageAreYouSureYouWishToRemoveMediaFolder": "Voulez-vous vraiment supprimer ce dossier multimédia ?",
"MessageConfirmDeleteGuideProvider": "Voulez-vous vraiment supprimer ce fournisseur de guide d'information ?",
"MessageConfirmDeleteTunerDevice": "Voulez-vous vraiment supprimer cet appareil ?",
"MessageAreYouSureDeleteSubtitles": "Voulez-vous vraiment supprimer ce fichier de sous-titres ?",
"MessageAreYouSureYouWishToRemoveMediaFolder": "Voulez-vous vraiment supprimer ce dossier multimédia ?",
"MessageConfirmDeleteGuideProvider": "Voulez-vous vraiment supprimer ce fournisseur de guide d'information ?",
"MessageConfirmDeleteTunerDevice": "Voulez-vous vraiment supprimer cet appareil ?",
"MessageConfirmProfileDeletion": "Voulez-vous vraiment supprimer ce profil ?",
"MessageConfirmRecordingCancellation": "Annuler l'enregistrement ?",
"MessageConfirmRemoveMediaLocation": "Voulez-vous vraiment supprimer cet emplacement ?",
"MessageConfirmRestart": "Voulez-vous vraiment redémarrer Jellyfin ?",
"MessageConfirmRevokeApiKey": "Voulez-vous vraiment révoquer cette clé API ? La connexion de l'application à ce serveur sera brutalement interrompue.",
"MessageConfirmShutdown": "Voulez-vous vraiment éteindre le serveur ?",
"MessageConfirmRecordingCancellation": "Annuler l'enregistrement ?",
"MessageConfirmRemoveMediaLocation": "Voulez-vous vraiment supprimer cet emplacement ?",
"MessageConfirmRestart": "Voulez-vous vraiment redémarrer Jellyfin ?",
"MessageConfirmRevokeApiKey": "Voulez-vous vraiment révoquer cette clé API ? La connexion de l'application à ce serveur sera brutalement interrompue.",
"MessageConfirmShutdown": "Voulez-vous vraiment éteindre le serveur ?",
"MessageContactAdminToResetPassword": "Veuillez contacter votre administrateur système pour réinitialiser votre mot de passe.",
"MessageCreateAccountAt": "Créer un compte sur {0}",
"MessageDeleteTaskTrigger": "Voulez-vous vraiment supprimer ce déclencheur de tâche ?",
@ -1238,7 +1238,7 @@
"SelectAdminUsername": "Veuillez choisir un nom d'utilisateur pour le compte administrateur.",
"HeaderNavigation": "Navigation",
"OptionForceRemoteSourceTranscoding": "Forcer le transcodage pour les sources de média externes telles que la TV en direct",
"MessageConfirmAppExit": "Voulez-vous quitter ?",
"MessageConfirmAppExit": "Voulez-vous quitter ?",
"LabelVideoResolution": "Résolution vidéo",
"LabelStreamType": "Type de flux",
"LabelPlayerDimensions": "Dimension du lecteur",
@ -1267,7 +1267,7 @@
"OnWakeFromSleep": "À la sortie de veille",
"WeeklyAt": "{0} à {1}",
"DailyAt": "Tous les jours à {0}",
"LastSeen": "Dernière connexion {0}",
"LastSeen": "Dernière activité {0}",
"PersonRole": "est {0}",
"ListPaging": "{0}-{1} de {2}",
"WriteAccessRequired": "Jellyfin a besoin d'un accès en écriture à ce dossier. Merci de vérifier les permissions de ce-dernier puis de réessayer.",
@ -1486,7 +1486,7 @@
"LabelHDHomerunPortRangeHelp": "Restreint la plage de ports UDP pour HDHomeRun à cette valeur. (La plage par défaut est 1024 - 65535).",
"LabelHDHomerunPortRange": "Plage de ports HDHomeRun",
"PreferFmp4HlsContainerHelp": "Préférer l'utilisation de fMP4 comme conteneur pour HLS rendant ainsi possible la lecture directe des flux HEVC et AV1 sur les appareils compatibles.",
"LabelPublishedServerUriHelp": "Remplacer l'URI utilisée par Jellyfin selon l'interface ou l'adresse IP du client.",
"LabelPublishedServerUriHelp": "Remplacer l'URI utilisée par Jellyfin selon l'interface ou l'adresse IP du client. Par exemple : internal=http://jellyfin.example.com, external=https://jellyfin.example.com, or all=https://jellyfin.example.com",
"LabelPublishedServerUri": "URIs du serveur publiées",
"LabelSyncPlayHaltPlayback": "Arrêter la lecture locale",
"SyncPlayGroupDefaultTitle": "Groupe de {0}",
@ -1769,7 +1769,7 @@
"ListView": "Vue en liste",
"GridView": "Vue en grille",
"BackdropScreensaver": "Écran de veille Diaporama",
"LogoScreensaver": "Écran de veille Logo",
"LogoScreensaver": "Logo de l'écran de veille",
"UnknownError": "Une erreur inconnue sest produite.",
"MachineTranslated": "Traduction automatique",
"AiTranslated": "Traduction par IA",
@ -1778,7 +1778,7 @@
"HeaderGuestCast": "Invités vedettes",
"LabelIsHearingImpaired": "Sous-titrage pour sourds et malentendants",
"SearchResultsEmpty": "Désolé ! Aucun résultat trouvé pour \"{0}\"",
"SelectAudioNormalizationHelp": "Gain de piste - permet de régler le volume de chaque piste de manière à ce qu'elles soient lues avec la même intensité sonore. Gain de l'album - ajuste le volume de toutes les pistes d'un album uniquement, en conservant la plage dynamique de l'album.",
"SelectAudioNormalizationHelp": "Gain de piste - permet de régler le volume de chaque piste de manière à ce qu'elles soient lues avec la même intensité sonore. Gain de l'album - ajuste le volume de toutes les pistes d'un album uniquement, en conservant la plage dynamique de l'album. Passer de \"Off\" à une autre option, requiert le redémarrage la lecture en cours.",
"LabelAlbumGain": "Gain de l'album",
"LabelSelectAudioNormalization": "Normalisation de l'audio",
"LabelTrackGain": "Gain de piste",
@ -1944,11 +1944,11 @@
"LabelAudioTagSettings": "Paramètres des balises audio",
"LabelCustomTagDelimiters": "Délimiteur de balise personnalisé",
"LabelCustomTagDelimitersHelp": "Caractères à traiter comme délimiteurs pour séparer les balises.",
"LabelLyricDownloaders": "Téléchargeurs de Lyrics",
"LabelLyricDownloaders": "Téléchargeurs de paroles",
"LabelDelimiterWhitelist": "Liste blanche des délimiteurs",
"LabelDelimiterWhitelistHelp": "Éléments à exclure de la division des balises. Un élément par ligne.",
"LabelDisableVbrAudioEncoding": "Désactiver l'encodage audio VBR",
"LabelSaveTrickplayLocally": "Enregistrer les images trickplay au même emplacement que les médias",
"LabelSaveTrickplayLocally": "Enregistrer les images de prévisualisation (trickplay) au même emplacement que les médias",
"LabelSaveTrickplayLocallyHelp": "Enregistrer les images trickplay dans les dossiers de médias les placera à côté de vos médias pour une migration et un accès faciles.",
"MediaInfoRotation": "Rotation",
"RenderPgsSubtitle": "Rendu expérimental des sous-titres PGS",
@ -1966,5 +1966,24 @@
"LabelAllowFmp4TranscodingContainer": "Autoriser le conteneur de transcodage fMP4",
"AllowTonemappingSoftwareHelp": "Le tone-mapping peut transformer la plage dynamique d'une vidéo de HDR à SDR tout en conservant les détails et les couleurs de l'image, qui sont des informations très importantes pour représenter la scène originale. Fonctionne uniquement avec les vidéos 10 bits HDR10, HLG et DoVi.",
"LabelTrickplayKeyFrameOnlyExtraction": "Générer uniquement des images à partir des images clés",
"LabelTrickplayKeyFrameOnlyExtractionHelp": "Extraire uniquement les images clés pour un traitement beaucoup plus rapide avec un minutage moins précis. Si le décodeur matériel configuré ne prend pas en charge ce mode, il utilisera le décodeur logiciel à la place."
"LabelTrickplayKeyFrameOnlyExtractionHelp": "Extraire uniquement les images clés pour un traitement beaucoup plus rapide avec un minutage moins précis. Si le décodeur matériel configuré ne prend pas en charge ce mode, il utilisera le décodeur logiciel à la place.",
"AllowFmp4TranscodingContainerHelp": "Autoriser le conteneur de transcodage fMP4 pour ce tuner afin d'activer les contenus HEVC et HDR. Tous les tuners ne sont pas compatibles avec ce conteneur. Désactivez ceci si vous rencontrez des problèmes de lecture.",
"LabelScreensaverTimeHelp": "La durée en secondes d'inactivité requise pour démarrer l'économiseur d'écran.",
"AlwaysBurnInSubtitleWhenTranscoding": "Toujours incruster les sous-titres lors du transcodage",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Incruster tous les sous-titres lorsque le transcodage se déclenche. Cela garantit la synchronisation des sous-titres après le transcodage au prix d'une vitesse de transcodage réduite.",
"LabelScreensaverTime": "Délai de l'économiseur d'écran",
"LabelQsvDevice": "Appareil QSV",
"LabelQsvDeviceHelp": "Spécifiez l'appareil pour Intel QSV sur un système multi-GPU. Sous Linux, il s'agit du nœud de rendu, par exemple, /dev/dri/renderD128. Sous Windows, il s'agit de l'indice de l'appareil commençant à 0. Laissez vide à moins que vous ne sachiez ce que vous faites.",
"HeaderMediaSegmentActions": "Actions sur les segments du média",
"HeaderEditPlaylist": "Modifier la liste de lecture",
"HeaderNewPlaylist": "Nouvelle liste de lecture",
"LabelMediaSegmentsType": "Segments {0}",
"MediaSegmentAction.None": "Aucun",
"MediaSegmentAction.Skip": "Passer",
"MediaSegmentType.Outro": "Générique de fin",
"MediaSegmentType.Commercial": "Commercial",
"MediaSegmentType.Preview": "Prévisualisation",
"MediaSegmentType.Recap": "Récapitulatif",
"MediaSegmentType.Intro": "Générique de début",
"PlaylistError.UpdateFailed": "Erreur lors de la mise à jour de la liste de lecture"
}

View file

@ -3,7 +3,7 @@
"Add": "הוסף",
"AddToCollection": "הוסף לאוסף",
"AddToPlayQueue": "הוסף לתור ברשימת ניגון",
"AddToPlaylist": "הוסף לרשימת ניגון",
"AddToPlaylist": "הוסף לרשימת השמעה",
"AdditionalNotificationServices": "עיין ברשימת התוספים להתקנת שירותי התראות נוספים.",
"All": "הכול",
"AllChannels": "כל הערוצים",
@ -67,7 +67,7 @@
"HeaderActiveDevices": "מכשירים פעילים",
"HeaderActiveRecordings": "הקלטות פעילות",
"HeaderAddToCollection": "להוסיף לאוסף",
"HeaderAddToPlaylist": "הוסף לרשימת ניגון",
"HeaderAddToPlaylist": "הוסף לרשימת השמעה",
"HeaderAdditionalParts": "חלקים נוספים",
"HeaderCancelRecording": "ביטול הקלטה",
"HeaderCancelSeries": "בטל סדרה",
@ -103,7 +103,7 @@
"HeaderScenes": "סצנות",
"HeaderSeriesOptions": "אפשרויות סדרה",
"HeaderServerSettings": "הגדרות שרת",
"HeaderSetupLibrary": "הגדר את ספריית המדיה שלך",
"HeaderSetupLibrary": "הגדרת ספריות המדיה שלך",
"HeaderSpecialEpisodeInfo": "פרטי אפיזודות מיוחדות",
"HeaderStatus": "מצב",
"HeaderSystemDlnaProfiles": "פרופילי מערכת",
@ -477,7 +477,7 @@
"ButtonAddImage": "הוסף תמונה",
"AskAdminToCreateLibrary": "בקש ממנהל ליצור ספרייה.",
"Ascending": "בסדר עולה",
"Photos": "תמונות",
"Photos": "צילומים",
"MessageConfirmShutdown": "‫האם אתה בטוח שברצונך לכבות את השרת?",
"MessageConfirmRestart": "‫האם אתה בטוח שברצונך לאתחל את שרת ה-Jellyfin?",
"HeaderThisUserIsCurrentlyDisabled": "משתמש זה אינו פעיל כרגע",
@ -584,7 +584,7 @@
"BookLibraryHelp": "ניתן להוסיף ספרים מוקלטים וספרים כתובים. עיינו {0} במדריך מתן שמות לספרים {1}.",
"Desktop": "שולחן עבודה",
"MessageDeleteTaskTrigger": "האם אתה בטוח כי ברצונך למחוק את מפעיל משימה זה?",
"LastSeen": "נראה לאחרונה ב-{0}",
"LastSeen": "פעילות אחרונה ב-{0}",
"PersonRole": "כ-{0}",
"ListPaging": "{0}-{1} מתוך {2}",
"EveryXHours": "בכל {0} שעות",
@ -596,7 +596,7 @@
"ChannelAccessHelp": "בחר את הערוצים לשיתוף עם משתמש זה. מנהלים יוכלו לערוך את כל הערוצים בעזרת מנהל המטא-דאטה.",
"ButtonResetEasyPassword": "אתחל קוד פין פשוט",
"ButtonLibraryAccess": "הרשאות גישה לספרייה",
"BurnSubtitlesHelp": "מחליט אם על השרת לצרוב כתוביות בזמן קידוד וידאו. הימנעות מכך תשפר מאוד את הביצועים. בחר \"אוטומטי\" לצריבת כתוביות על בסיס פורמט תמונה (VobSub, PGS, SUB, IDX, וכו..) וכתוביות ASS או SSA מסוימות.",
"BurnSubtitlesHelp": "מחליט אם על השרת לצרוב כתוביות. הימנעות מכך תשפר מאוד את הביצועים. בחר \"אוטומטי\" לצריבת כתוביות על בסיס פורמט תמונה (VobSub, PGS, SUB, IDX, וכו..) וכתוביות ASS או SSA מסוימות.",
"Artist": "אמן",
"AllowedRemoteAddressesHelp": "רשימת IP \\ מיסוך רשת המופרדת בפסיקים עבור רשתות שיורשו להתחבר מרחוק. במידה ותישאר ריקה, כל הכתובות יורשו להתחבר.",
"Album": "אלבום",
@ -840,7 +840,7 @@
"HeaderDeleteDevices": "מחק את כל המכשירים",
"HeaderApiKeysHelp": "תוכנות חיצוניות נדרשות להשתמש במפתח API חיצוני על מנת לתקשר עם השרת. מפתחות מונפקים ע\"י התחברות עם משתמש רגיל, או באמצעות הנפקה ידנית של מפתח לתוכנה.",
"EncoderPresetHelp": "בחר ערך מהיר יותר לשיפור ביצועים, או ערך איטי יותר לשיפור איכות.",
"H264CrfHelp": "Constant Rate Factor (CRF) הוא הגדרת איכות ברירת המחדל של מקודדי ה-x264 ו-x265. ניתן לקבוע ערך בין 0 ל-51, כשערך נמוך יותר יביא לאיכות גבוהה יותר (על חשבון קבצים גדולים יותר). ההגדרות המומלצות הן בין 18 ל-28. ערך ברירת המחדל של x264 הוא 23, ושל x265 הוא 28, אז ניתן לראות בכך נקודת התחלה.",
"H264CrfHelp": "Constant Rate Factor (CRF) הוא הגדרת איכות ברירת המחדל של מקודדי התוכנה x264 ו-x265. ניתן לקבוע ערך בין 0 ל-51, כשערך נמוך יותר יביא לאיכות גבוהה יותר (על חשבון קבצים גדולים יותר). ההגדרות המומלצות הן בין 18 ל-28. ערך ברירת המחדל של x264 הוא 23, ושל x265 הוא 28, אז ניתן לראות בכך נקודת התחלה. מקודדי חומרה לא משתמשים בהגדרות אלה.",
"FFmpegSavePathNotFound": "תיקיית FFmpeg לא נמצאה באמצעות נתיב התיקייה שהוכנס. FFprobe נדרש וצריך גם הוא להימצא באותה התיקייה. רכיבים אלו בד\"כ נמצאים ביחד באותה ההורדה. אנא בדוק את נתיב בתיקייה ונסה שנית.",
"DeleteDevicesConfirmation": "האם אתה בטוח כי ברצונך למחוק את כל המכשירים? כל החיבורים האחרים ינותקו. מכשירים יופיעו מחדש בפעם הבאה שיתחברו דרכם.",
"DeleteAll": "מחק הכל",
@ -851,7 +851,7 @@
"EnableFasterAnimations": "אנימציות מהירות",
"EnableBlurHash": "הפעל מצייני מיקום מטושטשים לתמונות",
"EnableBlurHashHelp": "תמונות שעדיין נטענות יוצגו עם מציין מיקום ייחודי.",
"Bwdif": "BWDIF",
"Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"ButtonCast": "שדר למכשיר",
"AllowTonemappingHelp": "מיפוי-טונים מאפשר המרה של וידאו מ-HDR ל-SDR תוך שמירה על פרטי וצבעי תמונה, החשובים לשימור מידע מהסצנה המקורית. כרגע עובד רק בקידוד קבצים של HDR10, HLG ו-DoVi. תכונה זו מצריכה הרצה של GPGPU בהתאם.",
"Subtitle": "כתובית",
@ -866,7 +866,7 @@
"ShowLess": "הצג פחות",
"ShowAdvancedSettings": "הצג הגדרות מתקדמות",
"SettingsWarning": "שינוי ערכים אלו עלול לגרום לחוסר יציבות או בעיות חיבור. אם אתה נתקל בבעיות, אנו ממליצים להחזיר אותם לערכי ברירת המחדל.",
"ServerRestartNeededAfterPluginInstall": "Jellyfin יצטרך לאתחל לאחר התקנת תוסף.",
"ServerRestartNeededAfterPluginInstall": "Jellyfin יצטרך לאתחל לאחר התקנת התוסף.",
"SeriesDisplayOrderHelp": "סדר פרקים על פי תאריך שידור, סדר DVD, או מספרים אבסולוטיים.",
"Series": "סדרה",
"SendMessage": "שלח הודעה",
@ -904,7 +904,7 @@
"SubtitleOffset": "קיזוז כתוביות",
"SubtitleAppearanceSettingsDisclaimer": "הגדרות אלו לא יחולו את קבצי כתוביות גרפיים (DVD ,PGS, וכו') או כתוביות SSA\\ASS עם סגנונות מוטמעים.",
"LabelAutoDiscoveryTracing": "אפשר מעקב אחר גילוי אוטומטי.",
"LabelAppNameExample": "דוגמא: Sickbeard, Sonarr",
"LabelAppNameExample": "שם לזיהוי מפתחות API. הגדרה זו לא תשנה התנהגות.",
"LabelAllowHWTranscoding": "אפשר קידוד חומרה",
"LabelAlbumArtMaxWidth": "רוחב מירבי של אומנות אלבום",
"LabelAlbumArtMaxHeight": "גובה מירבי של אומנות אלבום",
@ -1129,7 +1129,7 @@
"TypeOptionPluralBook": "ספרים",
"TypeOptionPluralAudio": "קטעי שמע",
"Video": "סרטון",
"Yadif": "YADIF",
"Yadif": "Yet Another DeInterlacing Filter (YADIF)",
"LabelMaxChromecastBitrate": "איכות הזרמת Google Cast",
"LabelMethod": "שיטה",
"LabelNewsCategories": "קטגוריות חדשות",
@ -1269,7 +1269,7 @@
"DeleteSeries": "מחיקת סידרה",
"DeleteEpisode": "מחיקת פרק",
"DeleteName": "מחיקת {0}",
"SelectAudioNormalizationHelp": "עוצמת רצועת שמע - משנה את העוצמה של כל רצועה כך שהם יתנגנו בעוצמת קול זהה. עוצמת אלבום - משנה את העוצמה של כל רצועות השמע באלבום בלבד, תוך שמירה על התחום הדינמי של האלבום.",
"SelectAudioNormalizationHelp": "עוצמת רצועת שמע - משנה את העוצמה של כל רצועה כך שעוצמת הקול תישאר זהה. עוצמת אלבום - משנה את העוצמה של רצועות השמע באלבום בלבד, תוך שמירה על התחום הדינמי של האלבום. החלפה בין \"כבוי\" ושאר האפשרויות דורשת ריסוט המנגינה.",
"LabelAllowContentWithTags": "אפשר פריטים עם תגיות",
"EnableSmoothScroll": "הפעל גלילה חלקה",
"HeaderDeleteSeries": "מחק סדרה",
@ -1278,16 +1278,80 @@
"LabelMaxDaysForNextUpHelp": "הגדר את מספר הימים המקסימלי שתכנית צריכה להישאר ברשימת 'הבא בתור' מבלי לצפות בה.",
"LabelMaxVideoResolution": "רזולוצייה מקסימלית המותרת להמרת קידוד וידאו",
"LabelLocalCustomCss": "קוד CSS לעיצוב מותאם אישית ללקוח זה בלבד. ייתכן שתרצה להשבית את קוד ה-CSS המסופק על ידי השרת.",
"LabelMaxAudiobookResume": "דקות נותרות בספר המוקלט להמשך",
"LabelMaxAudiobookResume": "דקות שנותרו להמשך הספר המוקלט",
"LabelMaxAudiobookResumeHelp": "כותרים נחשבים כנוגנו במלואם כאשר משך הזמן הנותר קטן יותר מערך זה.",
"LabelMetadataReaders": "קוראי מטא-דאטה",
"LabelMetadataSavers": "שומרי מטא-דאטה",
"PlaybackError.RateLimitExceeded": "מדיה",
"PlaybackError.RateLimitExceeded": "לא ניתן לנגן את המדיה כרגע עקבת מגבלה על קצב השימוש.",
"CoverArtist": "אמן קאבר",
"Colorist": "רשימת צבעים",
"Author": "יוצר",
"Author": "מחבר",
"ConfirmDeleteLyrics": "מחיקת המילים הללו ימחקו גם בקבצים שלך וגם בספריית המדיה שלך. אתה בטוח שברצונך להמשיך?",
"AirPlay": "AirPlay",
"Alternate": "אלטרנטיבי",
"AlternateDVD": "DVD אלטרנטיבי"
"AlternateDVD": "DVD אלטרנטיבי",
"DateModified": "תאריך שינוי",
"DeleteLyrics": "מחיקת מילות השיר",
"DisableVbrAudioEncodingHelp": "מונע מהשרת לקודד שמע בפורמט VBR עבור לקוח זה.",
"Editor": "עורך",
"Creator": "יוצר",
"EnableHi10p": "הפעלת פרופיל H.264 High 10",
"AllowFmp4TranscodingContainerHelp": "אפשר טרנסקודינג של fMP4 עבור מכוון זה כדי לאפשר תוכן HEVC ו-HDR. לא כל המכוונים תומכים בפורמט זה. בטל זאת אם יש בעיה בניגון.",
"AllowStreamSharingHelp": "אפשר ל-Jellyfin לשכפל את זרם mpgets מהמכוון ולשתףללקוחות. זה עוזר כשהמכוון מוגבל במספר הזרמים אך עלול לגרום לבעיות בניגון.",
"AlwaysRemuxFlacAudioFilesHelp": "אם יש לך קבצים שהדפדפן מסרב להציג או מראה זמנים שגויים, ניתן להפעיל זאת כפתרון חלופי.",
"AlwaysRemuxMp3AudioFilesHelp": "אם יש לך קבצים שהדפדפן מראה עבורם זמנים שגויים, ניתן להפעיל זאת כפתרון חלופי.",
"AndOtherArtists": "{0} ו-{1} אומנים נוספים.",
"AllowTonemappingSoftwareHelp": "מיפוי טונים יכול להמיר שת הטווח הדינמי של וידיאו מ-HDR ל-SDR תוך כדי שמירה על איכות וצבע התמונות, שהם חשובים להצגה. כרגע עובד רק עם סרטונים בפורמט 10bit HDR10, HLG, ו-DoVi.",
"EditLyrics": "עריכת מילות השיר",
"EnableDts": "הפעלת DTS (DCA)",
"AlwaysBurnInSubtitleWhenTranscoding": "צרוב כתוביות תמיד בעת המרת קידוד",
"EnableDtsHelp": "יש להפעיל רק אם מכשירך תומך ב-DTS או מחובר למקלט שמע תואם, אחרת תיגרם שגיאת ניגון.",
"EnableHi10pHelp": "הפעל כדי להימנע מהמרת קידוד וידיאו מסוג H.264 10-bit. יש לבטל אפשרות זו אם הוידיאו מציג פריימים ריקים.",
"ErrorDeletingLyrics": "אירעה שגיאה במחיקת המילים מהשרת. נא לבדוק שיש לג'ליפין הרשאות כתיבה לתיקיית המדיה ולנסות שוב.",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "צרוב את כל הכתוביות בזמן המרת קידוד. זה מבטיח שהכתוביות יהיו מסונכרנות אך מאט את מהירות ההמרה.",
"EnableTrueHd": "הפעלת TrueHD",
"EnableTrueHdHelp": "יש להפעיל רק אם מכשירך תומך ב-TrueHD או מחובר למקלט שמע תואם, אחרת תהיה שגיאת ניגון.",
"FallbackMaxStreamingBitrateHelp": "קצב סיביות הסטרימינג המקסימלי משמש כחלופה כאשר ffprobe לא מסוגל לקבוע את קצב סיביות המקור. זה עוזר למנוע מלקוחות מלבקש קידוד לקצב סיביות גבוה מדי, מה שיכול לגרום לנגן להיכשל ולהעמיס על המקודד.",
"HeaderAudioAdvanced": "שמע מתקדם",
"HeaderDeleteLyrics": "מחיקת מילים לשיר",
"HeaderLyricDownloads": "הורדת מילים לשיר",
"HeaderNextItem": "{0} הבא",
"HeaderNextItemPlayingInValue": "{0} הבא ינגן בעוד {1}",
"HeaderNoLyrics": "לא נמצאו מילים לשיר",
"HeaderPreviewLyrics": "תצוגה מקדימה למילות השיר",
"HeaderUploadLyrics": "העלאת מילים לשיר",
"HeaderVideoAdvanced": "וידיאו מתקדם",
"Illustrator": "מאייר",
"LabelAllowFmp4TranscodingContainer": "אפשר שינוי קידוד fMP4",
"LabelAlwaysRemuxFlacAudioFiles": "המר קבצי שמע FLAC תמיד",
"LabelAlwaysRemuxMp3AudioFiles": "המר קבצי שמע MP3 תמיד",
"LabelAllowStreamSharing": "אפשר שידור משותף",
"LabelAudioTagSettings": "הגדרות תגיות שמע",
"LabelSelectPreferredTranscodeVideoAudioCodec": "קידוד שמע מועדף בניגון וידיאו",
"LabelCustomTagDelimiters": "מפריד תגים מותאם",
"LabelCustomTagDelimitersHelp": "תווים שישמשו כמפרידים בין תגים.",
"LabelDelimiterWhitelist": "מפרידים מותרים",
"LabelDelimiterWhitelistHelp": "פריטים שיסוננו החוצה אחרי פיצול התגים. אחד בכל שורה.",
"LabelDisableVbrAudioEncoding": "ביטול קידוד שמע VBR",
"LabelDuration": "משך",
"LabelDropLyricsHere": "בטל את המילים כאן, או לחץ כדי לדפדף.",
"LabelEnablePlugin": "הפעלת התוסף",
"LabelFallbackMaxStreamingBitrate": "קצב ביטים מקסימלי לגיבוי (Mbps)",
"LabelInstalled": "מותקן",
"LabelIsSynced": "מסונכרן",
"LabelLineup": "הרכב",
"LabelLyricDownloaders": "תוכנות הורדת מילים לשירים",
"LabelMaxMuxingQueueSizeHelp": "מספר החבילות המקסימלי שניתן לאגור בזמן המתנה שכל ההזרמות יאותחלו. ניתן להגדיל זאת אם התקלת בשגיאת \"יותר מדי חבילות נאגרו לזרם הפלט\" בלוגים של FFmpeg. הערך המומלץ הוא 2048.",
"Inker": "דית",
"HeaderAddLyrics": "הוספת מילים לשיר",
"LabelMetadataDownloadersHelp": "הפעל ודרג את תוכנות הורדת המטא-דאטה לפי סדר עדיפות. תוכנות הורדת בעדיפות נמוכה יהיו בשימוש כדי למלא מידע חסר.",
"LabelMetadataPathHelp": "הגדרת מקום מוגדר אישית להורדת תמונות ומטה-דאטה.",
"LabelMetadataReadersHelp": "ניתן לדרג את מקורות המטה-דאטה המועדפים עליך לפי סדר העדפה. הקובץ הקיים הראשון ייקרא.",
"LabelMetadataSaversHelp": "בחירת פורמט הקובץ שישמש לשמירת המטה-דאטה.",
"LabelMinAudiobookResume": "מינימום דקות בספר-שמע כדי לאפשר המשך",
"LabelMinAudiobookResumeHelp": "כותרים נחשבים כלא-מנוגנים לפני זמן זה.",
"LabelNoChangelog": "לא סופק תיעוד שינויים לשחרור זה.",
"LabelNotInstalled": "לא מותקן",
"HeaderMediaSegmentActions": "פעולות על מקטע מדיה",
"LabelMediaSegmentsType": "{0} מקטעים"
}

View file

@ -164,7 +164,7 @@
"Label3DFormat": "3D formátum",
"LabelAlbumArtists": "Album előadók",
"LabelArtists": "Előadók",
"LabelAudioLanguagePreference": "Audió nyelvének beállítása",
"LabelAudioLanguagePreference": "Hang előnyben részesített nyelve",
"LabelBirthYear": "Születési év",
"LabelCachePath": "Gyorsítótár útvonal",
"LabelCollection": "Gyűjtemény",
@ -683,8 +683,8 @@
"LabelAllowHWTranscoding": "Hardveres átkódolás engedélyezése",
"LabelAllowedRemoteAddresses": "Távoli IP cím szűrő",
"LabelAllowedRemoteAddressesMode": "Távoli IP cím szűrő mód",
"LabelAppName": "App neve",
"LabelAppNameExample": "Például: Sickbeard, Sonarr",
"LabelAppName": "Alkalmazás neve",
"LabelAppNameExample": "Ember számára olvasható név az API -kulcsok azonosításához. Ez a beállítás nincs hatással a működésre.",
"LabelAutomaticallyRefreshInternetMetadataEvery": "A metaadatok automatikus frissítése az internetről",
"LabelBindToLocalNetworkAddress": "Kötés a helyi hálózati címhez",
"LabelBindToLocalNetworkAddressHelp": "A helyi IP cím felülbírálása a HTTP szerverhez való csatlakozáshoz. Ha üres marad, a szerver minden elérhető címhez kötődik. Az érték megváltoztatásához a szerver újraindítása szükséges.",
@ -1148,11 +1148,11 @@
"XmlDocumentAttributeListHelp": "Ezek a tulajdonságok minden XML válaszüzenet gyökér elemére alkalmazásra kerülnek.",
"Thumb": "Miniatűr",
"LabelBitrate": "Bitráta",
"LabelAudioSampleRate": "Audió mintavételi ráta",
"LabelAudioCodec": "Audió kódek",
"LabelAudioChannels": "Audió csatorna",
"LabelAudioBitrate": "Audió bitráta",
"LabelAudioBitDepth": "Audió bitmélység",
"LabelAudioSampleRate": "Hang mintavételi sebessége",
"LabelAudioCodec": "Hangkodek",
"LabelAudioChannels": "Hangcsatornák",
"LabelAudioBitrate": "Hang bitsebessége",
"LabelAudioBitDepth": "Hang bitmélysége",
"CopyStreamURLSuccess": "URL másolása sikeres.",
"CopyStreamURL": "Stream URL másolása",
"PlaybackData": "Lejátszási információk",
@ -1269,7 +1269,7 @@
"Album": "Album",
"LabelLibraryPageSizeHelp": "Az oldalnként megmutatott elemek száma. Nullára állítva a lapozási funkció ki lesz kapcsolva.",
"LabelLibraryPageSize": "Könyvtár oldalméret",
"LabelDeinterlaceMethod": "Deinterlacing mód",
"LabelDeinterlaceMethod": "Váltottsorosság-mentesítő mód",
"DeinterlaceMethodHelp": "Válaszd ki a szétválasztási módot, amelyet a váltott soros tartalom szoftveres átkódolásához használ. Ha engedélyezed a hardveres deinterlace-t támogató hardveres gyorsítást, akkor a hardveres deinterlacer lesz használva e beállítás helyett.",
"UnsupportedPlayback": "A Jellyfin nem tudja feloldani a DRM-mel védett tartalmak titkosítását, de ettől függetlenül mindig megpróbálja a lejátszást, a védett címek esetén is. Néhány fájl emiatt teljesen feketeként jelenhet meg a titkosítás vagy a nem támogatott funkciók miatt, például az interaktív címeknél.",
"Yadif": "YADIF",
@ -1373,7 +1373,7 @@
"ButtonUseQuickConnect": "Gyors kapcsolódás használata",
"ButtonActivate": "Aktiválás",
"Authorize": "Engedélyezés",
"Bwdif": "BWDIF",
"Bwdif": "Bob Weaver váltottsorosság-mentesítő szűrő (BWDIF)",
"SpecialFeatures": "Speciális tartalmak",
"QuickConnectNotActive": "A gyors csatlakozás nem engedélyezett ezen a szerveren",
"QuickConnectNotAvailable": "Kérd meg a szerver adminisztrátorát, hogy engedélyezze a gyors csatlakozást",
@ -1773,9 +1773,9 @@
"LabelIsHearingImpaired": "Hallássérültek számára (SDH)",
"GoHome": "Kezdőlapra",
"BackdropScreensaver": "Háttér képernyővédő",
"SelectAudioNormalizationHelp": "Sáverősítés - az egyes sávok hangerejét állítja be, hogy azonos hangerővel játsszák le őket. Album gain - csak az album összes zeneszámának hangerejét állítja be, megtartva az album dinamikatartományát.",
"SelectAudioNormalizationHelp": "Számerősítés az egyes számok hangerejét állítja be, hogy azonos hangerővel legyenek lejátszva. Albumerősítés az album összes számának hangerejét állítja, megtartva az album dinamikatartományát.",
"LabelAlbumGain": "Album nyereség",
"LabelSelectAudioNormalization": "Hang normalizálás",
"LabelSelectAudioNormalization": "Hangnormalizálás",
"LogoScreensaver": "Logo képernyővédő",
"UnknownError": "Ismeretlen hiba történt.",
"LabelTrackGain": "Címnyereség",
@ -1873,5 +1873,38 @@
"AllowFmp4TranscodingContainerHelp": "Az fMP4 átkódolási konténer engedélyezése ehhez a tunerhez, a HEVC és HDR tartalmak engedélyezéséhez. Nem minden tuner kompatibilis ezzel a tárolóval. Tiltsa le, ha lejátszási problémákat tapasztal.",
"Alternate": "Alternatív",
"AlternateDVD": "Alternatív DVD",
"AllowTonemappingSoftwareHelp": "A tónusleképzés képes a HDR videók dinamika tartományát SDR tartományba átalakítani, miközben megtartja a kép színét és részleteit, ami elengedhetetlen az eredeti jelenet megőrzéséhez. Jelenleg csak 10 bites HDR10, HLG és DoVi tartalmakon működik."
"AllowTonemappingSoftwareHelp": "A tónusleképzés képes a HDR videók dinamika tartományát SDR tartományba átalakítani, miközben megtartja a kép színét és részleteit, ami elengedhetetlen az eredeti jelenet megőrzéséhez. Jelenleg csak 10 bites HDR10, HLG és DoVi tartalmakon működik.",
"DateModified": "Dátum módosítva",
"EditLyrics": "Dalszöveg módosítása",
"DisableVbrAudioEncodingHelp": "Megakadályozza a szervert hogy a hangot VBR-rel kódolja a kliensnek.",
"HeaderAddLyrics": "Dalszöveg hozzáadása",
"EnableHi10p": "H.264 High 10 profil bekapcsolása",
"EnableHi10pHelp": "Kapcsold be hogy megakadályozd a H.264 10-bites videók transzkódolását. Kapcsold ki ezt a beállítást ha a videó üres képkockákat jelenít meg.",
"HeaderAudioAdvanced": "Haladó Hang",
"EnableTrueHdHelp": "Csak akkor kapcsold be ha az eszköz támogatja a TrueHD-t vagy csatlakoztatva van egy kompatibilis hang vevőhöz, egyébként lejátszási hibát okozhat.",
"HeaderPreviewLyrics": "Dalszöveg Előnézet",
"HeaderLyricDownloads": "Dalszöveg Letöltések",
"HeaderUploadLyrics": "Dalszöveg Feltöltése",
"HeaderNextItemPlayingInValue": "Következő: {0} Lejátszás kezdődik: {1}",
"Inker": "Színező",
"LabelAllowFmp4TranscodingContainer": "fMP4 transzkódoló container engedélyezése",
"Colorist": "Kolorista",
"AlwaysRemuxFlacAudioFilesHelp": "Ha vannak olyan fájlok amiket a böngésző nem játszik le vagy helytelenül számítja ki az időbélyegeket akkor kapcsold be ezt mint megoldás.",
"AlwaysRemuxMp3AudioFilesHelp": "Ha vannak olyan fájlok amikben a böngésző helytelenül számítja ki az időbélyegeket akkor kapcsold be ezt mint megoldás.",
"CoverArtist": "Borító művész",
"AndOtherArtists": "{0} és {1} további művész.",
"EnableDts": "DTS (DCA) bekapcsolása",
"EnableTrueHd": "TrueHD bekapcsolása",
"HeaderNextItem": "Következő {0}",
"HeaderVideoAdvanced": "Haladó VIdeó",
"Illustrator": "Illusztrátor",
"LabelAlwaysRemuxFlacAudioFiles": "Mindig remuxolja a FLAC-hangfájlokat",
"LabelAlwaysRemuxMp3AudioFiles": "Mindig remuxolja az MP3-hangfájlokat",
"LabelCustomTagDelimiters": "Egyéni címkeelválasztó",
"LabelCustomTagDelimitersHelp": "A címkék elválasztójaként kezelendő karakterek.",
"LabelDuration": "Hossz",
"LabelAudioTagSettings": "Hangcímke-beállítások",
"LabelDelimiterWhitelist": "Elválasztók engedélyezési listája",
"LabelDelimiterWhitelistHelp": "Elemek, melyek nem lesznek figyelembe véve a címkefelosztásnál. Soronként egy elem.",
"LabelDisableVbrAudioEncoding": "Változó bitsebességű hangfelvétel letiltása"
}

View file

@ -42,7 +42,7 @@
"BoxRear": "Box (retro)",
"Browse": "Esplora",
"MessageBrowsePluginCatalog": "Sfoglia il catalogo dei Plugins.",
"BurnSubtitlesHelp": "Determina se il server deve imprimere i sottotitoli quando i video vengono convertiti. Evitare ciò migliorerà di molto le prestazioni. Selezionare Auto per imprimere formati basati sull'immagine (VobSub, PGS, SUB, IDX, ecc.) e alcuni sottotitoli ASS o SSA.",
"BurnSubtitlesHelp": "Determina se il server deve imprimere i sottotitoli. Evitare ciò migliorerà di molto le prestazioni. Selezionare Auto per imprimere formati basati sull'immagine (VobSub, PGS, SUB, IDX, ecc.) e alcuni sottotitoli ASS o SSA.",
"ButtonAddMediaLibrary": "Aggiungi raccolta multimediale",
"ButtonAddScheduledTaskTrigger": "Aggiungi operazione",
"ButtonAddServer": "Aggiungi server",
@ -1424,7 +1424,7 @@
"YoutubePlaybackError": "Il video richiesto non può essere riprodotto.",
"YoutubeBadRequest": "Richiesta non valida.",
"PluginFromRepo": "{0} dal repository {1}",
"LabelPublishedServerUriHelp": "Sovrascrivi l'URI utilizzata da Jellyfin basandosi sull'interfaccia o sull'indirizzo IP del client.",
"LabelPublishedServerUriHelp": "Sovrascrivi l'URI utilizzata da Jellyfin basandosi sull'interfaccia o sull'indirizzo IP del client, ad esempio: internal=http://jellyfin.example.com, external=https://jellyfin.example.com, oppure all=https://jellyfin.example.com",
"LabelMaxMuxingQueueSizeHelp": "Massimo numero di pacchetti che possono essere bufferizzati prima che tutti gli stream vengano inizializzati. Prova ad incrementare il numero se si nota nei messaggi di errore di FFmpeg il messaggio \"Too many packets buffered for output stream\". Il valore raccomandato è 2048.",
"LabelEnableIP6Help": "Abilita la funzionalità IPv6.",
"LabelEnableIP6": "Abilita IPv6",
@ -1946,5 +1946,7 @@
"ReplaceTrickplayImages": "Sostituisci le immagini trickplay",
"FallbackMaxStreamingBitrateHelp": "Il bitrate massimo dello streaming viene utilizzato quando ffprobe non è in grado di determinare il bitrate sorgente. Ciò aiuta a impedire che i client richiedano un bitrate di transcodifica troppo alto che potrebbe causare il fallimento del player e il sovraccarico dell'encoder.",
"LabelFallbackMaxStreamingBitrate": "Bitrate massimo di ripiego (Mbps)",
"DateModified": "Data di modifica"
"DateModified": "Data di modifica",
"LabelScreensaverTimeHelp": "Tempo di inattività in secondi per l'avvio dello screensaver.",
"LabelLyricDownloaders": "Scaricatori di testi"
}

View file

@ -971,7 +971,7 @@
"AllLanguages": "Alle språk",
"AllComplexFormats": "Alle avanserte formater (ASS, SSA, VobSub, PGS, SUB, IDX, …)",
"AccessRestrictedTryAgainLater": "Tilgang er for øyeblikket begrenset. Vennligst prøv igjen senere.",
"BurnSubtitlesHelp": "Angir om serveren skal brenne inn undertekster imens videoer konverteres. Ytelsen på serveren vil forbedres dersom tekstingen ikke brennes inn. Velg Automatisk for å brenne inn bildebaserte formater (VobSub, PGS, SUB, IDX, osv.) og enkelte ASS eller SSA-undertekster.",
"BurnSubtitlesHelp": "Angir om tjeneren skal brenne inn undertekster. Ytelsen på serveren vil forbedres dersom tekstingen ikke brennes inn. Velg Automatisk for å brenne inn bildebaserte formater (VobSub, PGS, SUB, IDX, osv.) og enkelte ASS eller SSA-undertekster.",
"General": "Generelt",
"ChangingMetadataImageSettingsNewContent": "Endringer gjort i innstillinger for metadata eller omslagsbilder vil kun gjelde nytt innhold i biblioteket ditt. For å endre eksisterende innhold, må du oppdatere dets metadata manuelt.",
"DefaultSubtitlesHelp": "Undertekster lastes inn basert på flaggene \"standard\" og \"tvungen\" i videoens integrerte metadata. Språkpreferanser tas høyde for dersom flere valg er tilgjengelig.",
@ -1777,7 +1777,7 @@
"BackdropScreensaver": "Skjermsparingsbakgrunn",
"ForeignPartsOnly": "Kun tvungne/fremmede deler",
"SearchResultsEmpty": "Sorry! Ingen resultater funnet for \"{0}\"",
"SelectAudioNormalizationHelp": "Sporjustering - justerer volumet for hvert spor så de spiller av med samme volum. Albumsjustering - justerer volumet på alle sporene i et album, og beholder albumets dynamiske rekkevidde.",
"SelectAudioNormalizationHelp": "Sporjustering - justerer volumet for hvert spor så de spiller av med samme volum. Albumsjustering - justerer volumet på alle sporene i et album, og beholder albumets dynamiske rekkevidde. Bytte mellom \"Av\" og andre alternativer krever at du starter den gjeldende avspillingen på nytt.",
"LabelAlbumGain": "Albumjustering",
"LabelSelectAudioNormalization": "Lydnormalisering",
"LabelTrackGain": "Sporjustering",
@ -1962,5 +1962,9 @@
"DateModified": "Data modifisert",
"MessageCancelSeriesTimerError": "Det oppstod en feil under avbryting av serietidtakeren",
"MessageCancelTimerError": "Det oppstod en feil under avbryting av tidtakeren",
"MessageSplitVersionsError": "Det oppsto en feil under oppdeling av versjoner"
"MessageSplitVersionsError": "Det oppsto en feil under oppdeling av versjoner",
"LabelScreensaverTime": "Tid for skjermsparer",
"LabelScreensaverTimeHelp": "Hvor lang tid i sekunder med inaktivitet som kreves for å starte skjermspareren.",
"AlwaysBurnInSubtitleWhenTranscoding": "Alltid brenn inn undertekst ved omkoding",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Brenn in all undertekst når omkoding aktiveres. Dette sikrer synkronisering av undertekst etter transkoding på bekostning av redusert transkodingshastighet."
}

View file

@ -46,7 +46,7 @@
"BoxRear": "Hoes (achterkant)",
"Browse": "Bladeren",
"MessageBrowsePluginCatalog": "Bekijk de plug-in-catalogus voor beschikbare plug-ins.",
"BurnSubtitlesHelp": "Bepaal of de server ondertiteling moet inbranden bij het transcoderen van video's. Dit heeft een nadelig effect op de systeemprestaties. Selecteer Automatisch om afbeelding-gebaseerde formaten (VobSub, PGS, SUB, IDX etc.) en bepaalde ASS- of SSA-ondertiteling in te branden.",
"BurnSubtitlesHelp": "Bepaal of de server ondertiteling moet inbranden. Dit heeft een nadelig effect op de systeemprestaties. Selecteer Automatisch om op afbeeldingen gebaseerde formaten (VobSub, PGS, SUB, IDX etc.) en bepaalde ASS- of SSA-ondertiteling in te branden.",
"ButtonAddMediaLibrary": "Mediabibliotheek toevoegen",
"ButtonAddScheduledTaskTrigger": "Trigger toevoegen",
"ButtonAddServer": "Server toevoegen",
@ -1459,7 +1459,7 @@
"LabelUDPPortRange": "UDP-communicatiebereik",
"LabelSSDPTracingFilterHelp": "Optioneel IP-adres waarop het geregistreerde SSDP-verkeer wordt gefilterd.",
"LabelSSDPTracingFilter": "SSDP-filter",
"LabelPublishedServerUriHelp": "Overschrijf de URI die door Jellyfin wordt gebruikt, op basis van de interface of het IP-adres van de client.",
"LabelPublishedServerUriHelp": "Overschrijf de URI die door Jellyfin wordt gebruikt, op basis van de interface of het IP-adres van de cliënt. Bijvoorbeeld: internal=http://jellyfin.example.com, external=https://jellyfin.example.com of all=https://jellyfin.example.com",
"LabelPublishedServerUri": "Gepubliceerde server-URI's",
"LabelIsForced": "Geforceerd",
"LabelHDHomerunPortRangeHelp": "Beperkt het UDP-poortbereik van HDHomeRun tot deze waarde. (Standaard is 1024 - 65535).",
@ -1895,7 +1895,7 @@
"LabelSelectPreferredTranscodeVideoAudioCodec": "Gewenste geluidscodec bij afspelen video",
"SelectPreferredTranscodeVideoAudioCodecHelp": "Selecteer de gewenste geluidscodec om naartoe te transcoderen bij video-inhoud. Als de voorkeurscodec niet wordt ondersteund, gebruikt de server de beste codec die wel beschikbaar is.",
"Alternate": "Alternatief",
"AlternateDVD": "Alternatief dvd",
"AlternateDVD": "Alternatieve dvd",
"Regional": "Regionaal",
"LabelSelectPreferredTranscodeVideoCodec": "Gewenste beeldcodec voor transcoderen",
"SelectPreferredTranscodeVideoCodecHelp": "Selecteer de gewenste beeldcodec om naartoe te transcoderen. Als de voorkeurscodec niet wordt ondersteund, gebruikt de server de beste codec die wel beschikbaar is.",
@ -1966,5 +1966,23 @@
"MessageCancelTimerError": "Er is een fout opgetreden bij het annuleren van de tijdklok",
"MessageSplitVersionsError": "Er is een fout opgetreden bij het splitsen van de versies",
"MessageCancelSeriesTimerError": "Er is een fout opgetreden bij het annuleren van de serietijdklok",
"DateModified": "Datum gewijzigd"
"DateModified": "Datum gewijzigd",
"LabelScreensaverTimeHelp": "Het aantal seconden inactiviteit waarna de schermbeveiliging gestart wordt.",
"LabelScreensaverTime": "Wachttijd schermbeveiliging",
"AlwaysBurnInSubtitleWhenTranscoding": "Ondertiteling altijd inbranden bij transcoderen",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Alle ondertiteling inbranden wanneer er getranscodeerd wordt. Dit verzekert dat ondertiteling gelijkloopt, maar verlaagt de transcodeersnelheid.",
"LabelQsvDevice": "QSV-apparaat",
"LabelQsvDeviceHelp": "Op een systeem met meerdere GPU's is dit het apparaat dat gebruikt moet worden voor Intel QSV. Op Linux is dit de render-node, bijvoorbeeld /dev/dri/renderD128. Op Windows is dit de apparaatindex startend bij 0. Laat dit leeg tenzij je weet wat je doet.",
"HeaderEditPlaylist": "Afspeellijst bewerken",
"HeaderNewPlaylist": "Nieuwe afspeellijst",
"LabelMediaSegmentsType": "{0} segmenten",
"MediaSegmentAction.None": "Geen",
"MediaSegmentAction.Skip": "Overslaan",
"MediaSegmentType.Commercial": "Reclame",
"MediaSegmentType.Intro": "Intro",
"MediaSegmentType.Outro": "Outro",
"MediaSegmentType.Preview": "Voorvertoning",
"MediaSegmentType.Recap": "Terugblik",
"PlaylistError.UpdateFailed": "Fout bij bijwerken afspeellijst",
"HeaderMediaSegmentActions": "Acties voor mediasegmenten"
}

View file

@ -51,7 +51,7 @@
"BoxRear": "Pudełko (tył)",
"Browse": "Przeglądaj",
"MessageBrowsePluginCatalog": "Przejrzyj nasz katalog wtyczek żeby zobaczyć dostępne wtyczki.",
"BurnSubtitlesHelp": "Określ czy serwer powinien wypalać napisy podczas konwersji wideo. Unikanie wypalania napisów znacząco poprawia wydajność. Wybierz Automatycznie, w celu wypalania zarówno napisów w formatach graficznych (VobSub, PGS, SUB, IDX, itd.), jak i pewnych napisów ASS lub SSA.",
"BurnSubtitlesHelp": "Określ, czy serwer powinien wypalać napisy. Unikanie wypalania napisów znacząco poprawia wydajność. Wybierz Automatycznie w celu wypalania napisów w formatach graficznych (VobSub, PGS, SUB, IDX itp.) oraz niektórych napisów ASS lub SSA.",
"ButtonAddMediaLibrary": "Dodaj media do biblioteki",
"ButtonAddScheduledTaskTrigger": "Dodaj wyzwalacz",
"ButtonAddServer": "Dodaj serwer",
@ -1115,7 +1115,7 @@
"Trailers": "Zwiastuny",
"Transcoding": "Transkodowanie",
"Tuesday": "Wtorek",
"TvLibraryHelp": "Zapoznaj się z instrukcją{1} nazewnictwa seriali {0}.",
"TvLibraryHelp": "Zapoznaj się z {0}instrukcją nazewnictwa seriali{1}.",
"Uniform": "Jednolity",
"UninstallPluginConfirmation": "Czy na pewno chcesz usunąć {0}?",
"HeaderUninstallPlugin": "Usuń wtyczkę",
@ -1422,7 +1422,7 @@
"QuickConnectAuthorizeFail": "Nieznany kod szybkiego łączenia",
"QuickConnect": "Szybkie łączenie",
"LabelQuickConnectCode": "Kod szybkiego łączenia",
"LabelPublishedServerUriHelp": "Nadpisz URI używane przez Jellyfin bazując na interfejsie lub adresie IP klienta.",
"LabelPublishedServerUriHelp": "Zastąp URI używany przez Jellyfin na podstawie interfejsu lub adresu IP klienta. Na przykład: internal=http://jellyfin.example.com, external=https://jellyfin.example.comm lub all=https://jellyfin.example.com",
"LabelPublishedServerUri": "Publiczne URI serwera",
"LabelMinAudiobookResumeHelp": "Tytuły są uważane za nieodtworzone jeśli zostały zatrzymane przed tym czasem.",
"LabelMinAudiobookResume": "Minimalne wznowienie audiobooka w minutach",
@ -1780,7 +1780,7 @@
"LabelAlbumGain": "Wzmocnienie albumu",
"LabelSelectAudioNormalization": "Normalizacja dźwięku",
"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.",
"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. Przełączanie między opcją \"Wyłączone\" a innymi wymaga ponownego uruchomienia bieżącego odtwarzania.",
"SearchResultsEmpty": "Wybacz! Nie znaleziono wyników dla „{0}”",
"HeaderAllRecordings": "Wszystkie nagrania",
"LabelBuildVersion": "Wersja kompilacji",
@ -1967,5 +1967,23 @@
"MessageCancelSeriesTimerError": "Wystąpił błąd podczas anulowania timera serialu",
"MessageCancelTimerError": "Wystąpił błąd podczas anulowania timera",
"MessageSplitVersionsError": "Wystąpił błąd podczas podziału wersji",
"DateModified": "Data modyfikacji"
"DateModified": "Data modyfikacji",
"LabelScreensaverTime": "Czas wygaszacza ekranu",
"LabelScreensaverTimeHelp": "Ilość czasu bezczynności w sekundach, potrzebna do uruchomienia wygaszacza ekranu.",
"AlwaysBurnInSubtitleWhenTranscoding": "Zawsze wypalaj napisy podczas transkodowania",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Wypal wszystkie napisy, gdy zostanie wyzwolone transkodowanie. Zapewnia to synchronizację napisów po transkodowaniu kosztem zmniejszonej prędkości transkodowania.",
"LabelQsvDevice": "Urządzenie QSV",
"LabelQsvDeviceHelp": "Określ urządzenie dla Intel QSV w systemie z wieloma GPU. W systemie Linux jest to węzeł renderowania, np. /dev/dri/renderD128. W systemie Windows jest to indeks urządzenia zaczynający się od 0. Pozostaw puste, chyba że wiesz, co robisz.",
"HeaderEditPlaylist": "Edytuj listę odtwarzania",
"HeaderNewPlaylist": "Nowa lista odtwarzania",
"LabelMediaSegmentsType": "Segmenty: {0}",
"MediaSegmentAction.None": "Brak",
"MediaSegmentAction.Skip": "Pomiń",
"MediaSegmentType.Commercial": "Reklama",
"MediaSegmentType.Intro": "Wstęp",
"MediaSegmentType.Outro": "Zakończenie",
"PlaylistError.UpdateFailed": "Błąd podczas aktualizacji listy odtwarzania",
"MediaSegmentType.Preview": "Zapowiedź",
"MediaSegmentType.Recap": "Podsumowanie",
"HeaderMediaSegmentActions": "Działania segmentu mediów"
}

View file

@ -984,7 +984,7 @@
"CancelSeries": "Ukončiť seriál",
"ButtonSplit": "Rozdeliť",
"ButtonAddImage": "Pridať obrázok",
"BurnSubtitlesHelp": "Určuje, či má server pri prekódovaní videa vypáliť titulky do obrazu. Vynechanie tejto možnosti výrazne zvýši výkon. Vyberte možnosť Automaticky, pokiaľ chcete vypáliť do obrazu titulky v grafickom formáte (VobSub, PGS, SUB, IDX, atd.) a niektoré ASS alebo SSA titulky.",
"BurnSubtitlesHelp": "Určuje, či má server vypáliť titulky do videa. Vynechanie tejto možnosti výrazne zvýši výkon. Vyberte možnosť Automaticky, pokiaľ chcete vypáliť do obrazu titulky v grafickom formáte (VobSub, PGS, SUB, IDX, atd.) a niektoré ASS alebo SSA titulky.",
"MessageBrowsePluginCatalog": "Prehliadnite si náš katalóg dostupných zásuvných modulov.",
"Browse": "Prechádzať",
"Blacklist": "Blacklist",
@ -1102,7 +1102,7 @@
"MediaInfoPixelFormat": "Pixel formát",
"MediaInfoFramerate": "Snímková frekvencia",
"MediaInfoDefault": "Predvolený",
"MediaInfoCodecTag": "Štítok kodeku",
"MediaInfoCodecTag": "Tag kodeku",
"MediaInfoBitrate": "Dátový tok",
"MediaInfoAnamorphic": "Anamorfné",
"MapChannels": "Mapovať kanály",
@ -1428,7 +1428,7 @@
"LabelSSDPTracingFilterHelp": "Voliteľná IP adresa, pomocou ktorej sa má filtrovať logovaná SSDP komunikácia.",
"LabelSSDPTracingFilter": "SSDP filter",
"LabelQuickConnectCode": "Kód pre Rýchle pripojenie",
"LabelPublishedServerUriHelp": "Prepíšte URI požadovaným serverom Jellyfin v závislosti na rozhraní alebo IP adrese klienta.",
"LabelPublishedServerUriHelp": "Prepíšte URI požadovaným serverom Jellyfin v závislosti na rozhraní alebo IP adrese klienta. Napríklad: internal=http://jellyfin.example.com, external=https://jellyfin.example.com alebo all=https://jellyfin.example.com",
"LabelPublishedServerUri": "Verejné URI serveru",
"LabelOpenclDeviceHelp": "Zariadenie OpenCL použité pre mapovanie tónov. Naľavo od bodky je číslo platformy, napravo je číslo zariadenia na tejto platforme. Predvolená hodnota je 0.0. Je vyžadovaný súbor aplikácie FFmpeg obsahujúci metódu hardvérovej akcelerácie OpenCL.",
"LabelOpenclDevice": "Zariadenie OpenCL",
@ -1777,7 +1777,7 @@
"LabelThrottleDelaySecondsHelp": "Čas v sekundách, po ktorom bude prekódovanie obmedzené. Musí byť dostatočne veľký, aby mal klient v rezerve dostatočné množstvo prehrávaného súboru. Funguje len vtedy, ak je povolená funkcia Obmedziť prekódovanie.",
"AllowSegmentDeletionHelp": "Odstránenie starých segmentov po ich stiahnutí klientom. Tým sa zabráni tomu, aby sa celý prekódovaný súbor uložil na disk. Ak sa vyskytnú problémy s prehrávaním, vypnite túto funkciu.",
"LabelSegmentKeepSecondsHelp": "Čas v sekundách, počas ktorého sa majú segmenty uchovávať po ich stiahnutí klientom. Funguje len vtedy, ak je povolené mazanie segmentov.",
"SelectAudioNormalizationHelp": "Zosilnenie stopy - upravuje hlasitosť jednotlivých stôp tak, aby sa prehrávali s rovnakou hlasitosťou. Zosilnenie pre album - upravuje hlasitosť všetkých skladieb iba v albume, pričom zachováva dynamický rozsah albumu.",
"SelectAudioNormalizationHelp": "Zosilnenie stopy - upravuje hlasitosť jednotlivých stôp tak, aby sa prehrávali s rovnakou hlasitosťou. Zosilnenie pre album - upravuje hlasitosť všetkých skladieb iba v albume, pričom zachováva dynamický rozsah albumu. Vypnutie a následné zapnutie si vyžaduje reštartovanie aktuálne prehrávanej skladby.",
"LabelAlbumGain": "Zosilnenie pre album",
"LabelSelectAudioNormalization": "Normalizácia hlasitosti",
"LabelTrackGain": "Zosilnenie stopy",
@ -1914,7 +1914,7 @@
"MediaInfoRotation": "Rotácia",
"MoveToBottom": "Presunúť na spodnú časť",
"MoveToTop": "Presunúť na vrchnú časť",
"PreferNonstandardArtistsTagHelp": "Ak je to možné, použiť neštandardnú značku ARTISTS namiesto značky ARTIST.",
"PreferNonstandardArtistsTagHelp": "Ak je to možné, použiť neštandardný tag ARTISTS namiesto tagu ARTIST.",
"PreviewLyrics": "Náhľad textov piesní",
"Reset": "Resetovať",
"ReplaceTrickplayImages": "Nahradiť existujúce obrázky trickplay",
@ -1952,5 +1952,38 @@
"LabelFallbackMaxStreamingBitrate": "Záložný maximálny dátový tok (Mbps)",
"LabelSaveTrickplayLocally": "Uložit obrázky trickplay k médiám",
"LabelDelimiterWhitelistHelp": "Položky, ktoré sa majú vylúčiť z oddelovania tagov. Jedna položka na riadok.",
"LabelNoChangelog": "Pre túto verziu nie je k dispozícii žiadny zoznam zmien."
"LabelNoChangelog": "Pre túto verziu nie je k dispozícii žiadny zoznam zmien.",
"HeaderNextItemPlayingInValue": "Nasleduje {0} Prehrávanie za {1}",
"MessageSplitVersionsError": "Pri rozdeľovaní verzií došlo k chybe",
"PasswordMissingSaveError": "Nové heslo nesmie byť prázdne.",
"PluginLoadConfigError": "Pri získavaní konfigurácie zásuvného modulu došlo k chybe.",
"UseCustomTagDelimiters": "Použiť vlastný oddeľovač tagov",
"UseCustomTagDelimitersHelp": "Rozdeliť tagy umelcov/žánrov pomocou vlastných znakov.",
"LabelTrickplayKeyFrameOnlyExtraction": "Generovať obrázky len z kľúčových snímkov",
"MessageCancelSeriesTimerError": "Pri rušení časovača seriálu došlo k chybe",
"MessageCancelTimerError": "Pri rušení časovača došlo k chybe",
"PluginDisableError": "Pri zakazovaní zásuvného modulu došlo k chybe.",
"PluginEnableError": "Pri povoľovaní zásuvného modulu došlo k chybe.",
"PluginUninstallError": "Pri odinštalovaní zásuvného modulu došlo k chybe.",
"PreferNonstandardArtistsTag": "Preferovať tag ARTISTS, ak je k dispozícii",
"RenderPgsSubtitle": "Experimentálne vykresľovanie titulkov formátu PGS",
"VideoCodecTagNotSupported": "Tag video kodeku nie je podporovaný",
"LabelScreensaverTimeHelp": "Čas v sekundách nečinnosti potrebný na spustenie šetriča obrazovky.",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Vpálenie všetkých titulkov pri prekódovaní. Tým sa zabezpečí synchronizácia titulkov po prekódovaní za cenu zníženia rýchlosti prekódovania.",
"AlwaysBurnInSubtitleWhenTranscoding": "Pri prekódovaní vždy vpáliť titulky do videa",
"LabelScreensaverTime": "Čas šetriča obrazovky",
"LabelQsvDevice": "QSV zariadenie",
"LabelQsvDeviceHelp": "Špecifikujte zariadenie pre Intel QSV v systéme s viacerými grafickými procesormi. V Linuxe je to renderovací uzol, napr. /dev/dri/renderD128. Vo Windowse je to index zariadenia začínajúci od 0. Nechajte prázdne, ak neviete, čo robíte.",
"HeaderMediaSegmentActions": "Akcie segmentu médií",
"LabelMediaSegmentsType": "{0} segmenty",
"MediaSegmentAction.None": "Žiadne",
"MediaSegmentAction.Skip": "Preskočiť",
"MediaSegmentType.Commercial": "Komerčné",
"MediaSegmentType.Intro": "Úvod",
"MediaSegmentType.Outro": "Záverečné titulky",
"MediaSegmentType.Preview": "Ukážka",
"MediaSegmentType.Recap": "Rekapitulácia",
"PlaylistError.UpdateFailed": "Chyba pri aktualizácii playlistu",
"HeaderEditPlaylist": "Upraviť playlist",
"HeaderNewPlaylist": "Nový playlist"
}

View file

@ -1774,7 +1774,7 @@
"BackdropScreensaver": "Заставка «Задники»",
"LogoScreensaver": "Заставка з логотипом",
"LabelIsHearingImpaired": "Для людей з вадами слуху (SDH)",
"SelectAudioNormalizationHelp": "Посилення треку - регулює гучність кожного треку так, щоб вони відтворювалися з однаковою гучністю. Посилення альбому - регулює гучність лише всіх треків в альбомі, зберігаючи динамічний діапазон альбому.",
"SelectAudioNormalizationHelp": "Посилення треку - регулює гучність кожного треку так, щоб вони відтворювалися з однаковою гучністю. Посилення альбому - регулює гучність лише всіх треків в альбомі, зберігаючи динамічний діапазон альбому. Перемикання між «Вимкнено» та іншими опціями вимагає перезапуску поточного відтворення.",
"LabelAlbumGain": "Посилення альбому",
"LabelSelectAudioNormalization": "Нормалізація звуку",
"LabelTrackGain": "Посилення треку",

View file

@ -109,7 +109,7 @@
"ButtonAddScheduledTaskTrigger": "Thêm kích hoạt",
"ButtonAddMediaLibrary": "Thêm Thư Viện Phương Tiện",
"ButtonAddImage": "Thêm hình ảnh",
"BurnSubtitlesHelp": "Xác định xem máy chủ có nên ghi phụ đề khi chuyển mã video hay không. Tránh việc này sẽ cải thiện đáng kể hiệu suất. Chọn Tự động để ghi các định dạng dựa trên hình ảnh (VobSub, PGS, SUB, IDX, v.v.) và phụ đề ASS hoặc SSA nhất định.",
"BurnSubtitlesHelp": "Xác định xem máy chủ có nên ghi phụ đề hay không. Tránh việc này sẽ cải thiện đáng kể hiệu suất. Chọn Tự động để ghi các định dạng dựa trên hình ảnh (VobSub, PGS, SUB, IDX, v.v.) và phụ đề ASS hoặc SSA nhất định.",
"Browse": "Duyệt",
"BoxRear": "Hộp (mặt sau)",
"Books": "Sách",
@ -1484,7 +1484,7 @@
"LabelUDPPortRange": "Phạm Vi Giao Tiếp UDP",
"LabelSSDPTracingFilterHelp": "Địa chỉ IP tùy chọn để lọc lưu lượng SSDP đã ghi nhật ký.",
"LabelSSDPTracingFilter": "Bộ Lọc SSDP",
"LabelPublishedServerUriHelp": "Ghi đè URI được Jellyfin dùng, dựa trên giao diện hoặc địa chỉ IP của máy khách.",
"LabelPublishedServerUriHelp": "Ghi đè URI được Jellyfin dùng, dựa trên giao diện hoặc địa chỉ IP của máy khách. Ví dụ: internal=http://jellyfin.example.com, external=https://jellyfin.example.com, hoặc all=https://jellyfin.example.com",
"LabelPublishedServerUri": "URI Máy Chủ Đã Công Bố",
"LabelHDHomerunPortRangeHelp": "Giới hạn phạm vi cổng HDHomeRun UDP ở giá trị này. (Mặc định là 1024 - 65535).",
"LabelHDHomerunPortRange": "Phạm vi cổng HDHomeRun",
@ -1764,7 +1764,7 @@
"LabelTrackGain": "Điều Chỉnh Nhạc",
"GridView": "Xem Lưới",
"ListView": "Xem Danh Sách",
"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.",
"SelectAudioNormalizationHelp": "Chỉnh nhạc - điều chỉnh âm lượng mỗi bản nhạc để chúng phát với cùng một độ to. 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. Việc chuyển đổi giữa \"Tắt\" và các tùy chọn khác yêu cầu khởi động lại quá trình phát lại hiện tại.",
"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 sau khi chúng được khách hàng tải xuống.. Chỉ hoạt động nếu tính năng xóa phân đoạn được bật.",
"MenuClose": "Đóng Menu",
@ -1796,8 +1796,8 @@
"Lyric": "Lời bài hát",
"HeaderLyricDownloads": "Tải Xuống Lời Bài Hát",
"LabelSelectPreferredTranscodeVideoAudioCodec": "Bộ giải mã âm thanh chuyển mã ưa thích khi phát lại video",
"LibraryScanFanoutConcurrency": "Giới hạn nhiệm vụ quét thư viện song song",
"LibraryScanFanoutConcurrencyHelp": "Số lượng tác vụ song song tối đa trong quá trình quét thư viện. Đặt giá trị này thành 0 sẽ chọn một giới hạn dựa trên số lõi của hệ thống của bạn. CẢNH BÁO: Đặt số này quá cao có thể gây ra vấn đề với hệ thống tệp mạng; nếu gặp sự cố, hãy giảm số này.",
"LibraryScanFanoutConcurrency": "Giới hạn tác vụ quét thư viện song song",
"LibraryScanFanoutConcurrencyHelp": "Số lượng tác vụ song song tối đa trong quá trình quét thư viện. Đặt là 0 sẽ chọn một giới hạn dựa trên số lõi trên hệ thống của bạn. CẢNH BÁO: Đặt số này quá cao có thể gây ra vấn đề với hệ thống tệp mạng; nếu gặp sự cố, hãy giảm số này xuống thấp.",
"Lyrics": "Lời bài hát",
"PlaybackError.ASS_RENDER_ERROR": "Đã xảy ra lỗi trong trình kết xuất phụ đề ASS/SSA.",
"PlaybackError.FATAL_HLS_ERROR": "Đã xảy ra lỗi nghiêm trọng trong luồng HLS.",
@ -1964,5 +1964,9 @@
"DateModified": "Ngày sửa đổi",
"MessageCancelSeriesTimerError": "Đã xảy ra lỗi khi hủy bộ hẹn giờ chuỗi",
"MessageCancelTimerError": "Đã xảy ra lỗi khi hủy bộ hẹn giờ",
"MessageSplitVersionsError": "Đã xảy ra lỗi khi chia nhỏ các phiên bản"
"MessageSplitVersionsError": "Đã xảy ra lỗi khi chia nhỏ các phiên bản",
"AlwaysBurnInSubtitleWhenTranscoding": "Luôn ghi phụ đề khi chuyển mã",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "Ghi tất cả phụ đề khi chuyển mã được kích hoạt. Điều này đảm bảo đồng bộ hóa phụ đề sau khi chuyển mã nhưng phải trả giá bằng việc giảm tốc độ chuyển mã.",
"LabelScreensaverTime": "Thời Gian Bảo Vệ Màn Hình",
"LabelScreensaverTimeHelp": "Lượng thời gian tính bằng giây không hoạt động cần thiết để khởi động trình bảo vệ màn hình."
}

View file

@ -45,7 +45,7 @@
"Books": "书籍",
"Browse": "浏览",
"MessageBrowsePluginCatalog": "浏览我们的插件目录来查看现有插件。",
"BurnSubtitlesHelp": "确定服务器是否应该在转码视频时刻录字幕。 避免这种情况将大大提高性能。 选择自动刻录基于图像的格式VobSub、PGS、SUB、IDX 等)和某些 ASS 或 SSA 字幕。",
"BurnSubtitlesHelp": "确定服务器是否应该烧录字幕。 避免这种情况将大大提高性能。 选择自动烧录基于图像的格式VobSub、PGS、SUB、IDX 等)和某些 ASS 或 SSA 字幕。",
"ButtonAddMediaLibrary": "添加媒体库",
"ButtonAddScheduledTaskTrigger": "添加触发器",
"ButtonAddServer": "添加服务器",
@ -197,7 +197,7 @@
"GuestStar": "特邀明星",
"GuideProviderLogin": "登入",
"GuideProviderSelectListings": "选择列表",
"H264CrfHelp": "恒定速率因子 (CRF) 是 x264 软件编码的默认质量设置。您可以设置介于0和51之间的值 其中较低的值将导致更好的质量 (以更高的文件大小为代价)。正常值介于 18 和 28 之间。x264 的默认值为 23x265 的默认值为 28因此可以将其用作起始点。硬件编码器不使用这些设置。",
"H264CrfHelp": "恒定速率因子 (CRF) 是 x264 和x265 软件编码的默认质量设置。您可以设置介于0和51之间的值 较低的数值会带来更高的画质,但同时也会导致文件大小的增加。正常值介于 18 和 28 之间。x264 的默认值为 23x265 的默认值为 28因此可以用它作为初始参考值。硬件编码不使用这些设置。",
"EncoderPresetHelp": "选择较快的值以提高性能,或选择较慢的值以提高质量。",
"HDPrograms": "高清节目",
"HardwareAccelerationWarning": "启动硬件加速可能在某些环境下导致系统不稳定。请确认你的操作系统和显卡驱动程序是最新的。如果你在开启此项后播放视频时遇到困难,那么你需要将此选项设置回“没有”。",
@ -307,7 +307,7 @@
"HeaderPlaybackError": "播放错误",
"HeaderPleaseSignIn": "请登录",
"HeaderPluginInstallation": "插件安装",
"HeaderPreferredMetadataLanguage": "首选元数据语言",
"HeaderPreferredMetadataLanguage": "首选元数据语言",
"HeaderProfileInformation": "配置信息",
"HeaderProfileServerSettingsHelp": "这些参数将控制服务器如何将自己呈现给客户端。",
"HeaderRecentlyPlayed": "最近播放",
@ -353,7 +353,7 @@
"HeaderTranscodingProfileHelp": "添加转码配置文件标明哪些媒体格式需要转码处理。",
"HeaderTunerDevices": "调谐器设备",
"HeaderTuners": "调谐器",
"HeaderTypeImageFetchers": "图片获取程序 ({0})",
"HeaderTypeImageFetchers": "图片获取 ({0})",
"HeaderTypeText": "输入文本",
"HeaderUpcomingOnTV": "即将在电视上播放",
"HeaderUploadImage": "上传图片",
@ -401,7 +401,7 @@
"LabelAppNameExample": "用于识别 API 密钥的可读名称。此设置不影响功能。",
"LabelArtists": "艺术家",
"LabelArtistsHelp": "将多个艺术家用分号分隔。",
"LabelAudioLanguagePreference": "首选音频语言",
"LabelAudioLanguagePreference": "首选音频语言",
"LabelAutomaticallyAddToCollection": "自动添加到合集",
"LabelAutomaticallyAddToCollectionHelp": "至少有2个电影属于相同的合集时它们将会自动添加到合集中.",
"LabelAutomaticallyRefreshInternetMetadataEvery": "自动从互联网获取元数据并刷新",
@ -507,7 +507,7 @@
"LabelIconMaxHeight": "图标最大高度",
"LabelIconMaxWidth": "图标最大宽度",
"LabelIdentificationFieldHelp": "不区分大小写的字符串或正则表达式。",
"LabelImageFetchersHelp": "启用你首选的图片获取程序的优先级排序。",
"LabelImageFetchersHelp": "启用您首选的图片获取器并按优先级对其进行排序。",
"LabelImageType": "图片类型",
"LabelImportOnlyFavoriteChannels": "限制标记频道为我的最爱",
"LabelInNetworkSignInWithEasyPassword": "启用简单PIN码登录家庭网络",
@ -547,7 +547,7 @@
"LabelMessageText": "消息文本",
"LabelMessageTitle": "消息标题",
"LabelMetadata": "元数据",
"LabelMetadataDownloadLanguage": "首选下载语言",
"LabelMetadataDownloadLanguage": "首选下载语言",
"LabelMetadataDownloadersHelp": "启用媒体资料下载器的优先级排序,低优先级的下载器只会用来填补缺少的信息。",
"LabelMetadataPath": "元数据路径",
"LabelMetadataPathHelp": "为下载的图像和元数据指定自定义路径。",
@ -601,7 +601,7 @@
"LabelPostProcessor": "后处理应用程序",
"LabelPostProcessorArguments": "后处理程序命令行参数",
"LabelPostProcessorArgumentsHelp": "使用 {path} 作为录制文件的路径。",
"LabelPreferredDisplayLanguage": "首选显示语言",
"LabelPreferredDisplayLanguage": "首选显示语言",
"LabelPreferredSubtitleLanguage": "字幕语言偏好",
"LabelProfileAudioCodecs": "音频编解码器",
"LabelProfileCodecs": "编解码器",
@ -1022,7 +1022,7 @@
"Studios": "工作室",
"SubtitleAppearanceSettingsAlsoPassedToCastDevices": "这些设置也会被应用于任何通过此设备发起的 Google Cast 播放。",
"SubtitleAppearanceSettingsDisclaimer": "以下设置不适用于上述图形字幕或嵌入其自身样式的 ASS/SSA 字幕。",
"SubtitleDownloadersHelp": "按优先顺序启用并排列您的首选字幕下载程序。",
"SubtitleDownloadersHelp": "启用并按优先级顺序排列您首选的字幕下载器。",
"Subtitles": "字幕",
"Suggestions": "建议",
"Sunday": "星期天",
@ -1467,7 +1467,7 @@
"LabelUDPPortRangeHelp": "进行UDP连接时限制 Jellyfin 使用此端口范围。(默认值为 1024 - 65535。<br/> 注意:某些功能需要固定端口,这些端口可能不在此范围内。",
"LabelUDPPortRange": "UDP 通信范围",
"LabelSSDPTracingFilterHelp": "筛选记录的 SSDP 流量所依据的可选 IP 地址。",
"LabelPublishedServerUriHelp": "根据接口或客户端 IP 地址覆盖 Jellyfin 使用的 URI。",
"LabelPublishedServerUriHelp": "根据接口或客户端 IP 地址覆盖 Jellyfin 使用的 URI。例如internal=http://jellyfin.example.com、external=https://jellyfin.example.com 或 all=https://jellyfin.example.com",
"LabelIsForced": "强制的",
"LabelHDHomerunPortRangeHelp": "将 HD Homerun 的 UDP 端口范围限制为该值。(默认值为 1024 - 65535。",
"LabelHDHomerunPortRange": "HDHomeRun 端口范围",
@ -1566,7 +1566,7 @@
"LabelSyncPlaySettingsSyncCorrection": "同步校正",
"LabelSyncPlaySettingsExtraTimeOffsetHelp": "使用选定的设备手动调整时间偏移(以毫秒为单位)以进行时间同步。 小心调整。",
"LabelSyncPlaySettingsExtraTimeOffset": "额外时间偏移",
"LabelSyncPlaySettingsDescription": "更改SyncPlay首选项",
"LabelSyncPlaySettingsDescription": "更改 SyncPlay 首选项",
"HeaderSyncPlayTimeSyncSettings": "时间同步",
"HeaderSyncPlayPlaybackSettings": "回放",
"HeaderSyncPlaySettings": "SyncPlay设置",
@ -1756,11 +1756,11 @@
"LogLevel.Critical": "严重",
"LogLevel.None": "无",
"LabelThrottleDelaySecondsHelp": "当转码进度快于客户端下载进度的距离大于这个时间(以秒为单位),暂停转码以限制转码速度。必须足够长,以便客户端能够保持有效的缓冲时长。仅在启用了限制转码速度功能时生效。",
"AllowSegmentDeletion": "删除段",
"AllowSegmentDeletionHelp": "自动删除已经被客户端下载过的旧视频段。这样可以避免在磁盘上存储整个转码文件。如果出现播放问题,请关闭此选项。",
"AllowSegmentDeletion": "删除段",
"AllowSegmentDeletionHelp": "自动删除已经被客户端下载过的旧视频段。这样可以避免在磁盘上存储整个转码文件。如果出现播放问题,请关闭此选项。",
"LabelThrottleDelaySeconds": "限制转码速度阈值",
"LabelSegmentKeepSeconds": "缓存段时长",
"LabelSegmentKeepSecondsHelp": "已经被客户端下载过的视频片段应该在服务器上保留的时间(以秒为单位)。仅在启用片段删除时生效。",
"LabelSegmentKeepSeconds": "缓存段时长",
"LabelSegmentKeepSecondsHelp": "已经被客户端下载过的视频分段应该在服务器上保留的时间(以秒为单位)。仅在启用分段删除时生效。",
"HeaderEpisodesStatus": "剧集状态",
"LabelBackdropScreensaverInterval": "屏幕保护程序间隔",
"LabelBackdropScreensaverIntervalHelp": "不同屏幕保护切换的时间间隔秒数。",
@ -1893,13 +1893,13 @@
"Translator": "译者",
"LibraryScanFanoutConcurrency": "并行媒体库扫描任务限制",
"LibraryScanFanoutConcurrencyHelp": "媒体库扫描期间并行任务的最大数量。将其设置为 0 将根据您的系统核心数量选择限制。警告:将此数字设置得太高可能会导致网络文件系统出现问题; 如果您遇到问题,请降低此数字。",
"LabelSelectPreferredTranscodeVideoAudioCodec": "视频播放中首选的转码音频编解码器",
"SelectPreferredTranscodeVideoAudioCodecHelp": "选择视频内容转码到的首选音频编解码器。如果首选编解码器不受支持,服务器将使用下一个最佳可用编解码器。",
"LabelSelectPreferredTranscodeVideoAudioCodec": "转码播放视频时首选的音频编码",
"SelectPreferredTranscodeVideoAudioCodecHelp": "选择转码播放视频时首选的音频编码方式。如果首选的音频编码不被支持,服务器将使用下一个最佳的音频编码。",
"Regional": "区域",
"AlternateDVD": "替换DVD",
"Alternate": "替换",
"LabelSelectPreferredTranscodeVideoCodec": "首选转码视频编解码器",
"SelectPreferredTranscodeVideoCodecHelp": "选择要转码到的首选视频编解码器。如果不支持首选编解码器,服务器将使用下一个最佳可用编解码器。",
"LabelSelectPreferredTranscodeVideoCodec": "转码播放视频时首选的视频编码",
"SelectPreferredTranscodeVideoCodecHelp": "选择转码播放时首选的视频编码方式。如果首选的视频编码不被支持,服务器将使用下一个最佳的视频编码。",
"HeaderNextItem": "下 {0} 页",
"LabelEnablePlugin": "启用插件",
"LabelInstalled": "已安装",
@ -1961,11 +1961,29 @@
"LabelDelimiterWhitelist": "分隔符白名单",
"UseCustomTagDelimiters": "使用自定义标签分隔符",
"LabelDelimiterWhitelistHelp": "从标签分隔中排除的项目。每行一项。",
"PreferNonstandardArtistsTag": "可用时首选的艺术家标签",
"PreferNonstandardArtistsTag": "可用时首选 ARTISTS 标签",
"PreferNonstandardArtistsTagHelp": "如果可用,使用非标准的艺术家标签代替艺术家标签。",
"UseCustomTagDelimitersHelp": "使用自定义字符分割\"艺术家/类型\"标签。",
"MessageCancelSeriesTimerError": "取消节目计时器时发生错误",
"MessageCancelTimerError": "取消计时器时发生错误",
"MessageSplitVersionsError": "分割版本时发生错误",
"DateModified": "修改日期"
"DateModified": "修改日期",
"AlwaysBurnInSubtitleWhenTranscoding": "转码时总是烧录字幕",
"AlwaysBurnInSubtitleWhenTranscodingHelp": "当转码触发时烧录所有字幕。 这样可以保证转码后的字幕同步,但转码速度会降低。",
"LabelScreensaverTime": "屏幕保护时间",
"LabelScreensaverTimeHelp": "启动屏幕保护程序所需的无活动时间(以秒为单位)。",
"LabelQsvDeviceHelp": "为多 GPU 系统上的 Intel QSV 指定设备。在 Linux 上,这是渲染节点,例如 /dev/dri/renderD128。在 Windows 上,这是从 0 开始的设备序号。除非您知道自己在做什么,否则请留空。",
"LabelQsvDevice": "QSV 设备",
"HeaderEditPlaylist": "编辑播放列表",
"LabelMediaSegmentsType": "{0}分段",
"HeaderMediaSegmentActions": "媒体分段操作",
"MediaSegmentAction.None": "无",
"MediaSegmentAction.Skip": "跳过",
"MediaSegmentType.Commercial": "广告",
"MediaSegmentType.Intro": "片头",
"MediaSegmentType.Outro": "片尾",
"MediaSegmentType.Preview": "预告",
"MediaSegmentType.Recap": "回顾",
"HeaderNewPlaylist": "新建播放列表",
"PlaylistError.UpdateFailed": "更新播放列表时出错"
}

View file

@ -56,7 +56,7 @@
@media all and (min-width: 50em) {
.editPageSidebar {
position: fixed;
top: 5.2em;
top: 3.25rem;
bottom: 0;
width: 30%;
display: block;

View file

@ -1,6 +1,24 @@
import { describe, expect, it } from 'vitest';
import { toBoolean, toFloat } from './string';
import { isBlank, toBoolean, toFloat } from './string';
describe('isBlank', () => {
it('Should return true if the string is blank', () => {
let check = isBlank(undefined);
expect(check).toBe(true);
check = isBlank(null);
expect(check).toBe(true);
check = isBlank('');
expect(check).toBe(true);
check = isBlank(' \t\t ');
expect(check).toBe(true);
});
it('Should return false if the string is not blank', () => {
const check = isBlank('not an empty string');
expect(check).toBe(false);
});
});
describe('toBoolean', () => {
it('Should return the boolean represented by the string', () => {

View file

@ -1,3 +1,12 @@
/**
* Checks if a string is empty or contains only whitespace.
* @param {string} value The string to test.
* @returns {boolean} True if the string is blank.
*/
export function isBlank(value: string | undefined | null) {
return !value?.trim().length;
}
/**
* Gets the value of a string as boolean.
* @param {string} name The value as a string.