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

Integrate branch 'master' into feature/osd_chapter_selection

This commit is contained in:
TheMelmacian 2024-05-19 14:07:39 +02:00
commit 572de64bc7
124 changed files with 6080 additions and 3222 deletions

View file

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.repository == 'jellyfin/jellyfin-web' }} if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
steps: steps:
- uses: eps1lon/actions-label-merge-conflict@e62d7a53ff8be8b97684bffb6cfbbf3fc1115e2e # v3.0.0 - uses: eps1lon/actions-label-merge-conflict@6d74047dcef155976a15e4a124dde2c7fe0c5522 # v3.0.1
with: with:
dirtyLabel: 'merge conflict' dirtyLabel: 'merge conflict'
commentOnDirty: 'This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.' commentOnDirty: 'This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.'

View file

@ -18,7 +18,7 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup node environment - name: Setup node environment
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
@ -57,9 +57,11 @@ jobs:
steps: steps:
- name: Save PR context - name: Save PR context
env: env:
PR_BRANCH: ${{ github.ref_name }}
PR_NUMBER: ${{ github.event.number }} PR_NUMBER: ${{ github.event.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }} PR_SHA: ${{ github.event.pull_request.head.sha }}
run: | run: |
echo $PR_BRANCH > PR_branch
echo $PR_NUMBER > PR_number echo $PR_NUMBER > PR_number
echo $PR_SHA > PR_sha echo $PR_SHA > PR_sha
@ -68,5 +70,6 @@ jobs:
with: with:
name: PR_context name: PR_context
path: | path: |
PR_branch
PR_number PR_number
PR_sha PR_sha

View file

@ -19,16 +19,16 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 uses: github/codeql-action/init@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5
with: with:
languages: javascript languages: javascript
queries: +security-extended queries: +security-extended
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 uses: github/codeql-action/autobuild@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 uses: github/codeql-action/analyze@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5

View file

@ -18,7 +18,7 @@ jobs:
comment-id: ${{ github.event.comment.id }} comment-id: ${{ github.event.comment.id }}
reactions: '+1' reactions: '+1'
- name: Checkout the latest code - name: Checkout the latest code
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with: with:
token: ${{ secrets.JF_BOT_TOKEN }} token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0 fetch-depth: 0

View file

@ -17,7 +17,7 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
@ -33,6 +33,6 @@ jobs:
- name: Run eslint - name: Run eslint
if: ${{ github.repository == 'jellyfin/jellyfin-web' }} if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
uses: CatChen/eslint-suggestion-action@34e2a6c4193eba18a7a20710b5ae37850fc984c3 # v3.1.5 uses: CatChen/eslint-suggestion-action@b110ac684564c7b73e47cc223eb7a5266ec83fd3 # v4.1.1
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -8,13 +8,40 @@ on:
- completed - completed
jobs: jobs:
pr-context:
name: PR context
if: ${{ github.event.workflow_run.event == 'pull_request' }}
runs-on: ubuntu-latest
outputs:
branch: ${{ env.pr_branch }}
commit: ${{ env.pr_sha }}
pr_number: ${{ env.pr_number }}
steps:
- name: Get PR context
uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
id: pr_context
with:
run_id: ${{ github.event.workflow_run.id }}
name: PR_context
- name: Set PR context environment variables
if: ${{ steps.pr_context.conclusion == 'success' }}
run: |
echo "pr_branch=$(cat PR_branch)" >> $GITHUB_ENV
echo "pr_number=$(cat PR_number)" >> $GITHUB_ENV
echo "pr_sha=$(cat PR_sha)" >> $GITHUB_ENV
publish: publish:
permissions: permissions:
contents: read contents: read
deployments: write deployments: write
name: Deploy to Cloudflare Pages name: Deploy to Cloudflare Pages
if: ${{ always() }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
- pr-context
outputs: outputs:
url: ${{ steps.cf.outputs.url }} url: ${{ steps.cf.outputs.url }}
@ -33,32 +60,10 @@ jobs:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: jellyfin-web projectName: jellyfin-web
branch: ${{ github.event.workflow_run.head_branch }} branch: ${{ needs.pr-context.outputs.branch || github.ref_name }}
directory: dist directory: dist
gitHubToken: ${{ secrets.GITHUB_TOKEN }} gitHubToken: ${{ secrets.GITHUB_TOKEN }}
pr-context:
name: PR context
if: ${{ always() && github.event.workflow_run.event == 'pull_request' }}
runs-on: ubuntu-latest
outputs:
commit: ${{ env.pr_sha }}
pr_number: ${{ env.pr_number }}
steps:
- name: Get PR context
uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
id: pr_context
with:
run_id: ${{ github.event.workflow_run.id }}
name: PR_context
- name: Set PR context environment variables
if: ${{ steps.pr_context.conclusion == 'success' }}
run: |
echo "pr_number=$(cat PR_number)" >> $GITHUB_ENV
echo "pr_sha=$(cat PR_sha)" >> $GITHUB_ENV
compose-comment: compose-comment:
name: Compose comment name: Compose comment
if: ${{ always() }} if: ${{ always() }}
@ -68,7 +73,7 @@ jobs:
- pr-context - pr-context
with: with:
branch: ${{ github.event.workflow_run.head_branch }} branch: ${{ needs.pr-context.outputs.branch || github.ref_name }}
commit: ${{ needs.pr-context.outputs.commit != '' && needs.pr-context.outputs.commit || github.event.workflow_run.head_sha }} commit: ${{ needs.pr-context.outputs.commit != '' && needs.pr-context.outputs.commit || github.event.workflow_run.head_sha }}
preview_url: ${{ needs.publish.outputs.url }} preview_url: ${{ needs.publish.outputs.url }}
build_workflow_run_id: ${{ github.event.workflow_run.id }} build_workflow_run_id: ${{ github.event.workflow_run.id }}

View file

@ -17,7 +17,7 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup node environment - name: Setup node environment
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
@ -41,7 +41,7 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup node environment - name: Setup node environment
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
@ -62,7 +62,7 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup node environment - name: Setup node environment
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
@ -86,7 +86,7 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup node environment - name: Setup node environment
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
@ -107,7 +107,7 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup node environment - name: Setup node environment
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2

View file

@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with: with:
ref: master ref: master
token: ${{ secrets.JF_BOT_TOKEN }} token: ${{ secrets.JF_BOT_TOKEN }}

View file

@ -86,6 +86,7 @@
- [GeorgeH005](https://github.com/GeorgeH005) - [GeorgeH005](https://github.com/GeorgeH005)
- [JPUC1143](https://github.com/Jpuc1143) - [JPUC1143](https://github.com/Jpuc1143)
- [David Angel](https://github.com/davidangel) - [David Angel](https://github.com/davidangel)
- [Pithaya](https://github.com/Pithaya)
## Emby Contributors ## Emby Contributors

5035
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,21 @@
{ {
"name": "jellyfin-web", "name": "jellyfin-web",
"version": "10.9.0", "version": "10.10.0",
"description": "Web interface for Jellyfin", "description": "Web interface for Jellyfin",
"repository": "https://github.com/jellyfin/jellyfin-web", "repository": "https://github.com/jellyfin/jellyfin-web",
"license": "GPL-2.0-or-later", "license": "GPL-2.0-or-later",
"devDependencies": { "devDependencies": {
"@babel/core": "7.24.3", "@babel/core": "7.24.5",
"@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-class-properties": "7.18.6",
"@babel/plugin-proposal-private-methods": "7.18.6", "@babel/plugin-proposal-private-methods": "7.18.6",
"@babel/plugin-transform-modules-umd": "7.24.1", "@babel/plugin-transform-modules-umd": "7.24.1",
"@babel/preset-env": "7.24.3", "@babel/preset-env": "7.24.5",
"@babel/preset-react": "7.24.1", "@babel/preset-react": "7.24.1",
"@types/escape-html": "1.0.4", "@types/escape-html": "1.0.4",
"@types/loadable__component": "5.13.9", "@types/loadable__component": "5.13.9",
"@types/lodash-es": "4.17.12", "@types/lodash-es": "4.17.12",
"@types/markdown-it": "13.0.7", "@types/markdown-it": "14.1.1",
"@types/react": "17.0.79", "@types/react": "17.0.80",
"@types/react-dom": "17.0.25", "@types/react-dom": "17.0.25",
"@types/sortablejs": "1.15.8", "@types/sortablejs": "1.15.8",
"@typescript-eslint/eslint-plugin": "5.62.0", "@typescript-eslint/eslint-plugin": "5.62.0",
@ -28,8 +28,8 @@
"confusing-browser-globals": "1.0.11", "confusing-browser-globals": "1.0.11",
"copy-webpack-plugin": "12.0.2", "copy-webpack-plugin": "12.0.2",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"css-loader": "6.10.0", "css-loader": "7.1.1",
"cssnano": "6.1.2", "cssnano": "7.0.1",
"es-check": "7.1.1", "es-check": "7.1.1",
"eslint": "8.57.0", "eslint": "8.57.0",
"eslint-plugin-compat": "4.2.0", "eslint-plugin-compat": "4.2.0",
@ -37,62 +37,62 @@
"eslint-plugin-import": "2.29.1", "eslint-plugin-import": "2.29.1",
"eslint-plugin-jsx-a11y": "6.8.0", "eslint-plugin-jsx-a11y": "6.8.0",
"eslint-plugin-react": "7.34.1", "eslint-plugin-react": "7.34.1",
"eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-sonarjs": "0.24.0", "eslint-plugin-sonarjs": "0.25.1",
"expose-loader": "4.1.0", "expose-loader": "5.0.0",
"fork-ts-checker-webpack-plugin": "9.0.2", "fork-ts-checker-webpack-plugin": "9.0.2",
"html-loader": "4.2.0", "html-loader": "5.0.0",
"html-webpack-plugin": "5.6.0", "html-webpack-plugin": "5.6.0",
"jsdom": "23.2.0", "jsdom": "24.0.0",
"mini-css-extract-plugin": "2.8.1", "mini-css-extract-plugin": "2.9.0",
"postcss": "8.4.38", "postcss": "8.4.38",
"postcss-loader": "7.3.4", "postcss-loader": "8.1.1",
"postcss-preset-env": "9.5.2", "postcss-preset-env": "9.5.12",
"postcss-scss": "4.0.9", "postcss-scss": "4.0.9",
"sass": "1.72.0", "sass": "1.77.1",
"sass-loader": "13.3.3", "sass-loader": "14.2.1",
"source-map-loader": "4.0.2", "source-map-loader": "5.0.0",
"speed-measure-webpack-plugin": "1.5.0", "speed-measure-webpack-plugin": "1.5.0",
"style-loader": "3.3.4", "style-loader": "4.0.0",
"stylelint": "15.11.0", "stylelint": "15.11.0",
"stylelint-config-rational-order": "0.1.2", "stylelint-config-rational-order": "0.1.2",
"stylelint-no-browser-hacks": "1.3.0", "stylelint-no-browser-hacks": "1.3.0",
"stylelint-order": "6.0.4", "stylelint-order": "6.0.4",
"stylelint-scss": "5.3.2", "stylelint-scss": "5.3.2",
"ts-loader": "9.5.1", "ts-loader": "9.5.1",
"typescript": "5.4.3", "typescript": "5.4.5",
"vitest": "1.4.0", "vitest": "1.6.0",
"webpack": "5.91.0", "webpack": "5.91.0",
"webpack-bundle-analyzer": "4.10.1", "webpack-bundle-analyzer": "4.10.2",
"webpack-cli": "5.1.4", "webpack-cli": "5.1.4",
"webpack-dev-server": "4.15.2", "webpack-dev-server": "5.0.4",
"webpack-merge": "5.10.0", "webpack-merge": "5.10.0",
"worker-loader": "3.0.8" "worker-loader": "3.0.8"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "11.11.4", "@emotion/react": "11.11.4",
"@emotion/styled": "11.11.0", "@emotion/styled": "11.11.5",
"@fontsource/noto-sans": "5.0.21", "@fontsource/noto-sans": "5.0.22",
"@fontsource/noto-sans-hk": "5.0.18", "@fontsource/noto-sans-hk": "5.0.19",
"@fontsource/noto-sans-jp": "5.0.18", "@fontsource/noto-sans-jp": "5.0.19",
"@fontsource/noto-sans-kr": "5.0.18", "@fontsource/noto-sans-kr": "5.0.19",
"@fontsource/noto-sans-sc": "5.0.18", "@fontsource/noto-sans-sc": "5.0.19",
"@fontsource/noto-sans-tc": "5.0.18", "@fontsource/noto-sans-tc": "5.0.19",
"@jellyfin/libass-wasm": "4.2.1", "@jellyfin/libass-wasm": "4.2.1",
"@jellyfin/sdk": "0.0.0-unstable.202404101900", "@jellyfin/sdk": "0.0.0-unstable.202405190501",
"@loadable/component": "5.16.3", "@loadable/component": "5.16.4",
"@mui/icons-material": "5.15.11", "@mui/icons-material": "5.15.17",
"@mui/material": "5.15.11", "@mui/material": "5.15.17",
"@mui/x-data-grid": "6.19.5", "@mui/x-data-grid": "6.19.11",
"@react-hook/resize-observer": "1.2.6", "@react-hook/resize-observer": "1.2.6",
"@tanstack/react-query": "4.36.1", "@tanstack/react-query": "4.36.1",
"@tanstack/react-query-devtools": "4.36.1", "@tanstack/react-query-devtools": "4.36.1",
"@types/react-lazy-load-image-component": "1.6.3", "@types/react-lazy-load-image-component": "1.6.4",
"abortcontroller-polyfill": "1.7.5", "abortcontroller-polyfill": "1.7.5",
"blurhash": "2.0.5", "blurhash": "2.0.5",
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
"classnames": "2.5.1", "classnames": "2.5.1",
"core-js": "3.36.1", "core-js": "3.37.0",
"date-fns": "2.30.0", "date-fns": "2.30.0",
"dompurify": "3.0.1", "dompurify": "3.0.1",
"epubjs": "0.3.93", "epubjs": "0.3.93",
@ -101,7 +101,7 @@
"flv.js": "1.6.2", "flv.js": "1.6.2",
"headroom.js": "0.12.0", "headroom.js": "0.12.0",
"history": "5.3.0", "history": "5.3.0",
"hls.js": "1.5.7", "hls.js": "1.5.8",
"intersection-observer": "0.12.2", "intersection-observer": "0.12.2",
"jellyfin-apiclient": "1.11.0", "jellyfin-apiclient": "1.11.0",
"jquery": "3.7.1", "jquery": "3.7.1",
@ -116,12 +116,12 @@
"react-blurhash": "0.3.0", "react-blurhash": "0.3.0",
"react-dom": "17.0.2", "react-dom": "17.0.2",
"react-lazy-load-image-component": "1.6.0", "react-lazy-load-image-component": "1.6.0",
"react-router-dom": "6.22.3", "react-router-dom": "6.23.1",
"resize-observer-polyfill": "1.5.1", "resize-observer-polyfill": "1.5.1",
"screenfull": "6.0.2", "screenfull": "6.0.2",
"sortablejs": "1.15.2", "sortablejs": "1.15.2",
"swiper": "11.0.7", "swiper": "11.1.1",
"usehooks-ts": "2.16.0", "usehooks-ts": "3.1.0",
"webcomponents.js": "0.7.24", "webcomponents.js": "0.7.24",
"whatwg-fetch": "3.6.20" "whatwg-fetch": "3.6.20"
}, },

View file

@ -11,6 +11,7 @@ import ElevationScroll from 'components/ElevationScroll';
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer'; import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
import { useApi } from 'hooks/useApi'; import { useApi } from 'hooks/useApi';
import AppTabs from './components/AppTabs';
import AppDrawer from './components/drawer/AppDrawer'; import AppDrawer from './components/drawer/AppDrawer';
import './AppOverrides.scss'; import './AppOverrides.scss';
@ -55,7 +56,9 @@ const AppLayout: FC<AppLayoutProps> = ({
isDrawerAvailable={!isMediumScreen && isDrawerAvailable} isDrawerAvailable={!isMediumScreen && isDrawerAvailable}
isDrawerOpen={isDrawerOpen} isDrawerOpen={isDrawerOpen}
onDrawerButtonClick={onToggleDrawer} onDrawerButtonClick={onToggleDrawer}
/> >
<AppTabs isDrawerOpen={isDrawerOpen} />
</AppToolbar>
</AppBar> </AppBar>
</ElevationScroll> </ElevationScroll>

View file

@ -16,7 +16,15 @@ $mui-bp-xl: 1536px;
} }
// Fix the padding of dashboard pages // Fix the padding of dashboard pages
.content-primary.content-primary { .content-primary {
padding-top: 3.25rem !important; padding-top: 3.25rem;
}
// Tabbed pages
.withTabs .content-primary {
padding-top: 6.5rem;
@media all and (min-width: $mui-bp-lg) {
padding-top: 3.25rem;
}
} }
} }

View file

@ -0,0 +1,96 @@
import { Theme } from '@mui/material/styles';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import useMediaQuery from '@mui/material/useMediaQuery';
import debounce from 'lodash-es/debounce';
import isEqual from 'lodash-es/isEqual';
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import { EventType } from 'types/eventType';
import Events, { type Event } from 'utils/events';
interface AppTabsParams {
isDrawerOpen: boolean
}
interface TabDefinition {
href: string
name: string
}
const handleResize = debounce(() => window.dispatchEvent(new Event('resize')), 100);
const AppTabs: FC<AppTabsParams> = ({
isDrawerOpen
}) => {
const documentRef = useRef<Document>(document);
const [ activeIndex, setActiveIndex ] = useState(0);
const [ tabs, setTabs ] = useState<TabDefinition[]>();
const isBigScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('sm'));
const onTabsUpdate = useCallback((
_e: Event,
_newView?: string,
newIndex: number | undefined = 0,
newTabs?: TabDefinition[]
) => {
setActiveIndex(newIndex);
if (!isEqual(tabs, newTabs)) {
setTabs(newTabs);
}
}, [ tabs ]);
useEffect(() => {
const doc = documentRef.current;
if (doc) Events.on(doc, EventType.SET_TABS, onTabsUpdate);
return () => {
if (doc) Events.off(doc, EventType.SET_TABS, onTabsUpdate);
};
}, [ onTabsUpdate ]);
// HACK: Force resizing to workaround upstream bug with tab resizing
// https://github.com/mui/material-ui/issues/24011
useEffect(() => {
handleResize();
}, [ isDrawerOpen ]);
if (!tabs?.length) return null;
return (
<Tabs
value={activeIndex}
sx={{
width: '100%',
flexShrink: {
xs: 0,
lg: 'unset'
},
order: {
xs: 100,
lg: 'unset'
}
}}
variant={isBigScreen ? 'standard' : 'scrollable'}
centered={isBigScreen}
>
{
tabs.map(({ href, name }, index) => (
<Tab
key={`tab-${name}`}
label={name}
data-tab-index={`${index}`}
component={Link}
to={href}
/>
))
}
</Tabs>
);
};
export default AppTabs;

View file

@ -10,11 +10,6 @@ $mui-bp-xl: 1536px;
position: relative; position: relative;
} }
// Ensure the footer renders over the drawer
.appfooter {
z-index: 1201 !important; // mui drawer uses z-index 1200
}
// Hide some items from the user "settings" page that are in the drawer // Hide some items from the user "settings" page that are in the drawer
#myPreferencesMenuPage { #myPreferencesMenuPage {
.lnkQuickConnectPreferences, .lnkQuickConnectPreferences,

View file

@ -18,17 +18,30 @@ interface AppToolbarProps {
onDrawerButtonClick: (event: React.MouseEvent<HTMLElement>) => void onDrawerButtonClick: (event: React.MouseEvent<HTMLElement>) => void
} }
const PUBLIC_PATHS = [
'/addserver.html',
'/selectserver.html',
'/login.html',
'/forgotpassword.html',
'/forgotpasswordpin.html'
];
const ExperimentalAppToolbar: FC<AppToolbarProps> = ({ const ExperimentalAppToolbar: FC<AppToolbarProps> = ({
isDrawerAvailable, isDrawerAvailable,
isDrawerOpen, isDrawerOpen,
onDrawerButtonClick onDrawerButtonClick
}) => { }) => {
const location = useLocation(); const location = useLocation();
// The video osd does not show the standard toolbar
if (location.pathname === '/video') return null;
const isTabsAvailable = isTabPath(location.pathname); const isTabsAvailable = isTabPath(location.pathname);
const isPublicPath = PUBLIC_PATHS.includes(location.pathname);
return ( return (
<AppToolbar <AppToolbar
buttons={ buttons={!isPublicPath && (
<> <>
<SyncPlayButton /> <SyncPlayButton />
<RemotePlayButton /> <RemotePlayButton />
@ -45,10 +58,11 @@ const ExperimentalAppToolbar: FC<AppToolbarProps> = ({
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</> </>
} )}
isDrawerAvailable={isDrawerAvailable} isDrawerAvailable={isDrawerAvailable}
isDrawerOpen={isDrawerOpen} isDrawerOpen={isDrawerOpen}
onDrawerButtonClick={onDrawerButtonClick} onDrawerButtonClick={onDrawerButtonClick}
isUserMenuAvailable={!isPublicPath}
> >
{isTabsAvailable && (<AppTabs isDrawerOpen={isDrawerOpen} />)} {isTabsAvailable && (<AppTabs isDrawerOpen={isDrawerOpen} />)}
</AppToolbar> </AppToolbar>

View file

@ -6,7 +6,7 @@ import React, { type FC, useCallback } from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import classNames from 'classnames'; import classNames from 'classnames';
import { useLocalStorage } from 'hooks/useLocalStorage'; import { useLocalStorage } from 'hooks/useLocalStorage';
import { useGetItem, useGetItemsViewByType } from 'hooks/useFetchItems'; import { useGetItemsViewByType } from 'hooks/useFetchItems';
import { getDefaultLibraryViewSettings, getSettingsKey } from 'utils/items'; import { getDefaultLibraryViewSettings, getSettingsKey } from 'utils/items';
import { CardShape } from 'utils/card'; import { CardShape } from 'utils/card';
import Loading from 'components/loading/LoadingComponent'; import Loading from 'components/loading/LoadingComponent';
@ -28,6 +28,7 @@ import { LibraryTab } from 'types/libraryTab';
import { type LibraryViewSettings, type ParentId, ViewMode } from 'types/library'; import { type LibraryViewSettings, type ParentId, ViewMode } from 'types/library';
import type { CardOptions } from 'types/cardOptions'; import type { CardOptions } from 'types/cardOptions';
import type { ListOptions } from 'types/listOptions'; import type { ListOptions } from 'types/listOptions';
import { useItem } from 'hooks/useItem';
interface ItemsViewProps { interface ItemsViewProps {
viewType: LibraryTab; viewType: LibraryTab;
@ -79,7 +80,7 @@ const ItemsView: FC<ItemsViewProps> = ({
itemType, itemType,
libraryViewSettings libraryViewSettings
); );
const { data: item } = useGetItem(parentId); const { data: item } = useItem(parentId || undefined);
const getListOptions = useCallback(() => { const getListOptions = useCallback(() => {
const listOptions: ListOptions = { const listOptions: ListOptions = {

View file

@ -49,16 +49,6 @@ export const LEGACY_USER_ROUTES: LegacyRoute[] = [
controller: 'user/subtitles/index', controller: 'user/subtitles/index',
view: 'user/subtitles/index.html' view: 'user/subtitles/index.html'
} }
}, {
path: 'video',
pageProps: {
controller: 'playback/video/index',
view: 'playback/video/index.html',
type: 'video-osd',
isFullscreen: true,
isNowPlayingBarEnabled: false,
isThemeMediaSupported: true
}
}, { }, {
path: 'queue', path: 'queue',
pageProps: { pageProps: {

View file

@ -7,8 +7,10 @@ import { toAsyncPageRoute } from 'components/router/AsyncRoute';
import { toViewManagerPageRoute } from 'components/router/LegacyRoute'; import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
import { toRedirectRoute } from 'components/router/Redirect'; import { toRedirectRoute } from 'components/router/Redirect';
import AppLayout from '../AppLayout'; import AppLayout from '../AppLayout';
import { ASYNC_USER_ROUTES } from './asyncRoutes'; import { ASYNC_USER_ROUTES } from './asyncRoutes';
import { LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './legacyRoutes'; import { LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './legacyRoutes';
import VideoPage from './video';
export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [ export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [
{ {
@ -20,7 +22,13 @@ export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [
element: <ConnectionRequired isUserRequired />, element: <ConnectionRequired isUserRequired />,
children: [ children: [
...ASYNC_USER_ROUTES.map(toAsyncPageRoute), ...ASYNC_USER_ROUTES.map(toAsyncPageRoute),
...LEGACY_USER_ROUTES.map(toViewManagerPageRoute) ...LEGACY_USER_ROUTES.map(toViewManagerPageRoute),
// The video page is special since it combines new controls with the legacy view
{
path: 'video',
element: <VideoPage />
}
] ]
}, },

View file

@ -5,7 +5,6 @@ import React, { useCallback } from 'react';
import Page from 'components/Page'; import Page from 'components/Page';
import globalize from 'scripts/globalize'; import globalize from 'scripts/globalize';
import theme from 'themes/theme';
import { DisplayPreferences } from './DisplayPreferences'; import { DisplayPreferences } from './DisplayPreferences';
import { ItemDetailPreferences } from './ItemDetailPreferences'; import { ItemDetailPreferences } from './ItemDetailPreferences';
import { LibraryPreferences } from './LibraryPreferences'; import { LibraryPreferences } from './LibraryPreferences';
@ -80,11 +79,7 @@ export default function UserDisplayPreferences() {
<Button <Button
type='submit' type='submit'
sx={{ size='large'
color: theme.palette.text.primary,
fontSize: theme.typography.htmlFontSize,
fontWeight: theme.typography.fontWeightBold
}}
> >
{globalize.translate('Save')} {globalize.translate('Save')}
</Button> </Button>

View file

@ -0,0 +1,72 @@
import Box from '@mui/material/Box/Box';
import Fade from '@mui/material/Fade/Fade';
import React, { useRef, type FC, useEffect, useState } from 'react';
import RemotePlayButton from 'apps/experimental/components/AppToolbar/RemotePlayButton';
import SyncPlayButton from 'apps/experimental/components/AppToolbar/SyncPlayButton';
import AppToolbar from 'components/toolbar/AppToolbar';
import ViewManagerPage from 'components/viewManager/ViewManagerPage';
import { EventType } from 'types/eventType';
import Events, { type Event } from 'utils/events';
/**
* Video player page component that renders mui controls for the top controls and the legacy view for everything else.
*/
const VideoPage: FC = () => {
const documentRef = useRef<Document>(document);
const [ isVisible, setIsVisible ] = useState(true);
const onShowVideoOsd = (_e: Event, isShowing: boolean) => {
setIsVisible(isShowing);
};
useEffect(() => {
const doc = documentRef.current;
if (doc) Events.on(doc, EventType.SHOW_VIDEO_OSD, onShowVideoOsd);
return () => {
if (doc) Events.off(doc, EventType.SHOW_VIDEO_OSD, onShowVideoOsd);
};
}, []);
return (
<>
<Fade
in={isVisible}
easing='fade-out'
>
<Box sx={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
color: 'white'
}}>
<AppToolbar
isDrawerAvailable={false}
isDrawerOpen={false}
isUserMenuAvailable={false}
buttons={
<>
<SyncPlayButton />
<RemotePlayButton />
</>
}
/>
</Box>
</Fade>
<ViewManagerPage
controller='playback/video/index'
view='playback/video/index.html'
type='video-osd'
isFullscreen
isNowPlayingBarEnabled={false}
isThemeMediaSupported
/>
</>
);
};
export default VideoPage;

View file

@ -1,3 +1,4 @@
import { MINIMUM_VERSION } from '@jellyfin/sdk/lib/versions';
import { ConnectionManager, Credentials, ApiClient } from 'jellyfin-apiclient'; import { ConnectionManager, Credentials, ApiClient } from 'jellyfin-apiclient';
import { appHost } from './apphost'; import { appHost } from './apphost';
@ -33,8 +34,13 @@ class ServerConnections extends ConnectionManager {
super(...arguments); super(...arguments);
this.localApiClient = null; this.localApiClient = null;
// Set the apiclient minimum version to match the SDK
this._minServerVersion = MINIMUM_VERSION;
Events.on(this, 'localusersignedout', (_e, logoutInfo) => { Events.on(this, 'localusersignedout', (_e, logoutInfo) => {
setUserInfo(null, null); setUserInfo(null, null);
// Ensure the updated credentials are persisted to storage
credentialProvider.credentials(credentialProvider.credentials());
if (window.NativeShell && typeof window.NativeShell.onLocalUserSignedOut === 'function') { if (window.NativeShell && typeof window.NativeShell.onLocalUserSignedOut === 'function') {
window.NativeShell.onLocalUserSignedOut(logoutInfo); window.NativeShell.onLocalUserSignedOut(logoutInfo);
@ -128,12 +134,12 @@ class ServerConnections extends ConnectionManager {
} }
} }
const credentials = new Credentials(); const credentialProvider = new Credentials();
const capabilities = Dashboard.capabilities(appHost); const capabilities = Dashboard.capabilities(appHost);
export default new ServerConnections( export default new ServerConnections(
credentials, credentialProvider,
appHost.appName(), appHost.appName(),
appHost.appVersion(), appHost.appVersion(),
appHost.deviceName(), appHost.deviceName(),

View file

@ -2,7 +2,7 @@
position: fixed; position: fixed;
left: 0; left: 0;
right: 0; right: 0;
z-index: 10; z-index: 1201 !important; // mui drawer uses z-index 1200
bottom: 0; bottom: 0;
transition: transform 180ms linear; transition: transform 180ms linear;
contain: layout style; contain: layout style;

View file

@ -70,6 +70,21 @@ function getDeviceProfile(item) {
}); });
} }
const preferredTranscodeVideoAudioCodec = appSettings.preferredTranscodeVideoAudioCodec();
if (preferredTranscodeVideoAudioCodec) {
profile.TranscodingProfiles.forEach((transcodingProfile) => {
if (transcodingProfile.Type === 'Video') {
const audioCodecs = transcodingProfile.AudioCodec.split(',');
const index = audioCodecs.indexOf(preferredTranscodeVideoAudioCodec);
if (index !== -1) {
audioCodecs.splice(index, 1);
audioCodecs.unshift(preferredTranscodeVideoAudioCodec);
transcodingProfile.AudioCodec = audioCodecs.join(',');
}
}
});
}
resolve(profile); resolve(profile);
}); });
} }

View file

@ -4,6 +4,7 @@
* @module components/cardBuilder/cardBuilder * @module components/cardBuilder/cardBuilder
*/ */
import { PersonKind } from '@jellyfin/sdk/lib/generated-client/models/person-kind';
import escapeHtml from 'escape-html'; import escapeHtml from 'escape-html';
import browser from 'scripts/browser'; import browser from 'scripts/browser';
@ -698,8 +699,26 @@ function getCardFooterText(item, apiClient, options, footerClass, progressHtml,
} }
} }
if (options.showPersonRoleOrType && item.Role) { if (options.showPersonRoleOrType && item.Type) {
lines.push(globalize.translate('PersonRole', escapeHtml(item.Role))); if (item.Role) {
if ([ PersonKind.Actor, PersonKind.GuestStar ].includes(item.Type)) {
// List actor roles formatted like "as Character Name"
lines.push(globalize.translate('PersonRole', escapeHtml(item.Role)));
} else if (item.Role.toLowerCase() === item.Type.toLowerCase()) {
// Role and Type are the same so use the localized Type
lines.push(escapeHtml(globalize.translate(item.Type)));
} else if (item.Role.toLowerCase().includes(item.Type.toLowerCase())) {
// Avoid duplication if the Role includes the Type (i.e. Executive Producer)
lines.push(escapeHtml(item.Role));
} else {
// Type and Role are unique so list both (i.e. Writer | Novel)
lines.push(escapeHtml(globalize.translate(item.Type)));
lines.push(escapeHtml(item.Role));
}
} else {
// No Role so use the localized Type
lines.push(escapeHtml(globalize.translate(item.Type)));
}
} }
} }
@ -1285,8 +1304,7 @@ function updateUserData(card, userData) {
if (!playedIndicator) { if (!playedIndicator) {
playedIndicator = document.createElement('div'); playedIndicator = document.createElement('div');
playedIndicator.classList.add('playedIndicator'); playedIndicator.classList.add('playedIndicator', 'indicator');
playedIndicator.classList.add('indicator');
indicatorsElem = ensureIndicators(card, indicatorsElem); indicatorsElem = ensureIndicators(card, indicatorsElem);
indicatorsElem.appendChild(playedIndicator); indicatorsElem.appendChild(playedIndicator);
} }
@ -1302,7 +1320,7 @@ function updateUserData(card, userData) {
if (!countIndicator) { if (!countIndicator) {
countIndicator = document.createElement('div'); countIndicator = document.createElement('div');
countIndicator.classList.add('countIndicator'); countIndicator.classList.add('countIndicator', 'indicator');
indicatorsElem = ensureIndicators(card, indicatorsElem); indicatorsElem = ensureIndicators(card, indicatorsElem);
indicatorsElem.appendChild(countIndicator); indicatorsElem.appendChild(countIndicator);
} }

View file

@ -165,7 +165,7 @@ const UserPasswordForm: FunctionComponent<IProps> = ({ userId }: IProps) => {
<ButtonElement <ButtonElement
type='submit' type='submit'
className='raised button-submit block' className='raised button-submit block'
title='Save' title='SavePassword'
/> />
<ButtonElement <ButtonElement
type='button' type='button'

View file

@ -137,15 +137,15 @@ export function loadRecentlyAdded(
options: SectionOptions options: SectionOptions
) { ) {
elem.classList.remove('verticalSection'); elem.classList.remove('verticalSection');
const excludeViewTypes = ['playlists', 'livetv', 'boxsets', 'channels']; const excludeViewTypes = ['playlists', 'livetv', 'boxsets', 'channels', 'folders'];
const userExcludeItems = user.Configuration?.LatestItemsExcludes ?? []; const userExcludeItems = user.Configuration?.LatestItemsExcludes ?? [];
userViews.forEach(item => { userViews.forEach(item => {
if (!item.Id || userExcludeItems.indexOf(item.Id) !== -1) { if (!item.Id || userExcludeItems.includes(item.Id)) {
return; return;
} }
if (!item.CollectionType || excludeViewTypes.indexOf(item.CollectionType) !== -1) { if (item.CollectionType && excludeViewTypes.includes(item.CollectionType)) {
return; return;
} }

View file

@ -78,7 +78,7 @@ export function getPlayedIndicatorHtml(item) {
if (enablePlayedIndicator(item)) { if (enablePlayedIndicator(item)) {
const userData = item.UserData || {}; const userData = item.UserData || {};
if (userData.UnplayedItemCount) { if (userData.UnplayedItemCount) {
return '<div class="countIndicator indicator">' + datetime.toLocaleString(userData.UnplayedItemCount) + '</div>'; return '<div class="countIndicator indicator">' + formatCountIndicator(userData.UnplayedItemCount) + '</div>';
} }
if (userData.PlayedPercentage && userData.PlayedPercentage >= 100 || (userData.Played)) { if (userData.PlayedPercentage && userData.PlayedPercentage >= 100 || (userData.Played)) {
@ -93,12 +93,16 @@ export function getChildCountIndicatorHtml(item, options) {
const minCount = options?.minCount ? options.minCount : 0; const minCount = options?.minCount ? options.minCount : 0;
if (item.ChildCount && item.ChildCount > minCount) { if (item.ChildCount && item.ChildCount > minCount) {
return '<div class="countIndicator indicator">' + datetime.toLocaleString(item.ChildCount) + '</div>'; return '<div class="countIndicator indicator">' + formatCountIndicator(item.ChildCount) + '</div>';
} }
return ''; return '';
} }
function formatCountIndicator(count) {
return count >= 100 ? '99+' : count.toString();
}
export function getTimerIndicator(item) { export function getTimerIndicator(item) {
let status; let status;

View file

@ -60,6 +60,10 @@ const enablePlayedIndicator = (item: ItemDto) => {
return itemHelper.canMarkPlayed(item); return itemHelper.canMarkPlayed(item);
}; };
const formatCountIndicator = (count: number) => {
return count >= 100 ? '99+' : count.toString();
};
const useIndicator = (item: ItemDto) => { const useIndicator = (item: ItemDto) => {
const getMediaSourceIndicator = () => { const getMediaSourceIndicator = () => {
const mediaSourceCount = item.MediaSourceCount ?? 0; const mediaSourceCount = item.MediaSourceCount ?? 0;
@ -135,7 +139,7 @@ const useIndicator = (item: ItemDto) => {
if (childCount > 1) { if (childCount > 1) {
return ( return (
<Box className='countIndicator indicator childCountIndicator'> <Box className='countIndicator indicator childCountIndicator'>
{datetime.toLocaleString(item.ChildCount)} {formatCountIndicator(childCount)}
</Box> </Box>
); );
} }
@ -149,7 +153,7 @@ const useIndicator = (item: ItemDto) => {
if (userData.UnplayedItemCount) { if (userData.UnplayedItemCount) {
return ( return (
<Box className='countIndicator indicator unplayedItemCount'> <Box className='countIndicator indicator unplayedItemCount'>
{datetime.toLocaleString(userData.UnplayedItemCount)} {formatCountIndicator(userData.UnplayedItemCount)}
</Box> </Box>
); );
} }

View file

@ -280,11 +280,11 @@ export function getCommands(options) {
}); });
} }
if (item.PlaylistItemId && options.playlistId) { if (item.PlaylistItemId && options.playlistId && options.canEditPlaylist) {
commands.push({ commands.push({
name: globalize.translate('RemoveFromPlaylist'), name: globalize.translate('RemoveFromPlaylist'),
id: 'removefromplaylist', id: 'removefromplaylist',
icon: 'remove' icon: 'playlist_remove'
}); });
} }
@ -292,7 +292,7 @@ export function getCommands(options) {
commands.push({ commands.push({
name: globalize.translate('RemoveFromCollection'), name: globalize.translate('RemoveFromCollection'),
id: 'removefromcollection', id: 'removefromcollection',
icon: 'remove' icon: 'playlist_remove'
}); });
} }
@ -696,6 +696,6 @@ export function show(options) {
} }
export default { export default {
getCommands: getCommands, getCommands,
show: show show
}; };

View file

@ -432,6 +432,12 @@ export function setContentType(parent, contentType) {
parent.querySelector('.fldAllowEmbeddedSubtitlesContainer').classList.add('hide'); parent.querySelector('.fldAllowEmbeddedSubtitlesContainer').classList.add('hide');
} }
if (contentType === 'music') {
parent.querySelector('.lyricSettingsSection').classList.remove('hide');
} else {
parent.querySelector('.lyricSettingsSection').classList.add('hide');
}
parent.querySelector('.chkAutomaticallyAddToCollectionContainer').classList.toggle('hide', contentType !== 'movies' && contentType !== 'mixed'); parent.querySelector('.chkAutomaticallyAddToCollectionContainer').classList.toggle('hide', contentType !== 'movies' && contentType !== 'mixed');
return populateMetadataSettings(parent, contentType); return populateMetadataSettings(parent, contentType);
@ -536,6 +542,7 @@ export function getLibraryOptions(parent) {
SkipSubtitlesIfEmbeddedSubtitlesPresent: parent.querySelector('#chkSkipIfGraphicalSubsPresent').checked, SkipSubtitlesIfEmbeddedSubtitlesPresent: parent.querySelector('#chkSkipIfGraphicalSubsPresent').checked,
SkipSubtitlesIfAudioTrackMatches: parent.querySelector('#chkSkipIfAudioTrackPresent').checked, SkipSubtitlesIfAudioTrackMatches: parent.querySelector('#chkSkipIfAudioTrackPresent').checked,
SaveSubtitlesWithMedia: parent.querySelector('#chkSaveSubtitlesLocally').checked, SaveSubtitlesWithMedia: parent.querySelector('#chkSaveSubtitlesLocally').checked,
SaveLyricsWithMedia: parent.querySelector('#chkSaveLyricsLocally').checked,
RequirePerfectSubtitleMatch: parent.querySelector('#chkRequirePerfectMatch').checked, RequirePerfectSubtitleMatch: parent.querySelector('#chkRequirePerfectMatch').checked,
AutomaticallyAddToCollection: parent.querySelector('#chkAutomaticallyAddToCollection').checked, AutomaticallyAddToCollection: parent.querySelector('#chkAutomaticallyAddToCollection').checked,
MetadataSavers: Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll('.chkMetadataSaver'), elem => { MetadataSavers: Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll('.chkMetadataSaver'), elem => {
@ -596,6 +603,7 @@ export function setLibraryOptions(parent, options) {
parent.querySelector('#selectAllowEmbeddedSubtitles').value = options.AllowEmbeddedSubtitles; parent.querySelector('#selectAllowEmbeddedSubtitles').value = options.AllowEmbeddedSubtitles;
parent.querySelector('#chkSkipIfGraphicalSubsPresent').checked = options.SkipSubtitlesIfEmbeddedSubtitlesPresent; parent.querySelector('#chkSkipIfGraphicalSubsPresent').checked = options.SkipSubtitlesIfEmbeddedSubtitlesPresent;
parent.querySelector('#chkSaveSubtitlesLocally').checked = options.SaveSubtitlesWithMedia; parent.querySelector('#chkSaveSubtitlesLocally').checked = options.SaveSubtitlesWithMedia;
parent.querySelector('#chkSaveLyricsLocally').checked = options.SaveLyricsWithMedia;
parent.querySelector('#chkSkipIfAudioTrackPresent').checked = options.SkipSubtitlesIfAudioTrackMatches; parent.querySelector('#chkSkipIfAudioTrackPresent').checked = options.SkipSubtitlesIfAudioTrackMatches;
parent.querySelector('#chkRequirePerfectMatch').checked = options.RequirePerfectSubtitleMatch; parent.querySelector('#chkRequirePerfectMatch').checked = options.RequirePerfectSubtitleMatch;
parent.querySelector('#chkAutomaticallyAddToCollection').checked = options.AutomaticallyAddToCollection; parent.querySelector('#chkAutomaticallyAddToCollection').checked = options.AutomaticallyAddToCollection;

View file

@ -193,3 +193,15 @@
<div class="fieldDescription checkboxFieldDescription">${SaveSubtitlesIntoMediaFoldersHelp}</div> <div class="fieldDescription checkboxFieldDescription">${SaveSubtitlesIntoMediaFoldersHelp}</div>
</div> </div>
</div> </div>
<div class="lyricSettingsSection hide">
<h2>${Lyrics}</h2>
<div class="checkboxContainer checkboxContainer-withDescription advanced">
<label>
<input type="checkbox" is="emby-checkbox" id="chkSaveLyricsLocally" />
<span>${SaveLyricsIntoMediaFolders}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${SaveLyricsIntoMediaFoldersHelp}</div>
</div>
</div>

View file

@ -591,8 +591,7 @@ function setFieldVisibilities(context, item) {
|| item.Type === 'Genre' || item.Type === 'Genre'
|| item.Type === 'Studio' || item.Type === 'Studio'
|| item.Type === 'MusicGenre' || item.Type === 'MusicGenre'
|| item.Type === 'TvChannel' || item.Type === 'TvChannel') {
|| item.Type === 'Book') {
hideElement('#peopleCollapsible', context); hideElement('#peopleCollapsible', context);
} else { } else {
showElement('#peopleCollapsible', context); showElement('#peopleCollapsible', context);

View file

@ -26,6 +26,18 @@
<option value="Engineer">${Engineer}</option> <option value="Engineer">${Engineer}</option>
<option value="Mixer">${Mixer}</option> <option value="Mixer">${Mixer}</option>
<option value="Remixer">${Remixer}</option> <option value="Remixer">${Remixer}</option>
<option value="AlbumArtist">${AlbumArtist}</option>
<option value="Artist">${Artist}</option>
<option value="Creator">${Creator}</option>
<option value="Author">${Author}</option>
<option value="Illustrator">${Illustrator}</option>
<option value="Penciller">${Penciller}</option>
<option value="Inker">${Inker}</option>
<option value="Colorist">${Colorist}</option>
<option value="Letterer">${Letterer}</option>
<option value="CoverArtist">${CoverArtist}</option>
<option value="Editor">${Editor}</option>
<option value="Translator">${Translator}</option>
</select> </select>
</div> </div>

View file

@ -43,8 +43,7 @@ let currentRuntimeTicks = 0;
let isVisibilityAllowed = true; let isVisibilityAllowed = true;
let lyricPageActive = false; let isLyricPageActive = false;
let isAudio = false;
function getNowPlayingBarHtml() { function getNowPlayingBarHtml() {
let html = ''; let html = '';
@ -86,7 +85,7 @@ function getNowPlayingBarHtml() {
html += `<button is="paper-icon-button-light" class="btnAirPlay mediaButton" title="${globalize.translate('AirPlay')}"><span class="material-icons airplay" aria-hidden="true"></span></button>`; html += `<button is="paper-icon-button-light" class="btnAirPlay mediaButton" title="${globalize.translate('AirPlay')}"><span class="material-icons airplay" aria-hidden="true"></span></button>`;
html += `<button is="paper-icon-button-light" class="openLyricsButton mediaButton" title="${globalize.translate('Lyrics')}"><span class="material-icons lyrics" style="top:0.1em" aria-hidden="true"></span></button>`; html += `<button is="paper-icon-button-light" class="openLyricsButton mediaButton hide" title="${globalize.translate('Lyrics')}"><span class="material-icons lyrics" style="top:0.1em" aria-hidden="true"></span></button>`;
html += `<button is="paper-icon-button-light" class="toggleRepeatButton mediaButton" title="${globalize.translate('Repeat')}"><span class="material-icons repeat" aria-hidden="true"></span></button>`; html += `<button is="paper-icon-button-light" class="toggleRepeatButton mediaButton" title="${globalize.translate('Repeat')}"><span class="material-icons repeat" aria-hidden="true"></span></button>`;
html += `<button is="paper-icon-button-light" class="btnShuffleQueue mediaButton" title="${globalize.translate('Shuffle')}"><span class="material-icons shuffle" aria-hidden="true"></span></button>`; html += `<button is="paper-icon-button-light" class="btnShuffleQueue mediaButton" title="${globalize.translate('Shuffle')}"><span class="material-icons shuffle" aria-hidden="true"></span></button>`;
@ -220,7 +219,7 @@ function bindEvents(elem) {
}); });
lyricButton.addEventListener('click', function() { lyricButton.addEventListener('click', function() {
if (lyricPageActive) { if (isLyricPageActive) {
appRouter.back(); appRouter.back();
} else { } else {
appRouter.show('lyrics'); appRouter.show('lyrics');
@ -303,8 +302,8 @@ function getNowPlayingBar() {
nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar');
if (layoutManager.mobile) { if (layoutManager.mobile) {
hideButton(nowPlayingBarElement.querySelector('.btnShuffleQueue')); nowPlayingBarElement.querySelector('.btnShuffleQueue').classList.add('hide');
hideButton(nowPlayingBarElement.querySelector('.nowPlayingBarCenter')); nowPlayingBarElement.querySelector('.nowPlayingBarCenter').classList.add('hide');
} }
if (browser.safari && browser.slow) { if (browser.safari && browser.slow) {
@ -319,14 +318,6 @@ function getNowPlayingBar() {
return nowPlayingBarElement; return nowPlayingBarElement;
} }
function showButton(button) {
button.classList.remove('hide');
}
function hideButton(button) {
button.classList.add('hide');
}
function updatePlayPauseState(isPaused) { function updatePlayPauseState(isPaused) {
if (playPauseButtons) { if (playPauseButtons) {
playPauseButtons.forEach((button) => { playPauseButtons.forEach((button) => {
@ -378,7 +369,7 @@ function updatePlayerStateInternal(event, state, player) {
updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks, playbackManager.getBufferedRanges(player)); updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks, playbackManager.getBufferedRanges(player));
updateNowPlayingInfo(state); updateNowPlayingInfo(state);
updateLyricButton(); updateLyricButton(nowPlayingItem);
} }
function updateRepeatModeDisplay(repeatMode) { function updateRepeatModeDisplay(repeatMode) {
@ -453,11 +444,7 @@ function updatePlayerVolumeState(isMuted, volumeLevel) {
showVolumeSlider = false; showVolumeSlider = false;
} }
if (showMuteButton) { muteButton.classList.toggle('hide', !showMuteButton);
showButton(muteButton);
} else {
hideButton(muteButton);
}
// See bindEvents for why this is necessary // See bindEvents for why this is necessary
if (volumeSlider) { if (volumeSlider) {
@ -469,20 +456,18 @@ function updatePlayerVolumeState(isMuted, volumeLevel) {
} }
} }
function updateLyricButton() { function updateLyricButton(item) {
if (!isEnabled) { if (!isEnabled) return;
return;
}
isAudio ? showButton(lyricButton) : hideButton(lyricButton); const hasLyrics = !!item && item.Type === 'Audio' && item.HasLyrics;
lyricButton.classList.toggle('hide', !hasLyrics);
setLyricButtonActiveStatus(); setLyricButtonActiveStatus();
} }
function setLyricButtonActiveStatus() { function setLyricButtonActiveStatus() {
if (!isEnabled) { if (!isEnabled) return;
return;
} lyricButton.classList.toggle('buttonActive', isLyricPageActive);
lyricButton.classList.toggle('buttonActive', lyricPageActive);
} }
function seriesImageUrl(item, options) { function seriesImageUrl(item, options) {
@ -628,8 +613,6 @@ function onPlaybackStart(e, state) {
console.debug('nowplaying event: ' + e.type); console.debug('nowplaying event: ' + e.type);
const player = this; const player = this;
isAudio = state.NowPlayingItem.Type === 'Audio';
onStateChanged.call(player, e, state); onStateChanged.call(player, e, state);
} }
@ -733,7 +716,7 @@ function onStateChanged(event, state) {
} }
getNowPlayingBar(); getNowPlayingBar();
updateLyricButton(); updateLyricButton(state.NowPlayingItem);
updatePlayerStateInternal(event, state, player); updatePlayerStateInternal(event, state, player);
} }
@ -790,7 +773,7 @@ function refreshFromPlayer(player, type) {
} }
function bindToPlayer(player) { function bindToPlayer(player) {
lyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics'; isLyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
if (player === currentPlayer) { if (player === currentPlayer) {
return; return;
} }
@ -823,7 +806,7 @@ Events.on(playbackManager, 'playerchange', function () {
bindToPlayer(playbackManager.getCurrentPlayer()); bindToPlayer(playbackManager.getCurrentPlayer());
document.addEventListener('viewbeforeshow', function (e) { document.addEventListener('viewbeforeshow', function (e) {
lyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics'; isLyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
setLyricButtonActiveStatus(); setLyricButtonActiveStatus();
if (!e.detail.options.enableMediaControl) { if (!e.detail.options.enableMediaControl) {
if (isVisibilityAllowed) { if (isVisibilityAllowed) {

View file

@ -1,38 +1,40 @@
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client'; import ServerConnections from 'components/ServerConnections';
import { getItemQuery } from 'hooks/useItem';
import { toApi } from 'utils/jellyfin-apiclient/compat';
import { queryClient } from 'utils/query/queryClient';
import { playbackManager } from './playbackmanager'; import { playbackManager } from './playbackmanager';
interface PlaybackInfo { async function mirrorIfEnabled(serverId: string, itemId: string) {
item: BaseItemDto; if (playbackManager.enableDisplayMirroring()) {
context?: string;
}
function mirrorItem(info: PlaybackInfo, player?: unknown) {
const { item } = info;
playbackManager.displayContent({
ItemName: item.Name,
ItemId: item.Id,
ItemType: item.Type,
Context: info.context
}, player);
}
function mirrorIfEnabled(info: PlaybackInfo) {
if (info && playbackManager.enableDisplayMirroring()) {
const playerInfo = playbackManager.getPlayerInfo(); const playerInfo = playbackManager.getPlayerInfo();
if (playerInfo && !playerInfo.isLocalPlayer && playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { if (playerInfo && !playerInfo.isLocalPlayer && playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
mirrorItem(info, playbackManager.getCurrentPlayer()); const apiClient = ServerConnections.getApiClient(serverId);
const api = toApi(apiClient);
const userId = apiClient.getCurrentUserId();
try {
const item = await queryClient.fetchQuery(getItemQuery(
api,
userId,
itemId));
playbackManager.displayContent({
ItemName: item.Name,
ItemId: item.Id,
ItemType: item.Type
}, playbackManager.getCurrentPlayer());
} catch (err) {
console.error('[DisplayMirrorManager] failed to mirror item', err);
}
} }
} }
} }
document.addEventListener('viewshow', e => { document.addEventListener('viewshow', e => {
const state = e.detail.state || {}; const { serverId, id } = e.detail?.params || {};
const { item } = state; if (serverId && id) {
void mirrorIfEnabled(serverId, id);
if (item?.ServerId) {
mirrorIfEnabled({ item });
} }
}); });

View file

@ -18,6 +18,7 @@ import { PluginType } from '../../types/plugin.ts';
import { includesAny } from '../../utils/container.ts'; import { includesAny } from '../../utils/container.ts';
import { getItems } from '../../utils/jellyfin-apiclient/getItems.ts'; import { getItems } from '../../utils/jellyfin-apiclient/getItems.ts';
import { getItemBackdropImageUrl } from '../../utils/jellyfin-apiclient/backdropImage'; import { getItemBackdropImageUrl } from '../../utils/jellyfin-apiclient/backdropImage';
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import { MediaError } from 'types/mediaError'; import { MediaError } from 'types/mediaError';
import { getMediaError } from 'utils/mediaError'; import { getMediaError } from 'utils/mediaError';
@ -1789,42 +1790,85 @@ class PlaybackManager {
}); });
} }
function translateItemsForPlayback(items, options) { async function translateItemsForPlayback(items, options) {
if (!items.length) return Promise.resolve([]); if (!items.length) return [];
if (items.length > 1 && options && options.ids) { sortItemsIfNeeded(items, options);
const firstItem = items[0];
const serverId = firstItem.ServerId;
const queryOptions = options.queryOptions || {};
const promise = getPlaybackPromise(firstItem, serverId, options, queryOptions, items);
if (promise) {
const result = await promise;
return result ? result.Items : items;
} else {
return items;
}
}
function sortItemsIfNeeded(items, options) {
if (items.length > 1 && options?.ids) {
// Use the original request id array for sorting the result in the proper order // Use the original request id array for sorting the result in the proper order
items.sort(function (a, b) { items.sort(function (a, b) {
return options.ids.indexOf(a.Id) - options.ids.indexOf(b.Id); return options.ids.indexOf(a.Id) - options.ids.indexOf(b.Id);
}); });
} }
}
const firstItem = items[0]; function getPlaybackPromise(firstItem, serverId, options, queryOptions, items) {
let promise; switch (firstItem.Type) {
case 'Program':
return getItemsForPlayback(serverId, {
Ids: firstItem.ChannelId
});
case 'Playlist':
return getItemsForPlayback(serverId, {
ParentId: firstItem.Id,
SortBy: options.shuffle ? 'Random' : null
});
case 'MusicArtist':
return getItemsForPlayback(serverId, mergePlaybackQueries({
ArtistIds: firstItem.Id,
Filters: 'IsNotFolder',
Recursive: true,
SortBy: options.shuffle ? 'Random' : 'SortName',
MediaTypes: 'Audio'
}, queryOptions));
case 'PhotoAlbum':
return getItemsForPlayback(serverId, mergePlaybackQueries({
ParentId: firstItem.Id,
Filters: 'IsNotFolder',
// Setting this to true may cause some incorrect sorting
Recursive: false,
SortBy: options.shuffle ? 'Random' : 'SortName',
// Only include Photos because we do not handle mixed queues currently
MediaTypes: 'Photo',
Limit: UNLIMITED_ITEMS
}, queryOptions));
case 'MusicGenre':
return getItemsForPlayback(serverId, mergePlaybackQueries({
GenreIds: firstItem.Id,
Filters: 'IsNotFolder',
Recursive: true,
SortBy: options.shuffle ? 'Random' : 'SortName',
MediaTypes: 'Audio'
}, queryOptions));
case 'Series':
case 'Season':
return getSeriesOrSeasonPlaybackPromise(firstItem, options, items);
case 'Episode':
return getEpisodePlaybackPromise(firstItem, options, items);
}
const serverId = firstItem.ServerId; return getNonItemTypePromise(firstItem, serverId, options, queryOptions);
}
const queryOptions = options.queryOptions || {}; function getNonItemTypePromise(firstItem, serverId, options, queryOptions) {
if (firstItem.MediaType === 'Photo') {
if (firstItem.Type === 'Program') { return getItemsForPlayback(serverId, mergePlaybackQueries({
promise = getItemsForPlayback(serverId, {
Ids: firstItem.ChannelId
});
} else if (firstItem.Type === 'Playlist') {
promise = getItemsForPlayback(serverId, {
ParentId: firstItem.Id,
SortBy: options.shuffle ? 'Random' : null
});
} else if (firstItem.Type === 'MusicArtist') {
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
ArtistIds: firstItem.Id,
Filters: 'IsNotFolder',
Recursive: true,
SortBy: options.shuffle ? 'Random' : 'SortName',
MediaTypes: 'Audio'
}, queryOptions));
} else if (firstItem.MediaType === 'Photo') {
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
ParentId: firstItem.ParentId, ParentId: firstItem.ParentId,
Filters: 'IsNotFolder', Filters: 'IsNotFolder',
// Setting this to true may cause some incorrect sorting // Setting this to true may cause some incorrect sorting
@ -1847,66 +1891,8 @@ class PlaybackManager {
return Promise.resolve(result); return Promise.resolve(result);
}); });
} else if (firstItem.Type === 'PhotoAlbum') {
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
ParentId: firstItem.Id,
Filters: 'IsNotFolder',
// Setting this to true may cause some incorrect sorting
Recursive: false,
SortBy: options.shuffle ? 'Random' : 'SortName',
// Only include Photos because we do not handle mixed queues currently
MediaTypes: 'Photo',
Limit: UNLIMITED_ITEMS
}, queryOptions));
} else if (firstItem.Type === 'MusicGenre') {
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
GenreIds: firstItem.Id,
Filters: 'IsNotFolder',
Recursive: true,
SortBy: options.shuffle ? 'Random' : 'SortName',
MediaTypes: 'Audio'
}, queryOptions));
} else if (firstItem.Type === 'Series' || firstItem.Type === 'Season') {
const apiClient = ServerConnections.getApiClient(firstItem.ServerId);
const isSeason = firstItem.Type === 'Season';
promise = apiClient.getEpisodes(firstItem.SeriesId || firstItem.Id, {
IsVirtualUnaired: false,
IsMissing: false,
SeasonId: isSeason ? firstItem.Id : undefined,
SortBy: options.shuffle ? 'Random' : undefined,
UserId: apiClient.getCurrentUserId(),
Fields: ['Chapters', 'Trickplay']
}).then(function (episodesResult) {
const originalResults = episodesResult.Items;
let foundItem = false;
if (!options.shuffle) {
episodesResult.Items = episodesResult.Items.filter(function (e) {
if (foundItem) {
return true;
}
if (!e.UserData.Played) {
foundItem = true;
return true;
}
return false;
});
}
if (episodesResult.Items.length === 0) {
episodesResult.Items = originalResults;
}
episodesResult.TotalRecordCount = episodesResult.Items.length;
return episodesResult;
});
} else if (firstItem.IsFolder && firstItem.CollectionType === 'homevideos') { } else if (firstItem.IsFolder && firstItem.CollectionType === 'homevideos') {
promise = getItemsForPlayback(serverId, mergePlaybackQueries({ return getItemsForPlayback(serverId, mergePlaybackQueries({
ParentId: firstItem.Id, ParentId: firstItem.Id,
Filters: 'IsNotFolder', Filters: 'IsNotFolder',
Recursive: true, Recursive: true,
@ -1922,7 +1908,8 @@ class PlaybackManager {
} else if (firstItem.Type !== 'BoxSet') { } else if (firstItem.Type !== 'BoxSet') {
sortBy = 'SortName'; sortBy = 'SortName';
} }
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
return getItemsForPlayback(serverId, mergePlaybackQueries({
ParentId: firstItem.Id, ParentId: firstItem.Id,
Filters: 'IsNotFolder', Filters: 'IsNotFolder',
Recursive: true, Recursive: true,
@ -1930,48 +1917,95 @@ class PlaybackManager {
SortBy: sortBy, SortBy: sortBy,
MediaTypes: 'Audio,Video' MediaTypes: 'Audio,Video'
}, queryOptions)); }, queryOptions));
} else if (firstItem.Type === 'Episode' && items.length === 1 && getPlayer(firstItem, options).supportsProgress !== false) {
promise = new Promise(function (resolve, reject) {
const apiClient = ServerConnections.getApiClient(firstItem.ServerId);
apiClient.getCurrentUser().then(function (user) {
if (!user.Configuration.EnableNextEpisodeAutoPlay || !firstItem.SeriesId) {
resolve(null);
return;
}
apiClient.getEpisodes(firstItem.SeriesId, {
IsVirtualUnaired: false,
IsMissing: false,
UserId: apiClient.getCurrentUserId(),
Fields: ['Chapters', 'Trickplay']
}).then(function (episodesResult) {
let foundItem = false;
episodesResult.Items = episodesResult.Items.filter(function (e) {
if (foundItem) {
return true;
}
if (e.Id === firstItem.Id) {
foundItem = true;
return true;
}
return false;
});
episodesResult.TotalRecordCount = episodesResult.Items.length;
resolve(episodesResult);
}, reject);
});
});
} }
if (promise) { return null;
return promise.then(function (result) { }
return result ? result.Items : items;
}); async function getSeriesOrSeasonPlaybackPromise(firstItem, options, items) {
const apiClient = ServerConnections.getApiClient(firstItem.ServerId);
const startSeasonId = firstItem.Type === 'Season' ? items[options.startIndex || 0].Id : undefined;
const episodesResult = await apiClient.getEpisodes(firstItem.SeriesId || firstItem.Id, {
IsVirtualUnaired: false,
IsMissing: false,
SeasonId: (startSeasonId && items.length === 1) ? startSeasonId : undefined,
SortBy: options.shuffle ? 'Random' : undefined,
UserId: apiClient.getCurrentUserId(),
Fields: ['Chapters', 'Trickplay']
});
if (options.shuffle) {
episodesResult.StartIndex = 0;
} else { } else {
return Promise.resolve(items); episodesResult.StartIndex = undefined;
let seasonStartIndex;
for (const [index, e] of episodesResult.Items.entries()) {
if (startSeasonId) {
if (e.SeasonId == startSeasonId) {
if (seasonStartIndex === undefined) {
seasonStartIndex = index;
}
} else {
continue;
}
}
if (!e.UserData.Played) {
episodesResult.StartIndex = index;
break;
}
}
episodesResult.StartIndex = episodesResult.StartIndex || seasonStartIndex || 0;
} }
// TODO: fix calling code to read episodesResult.StartIndex instead when set.
options.startIndex = episodesResult.StartIndex;
episodesResult.TotalRecordCount = episodesResult.Items.length;
return episodesResult;
}
function getEpisodePlaybackPromise(firstItem, options, items) {
if (items.length === 1 && getPlayer(firstItem, options).supportsProgress !== false) {
return getEpisodes(firstItem, options);
} else {
return null;
}
}
function getEpisodes(firstItem, options) {
return new Promise(function (resolve, reject) {
const apiClient = ServerConnections.getApiClient(firstItem.ServerId);
if (!firstItem.SeriesId) {
resolve(null);
return;
}
apiClient.getEpisodes(firstItem.SeriesId, {
IsVirtualUnaired: false,
IsMissing: false,
UserId: apiClient.getCurrentUserId(),
Fields: ['Chapters', 'Trickplay']
}).then(function (episodesResult) {
resolve(filterEpisodes(episodesResult, firstItem, options));
}, reject);
});
}
function filterEpisodes(episodesResult, firstItem, options) {
for (const [index, e] of episodesResult.Items.entries()) {
if (e.Id === firstItem.Id) {
episodesResult.StartIndex = index;
break;
}
}
// TODO: fix calling code to read episodesResult.StartIndex instead when set.
options.startIndex = episodesResult.StartIndex;
episodesResult.TotalRecordCount = episodesResult.Items.length;
return episodesResult;
} }
self.translateItemsForPlayback = translateItemsForPlayback; self.translateItemsForPlayback = translateItemsForPlayback;
@ -2245,35 +2279,42 @@ class PlaybackManager {
playOptions.isFirstItem = true; playOptions.isFirstItem = true;
} }
return runInterceptors(item, playOptions).then(function () { const apiClient = ServerConnections.getApiClient(item.ServerId);
if (playOptions.fullscreen) {
loading.show();
}
// TODO: This should be the media type requested, not the original media type // TODO: This should be the media type requested, not the original media type
const mediaType = item.MediaType; const mediaType = item.MediaType;
const onBitrateDetectionFailure = function () { return runInterceptors(item, playOptions)
return playAfterBitrateDetect(getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType), item, playOptions, onPlaybackStartedFn, prevSource); .then(() => {
}; if (playOptions.fullscreen) {
loading.show();
if (!isServerItem(item) || itemHelper.isLocalItem(item)) {
return onBitrateDetectionFailure();
}
const apiClient = ServerConnections.getApiClient(item.ServerId);
apiClient.getEndpointInfo().then(function (endpointInfo) {
if ((mediaType === 'Video' || mediaType === 'Audio') && appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType)) {
return apiClient.detectBitrate().then(function (bitrate) {
appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType, bitrate);
return playAfterBitrateDetect(bitrate, item, playOptions, onPlaybackStartedFn, prevSource);
}, onBitrateDetectionFailure);
} else {
onBitrateDetectionFailure();
} }
}, onBitrateDetectionFailure);
}, onInterceptorRejection); if (!isServerItem(item) || itemHelper.isLocalItem(item)) {
return Promise.reject('skip bitrate detection');
}
return apiClient.getEndpointInfo().then((endpointInfo) => {
if ((mediaType === 'Video' || mediaType === 'Audio') && appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType)) {
return apiClient.detectBitrate().then((bitrate) => {
appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType, bitrate);
return bitrate;
});
}
return Promise.reject('skip bitrate detection');
});
})
.catch(() => getSavedMaxStreamingBitrate(apiClient, mediaType))
.then((bitrate) => {
return playAfterBitrateDetect(bitrate, item, playOptions, onPlaybackStartedFn, prevSource);
})
.catch(onInterceptorRejection)
.finally(() => {
if (playOptions.fullscreen) {
loading.hide();
}
});
} }
function cancelPlayback() { function cancelPlayback() {
@ -2287,8 +2328,21 @@ class PlaybackManager {
Events.trigger(self, 'playbackcancelled'); Events.trigger(self, 'playbackcancelled');
} }
function onInterceptorRejection() { function onInterceptorRejection(e) {
cancelPlayback(); cancelPlayback();
let displayErrorCode = 'ErrorDefault';
if (e instanceof Response) {
if (e.status >= 500) {
displayErrorCode = `PlaybackError.${MediaError.SERVER_ERROR}`;
} else if (e.status >= 400) {
displayErrorCode = `PlaybackError.${MediaError.NO_MEDIA_ERROR}`;
}
}
showPlaybackInfoErrorMessage(self, displayErrorCode);
return Promise.reject(); return Promise.reject();
} }
@ -2782,7 +2836,7 @@ class PlaybackManager {
} else { } else {
if (item.AlbumId != null) { if (item.AlbumId != null) {
return apiClient.getItem(apiClient.getCurrentUserId(), item.AlbumId).then(function(result) { return apiClient.getItem(apiClient.getCurrentUserId(), item.AlbumId).then(function(result) {
mediaSource.albumLUFS = result.LUFS; mediaSource.albumNormalizationGain = result.NormalizationGain;
return mediaSource; return mediaSource;
}); });
} }
@ -3313,7 +3367,13 @@ class PlaybackManager {
if (errorOccurred) { if (errorOccurred) {
showPlaybackInfoErrorMessage(self, 'PlaybackError' + displayErrorCode); showPlaybackInfoErrorMessage(self, 'PlaybackError' + displayErrorCode);
} else if (nextItem) { } else if (nextItem) {
self.nextTrack(); const apiClient = ServerConnections.getApiClient(nextItem.item.ServerId);
apiClient.getCurrentUser().then(function (user) {
if (user.Configuration.EnableNextEpisodeAutoPlay || nextMediaType !== MediaType.Video) {
self.nextTrack();
}
});
} }
} }

View file

@ -173,6 +173,8 @@ function loadForm(context, user, userSettings, systemInfo, apiClient) {
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false; context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer(); context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
context.querySelector('.chkEnableDts').checked = appSettings.enableDts();
context.querySelector('.chkEnableTrueHd').checked = appSettings.enableTrueHd();
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode(); context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
context.querySelector('#selectAudioNormalization').value = userSettings.selectAudioNormalization(); context.querySelector('#selectAudioNormalization').value = userSettings.selectAudioNormalization();
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay(); context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
@ -180,6 +182,7 @@ function loadForm(context, user, userSettings, systemInfo, apiClient) {
context.querySelector('.chkRememberSubtitleSelections').checked = user.Configuration.RememberSubtitleSelections || false; context.querySelector('.chkRememberSubtitleSelections').checked = user.Configuration.RememberSubtitleSelections || false;
context.querySelector('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers(); context.querySelector('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers();
context.querySelector('.chkLimitSupportedVideoResolution').checked = appSettings.limitSupportedVideoResolution(); context.querySelector('.chkLimitSupportedVideoResolution').checked = appSettings.limitSupportedVideoResolution();
context.querySelector('#selectPreferredTranscodeVideoAudioCodec').value = appSettings.preferredTranscodeVideoAudioCodec();
setMaxBitrateIntoField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video'); setMaxBitrateIntoField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video');
setMaxBitrateIntoField(context.querySelector('.selectVideoInternetQuality'), false, 'Video'); setMaxBitrateIntoField(context.querySelector('.selectVideoInternetQuality'), false, 'Video');
@ -215,6 +218,10 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
appSettings.maxChromecastBitrate(context.querySelector('.selectChromecastVideoQuality').value); appSettings.maxChromecastBitrate(context.querySelector('.selectChromecastVideoQuality').value);
appSettings.maxVideoWidth(context.querySelector('.selectMaxVideoWidth').value); appSettings.maxVideoWidth(context.querySelector('.selectMaxVideoWidth').value);
appSettings.limitSupportedVideoResolution(context.querySelector('.chkLimitSupportedVideoResolution').checked); appSettings.limitSupportedVideoResolution(context.querySelector('.chkLimitSupportedVideoResolution').checked);
appSettings.preferredTranscodeVideoAudioCodec(context.querySelector('#selectPreferredTranscodeVideoAudioCodec').value);
appSettings.enableDts(context.querySelector('.chkEnableDts').checked);
appSettings.enableTrueHd(context.querySelector('.chkEnableTrueHd').checked);
setMaxBitrateFromField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video'); setMaxBitrateFromField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video');
setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video'); setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video');

View file

@ -159,6 +159,41 @@
</div> </div>
</div> </div>
<div class="verticalSection verticalSection-extrabottompadding">
<h2 class="sectionTitle">
${HeaderVideoAdvanced}
</h2>
<div class="checkboxContainer checkboxContainer-withDescription fldEnableDts">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEnableDts" />
<span>${EnableDts}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${EnableDtsHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldEnableTrueHd">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEnableTrueHd" />
<span>${EnableTrueHd}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${EnableTrueHdHelp}</div>
</div>
<div class="selectContainer">
<select is="emby-select" id="selectPreferredTranscodeVideoAudioCodec" label="${LabelSelectPreferredTranscodeVideoAudioCodec}">
<option value="">${Auto}</option>
<option value="aac">AAC</option>
<option value="ac3">AC3</option>
<option value="alac">ALAC</option>
<option value="dts">DTS</option>
<option value="flac">FLAC</option>
<option value="opus">Opus</option>
</select>
<div class="fieldDescription">${SelectPreferredTranscodeVideoAudioCodecHelp}</div>
</div>
</div>
<button is="emby-button" type="submit" class="raised button-submit block btnSave hide"> <button is="emby-button" type="submit" class="raised button-submit block btnSave hide">
<span>${Save}</span> <span>${Save}</span>
</button> </button>

View file

@ -4,6 +4,7 @@ import { getItemsApi } from '@jellyfin/sdk/lib/utils/api/items-api';
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api'; import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
import escapeHtml from 'escape-html'; import escapeHtml from 'escape-html';
import toast from 'components/toast/toast';
import dom from 'scripts/dom'; import dom from 'scripts/dom';
import globalize from 'scripts/globalize'; import globalize from 'scripts/globalize';
import { currentSettings as userSettings } from 'scripts/settings/userSettings'; import { currentSettings as userSettings } from 'scripts/settings/userSettings';
@ -52,12 +53,14 @@ function onSubmit(this: HTMLElement, e: Event) {
addToPlaylist(panel, playlistId) addToPlaylist(panel, playlistId)
.catch(err => { .catch(err => {
console.error('[PlaylistEditor] Failed to add to playlist %s', playlistId, err); console.error('[PlaylistEditor] Failed to add to playlist %s', playlistId, err);
toast(globalize.translate('PlaylistError.AddFailed'));
}) })
.finally(loading.hide); .finally(loading.hide);
} else { } else {
createPlaylist(panel) createPlaylist(panel)
.catch(err => { .catch(err => {
console.error('[PlaylistEditor] Failed to create playlist', err); console.error('[PlaylistEditor] Failed to create playlist', err);
toast(globalize.translate('PlaylistError.CreateFailed'));
}) })
.finally(loading.hide); .finally(loading.hide);
} }
@ -73,13 +76,16 @@ function createPlaylist(dlg: DialogElement) {
const apiClient = ServerConnections.getApiClient(currentServerId); const apiClient = ServerConnections.getApiClient(currentServerId);
const api = toApi(apiClient); const api = toApi(apiClient);
const itemIds = dlg.querySelector<HTMLInputElement>('.fldSelectedItemIds')?.value || ''; const itemIds = dlg.querySelector<HTMLInputElement>('.fldSelectedItemIds')?.value || undefined;
return getPlaylistsApi(api) return getPlaylistsApi(api)
.createPlaylist({ .createPlaylist({
name: dlg.querySelector<HTMLInputElement>('#txtNewPlaylistName')?.value, createPlaylistDto: {
ids: itemIds.split(','), Name: dlg.querySelector<HTMLInputElement>('#txtNewPlaylistName')?.value,
userId: apiClient.getCurrentUserId() IsPublic: dlg.querySelector<HTMLInputElement>('#chkPlaylistPublic')?.checked,
Ids: itemIds?.split(','),
UserId: apiClient.getCurrentUserId()
}
}) })
.then(result => { .then(result => {
dlg.submitted = true; dlg.submitted = true;
@ -147,6 +153,32 @@ function populatePlaylists(editorOptions: PlaylistEditorOptions, panel: DialogEl
recursive: true recursive: true
}) })
.then(({ data }) => { .then(({ data }) => {
return Promise.all((data.Items || []).map(item => {
const playlist = {
item,
permissions: undefined
};
if (!item.Id) return playlist;
return getPlaylistsApi(api)
.getPlaylistUser({
playlistId: item.Id,
userId: apiClient.getCurrentUserId()
})
.then(({ data: permissions }) => ({
...playlist,
permissions
}))
.catch(err => {
// If a user doesn't have access, then the request will 404 and throw
console.info('[PlaylistEditor] Failed to fetch playlist permissions', err);
return playlist;
});
}));
})
.then(playlists => {
let html = ''; let html = '';
if ((editorOptions.enableAddToPlayQueue !== false && playbackManager.isPlaying()) || SyncPlay?.Manager.isSyncPlayEnabled()) { if ((editorOptions.enableAddToPlayQueue !== false && playbackManager.isPlaying()) || SyncPlay?.Manager.isSyncPlayEnabled()) {
@ -155,8 +187,10 @@ function populatePlaylists(editorOptions: PlaylistEditorOptions, panel: DialogEl
html += `<option value="">${globalize.translate('OptionNew')}</option>`; html += `<option value="">${globalize.translate('OptionNew')}</option>`;
html += data.Items?.map(i => { html += playlists.map(({ item, permissions }) => {
return `<option value="${i.Id}">${escapeHtml(i.Name)}</option>`; if (!permissions?.CanEdit) return '';
return `<option value="${item.Id}">${escapeHtml(item.Name)}</option>`;
}); });
select.innerHTML = html; select.innerHTML = html;
@ -195,6 +229,17 @@ function getEditorHtml(items: string[]) {
html += `<input is="emby-input" type="text" id="txtNewPlaylistName" required="required" label="${globalize.translate('LabelName')}"${autoFocus} />`; html += `<input is="emby-input" type="text" id="txtNewPlaylistName" required="required" label="${globalize.translate('LabelName')}"${autoFocus} />`;
html += '</div>'; html += '</div>';
html += `
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" id="chkPlaylistPublic" checked />
<span>${globalize.translate('PlaylistPublic')}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">
${globalize.translate('PlaylistPublicDescription')}
</div>
</div>`;
// newPlaylistInfo // newPlaylistInfo
html += '</div>'; html += '</div>';

View file

@ -1,3 +1,4 @@
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
import { Action, createHashHistory } from 'history'; import { Action, createHashHistory } from 'history';
import { appHost } from '../apphost'; import { appHost } from '../apphost';
@ -9,8 +10,11 @@ import loading from '../loading/loading';
import viewManager from '../viewManager/viewManager'; import viewManager from '../viewManager/viewManager';
import ServerConnections from '../ServerConnections'; import ServerConnections from '../ServerConnections';
import alert from '../alert'; import alert from '../alert';
import { ConnectionState } from '../../utils/jellyfin-apiclient/ConnectionState.ts';
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type'; import { queryClient } from 'utils/query/queryClient';
import { getItemQuery } from 'hooks/useItem';
import { toApi } from 'utils/jellyfin-apiclient/compat';
import { ConnectionState } from 'utils/jellyfin-apiclient/ConnectionState.ts';
export const history = createHashHistory(); export const history = createHashHistory();
@ -183,18 +187,26 @@ class AppRouter {
showItem(item, serverId, options) { showItem(item, serverId, options) {
// TODO: Refactor this so it only gets items, not strings. // TODO: Refactor this so it only gets items, not strings.
if (typeof (item) === 'string') { if (typeof item === 'string') {
const apiClient = serverId ? ServerConnections.getApiClient(serverId) : ServerConnections.currentApiClient(); const apiClient = serverId ? ServerConnections.getApiClient(serverId) : ServerConnections.currentApiClient();
apiClient.getItem(apiClient.getCurrentUserId(), item).then((itemObject) => { const api = toApi(apiClient);
this.showItem(itemObject, options); const userId = apiClient.getCurrentUserId();
});
queryClient
.fetchQuery(getItemQuery(api, userId, item))
.then(itemObject => {
this.showItem(itemObject, options);
})
.catch(err => {
console.error('[AppRouter] Failed to fetch item', err);
});
} else { } else {
if (arguments.length === 2) { if (arguments.length === 2) {
options = arguments[1]; options = arguments[1];
} }
const url = this.getRouteUrl(item, options); const url = this.getRouteUrl(item, options);
this.show(url, { item }); this.show(url);
} }
} }

View file

@ -2,6 +2,7 @@
* Module shortcuts. * Module shortcuts.
* @module components/shortcuts * @module components/shortcuts
*/ */
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
import { playbackManager } from './playback/playbackmanager'; import { playbackManager } from './playback/playbackmanager';
import inputManager from '../scripts/inputManager'; import inputManager from '../scripts/inputManager';
@ -12,6 +13,7 @@ import recordingHelper from './recordingcreator/recordinghelper';
import ServerConnections from './ServerConnections'; import ServerConnections from './ServerConnections';
import toast from './toast/toast'; import toast from './toast/toast';
import * as userSettings from '../scripts/settings/userSettings'; import * as userSettings from '../scripts/settings/userSettings';
import { toApi } from 'utils/jellyfin-apiclient/compat';
function playAllFromHere(card, serverId, queue) { function playAllFromHere(card, serverId, queue) {
const parent = card.parentNode; const parent = card.parentNode;
@ -100,7 +102,7 @@ function notifyRefreshNeeded(childElement, itemsContainer) {
} }
} }
function showContextMenu(card, options) { function showContextMenu(card, options = {}) {
getItem(card).then(item => { getItem(card).then(item => {
const playlistId = card.getAttribute('data-playlistid'); const playlistId = card.getAttribute('data-playlistid');
const collectionId = card.getAttribute('data-collectionid'); const collectionId = card.getAttribute('data-collectionid');
@ -110,28 +112,56 @@ function showContextMenu(card, options) {
item.PlaylistItemId = elem ? elem.getAttribute('data-playlistitemid') : null; item.PlaylistItemId = elem ? elem.getAttribute('data-playlistitemid') : null;
} }
import('./itemContextMenu').then((itemContextMenu) => { const apiClient = ServerConnections.getApiClient(item.ServerId);
ServerConnections.getApiClient(item.ServerId).getCurrentUser().then(user => { const api = toApi(apiClient);
itemContextMenu.show(Object.assign({
item: item, Promise.all([
// Import the item menu component
import('./itemContextMenu'),
// Fetch the current user
apiClient.getCurrentUser(),
// Fetch playlist perms if item is a child of a playlist
playlistId ?
getPlaylistsApi(api)
.getPlaylistUser({
playlistId,
userId: apiClient.getCurrentUserId()
})
.then(({ data }) => data)
.catch(err => {
// If a user doesn't have access, then the request will 404 and throw
console.info('[Shortcuts] Failed to fetch playlist permissions', err);
return { CanEdit: false };
}) :
// Not a playlist item
Promise.resolve({ CanEdit: false })
])
.then(([
itemContextMenu,
user,
playlistPerms
]) => {
return itemContextMenu.show({
item,
play: true, play: true,
queue: true, queue: true,
playAllFromHere: !item.IsFolder, playAllFromHere: item.Type === 'Season' || !item.IsFolder,
queueAllFromHere: !item.IsFolder, queueAllFromHere: !item.IsFolder,
playlistId: playlistId, playlistId,
collectionId: collectionId, canEditPlaylist: !!playlistPerms.CanEdit,
user: user collectionId,
}, options || {})) user,
.then(result => { ...options
if (result.command === 'playallfromhere' || result.command === 'queueallfromhere') { });
executeAction(card, options.positionTo, result.command); })
} else if (result.updated || result.deleted) { .then(result => {
notifyRefreshNeeded(card, options.itemsContainer); if (result.command === 'playallfromhere' || result.command === 'queueallfromhere') {
} executeAction(card, options.positionTo, result.command);
}) } else if (result.updated || result.deleted) {
.catch(() => { /* no-op */ }); notifyRefreshNeeded(card, options.itemsContainer);
}); }
}); })
.catch(() => { /* no-op */ });
}); });
} }
@ -406,8 +436,8 @@ export function getShortcutAttributesHtml(item, serverId) {
} }
export default { export default {
on: on, on,
off: off, off,
onClick: onClick, onClick,
getShortcutAttributesHtml: getShortcutAttributesHtml getShortcutAttributesHtml
}; };

View file

@ -1,6 +1,14 @@
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import { getLibraryApi } from '@jellyfin/sdk/lib/utils/api/library-api';
import { getItemQuery } from 'hooks/useItem';
import { currentSettings as userSettings } from 'scripts/settings/userSettings';
import { ItemKind } from 'types/base/models/item-kind';
import Events from 'utils/events.ts';
import { toApi } from 'utils/jellyfin-apiclient/compat';
import { queryClient } from 'utils/query/queryClient';
import { playbackManager } from './playback/playbackmanager'; import { playbackManager } from './playback/playbackmanager';
import * as userSettings from '../scripts/settings/userSettings';
import Events from '../utils/events.ts';
import ServerConnections from './ServerConnections'; import ServerConnections from './ServerConnections';
let currentOwnerId; let currentOwnerId;
@ -50,44 +58,61 @@ function stopIfPlaying() {
} }
function enabled(mediaType) { function enabled(mediaType) {
if (mediaType === 'Video') { if (mediaType === MediaType.Video) {
return userSettings.enableThemeVideos(); return userSettings.enableThemeVideos();
} }
return userSettings.enableThemeSongs(); return userSettings.enableThemeSongs();
} }
const excludeTypes = ['CollectionFolder', 'UserView', 'Program', 'SeriesTimer', 'Person', 'TvChannel', 'Channel']; const excludeTypes = [
ItemKind.CollectionFolder,
ItemKind.UserView,
ItemKind.Person,
ItemKind.Program,
ItemKind.TvChannel,
ItemKind.Channel,
ItemKind.SeriesTimer
];
function loadThemeMedia(item) { async function loadThemeMedia(serverId, itemId) {
if (item.CollectionType) { const apiClient = ServerConnections.getApiClient(serverId);
stopIfPlaying(); const api = toApi(apiClient);
return; const userId = apiClient.getCurrentUserId();
}
if (excludeTypes.indexOf(item.Type) !== -1) { try {
stopIfPlaying(); const item = await queryClient.fetchQuery(getItemQuery(
return; api,
} userId,
itemId));
const apiClient = ServerConnections.getApiClient(item.ServerId); if (item.CollectionType) {
apiClient.getThemeMedia(apiClient.getCurrentUserId(), item.Id, true).then(function (themeMediaResult) { stopIfPlaying();
const result = userSettings.enableThemeVideos() && themeMediaResult.ThemeVideosResult.Items.length ? themeMediaResult.ThemeVideosResult : themeMediaResult.ThemeSongsResult; return;
const ownerId = result.OwnerId;
if (ownerId !== currentOwnerId) {
playThemeMedia(result.Items, ownerId);
} }
});
if (excludeTypes.includes(item.Type)) {
stopIfPlaying();
return;
}
const { data: themeMedia } = await getLibraryApi(api)
.getThemeMedia({ userId, itemId: item.Id, inheritFromParent: true });
const result = userSettings.enableThemeVideos() && themeMedia.ThemeVideosResult?.Items?.length ? themeMedia.ThemeVideosResult : themeMedia.ThemeSongsResult;
if (result.OwnerId !== currentOwnerId) {
playThemeMedia(result.Items, result.OwnerId);
}
} catch (err) {
console.error('[ThemeMediaPlayer] failed to load theme media', err);
}
} }
document.addEventListener('viewshow', function (e) { document.addEventListener('viewshow', e => {
const state = e.detail.state || {}; const { serverId, id } = e.detail?.params || {};
const item = state.item; if (serverId && id) {
void loadThemeMedia(serverId, id);
if (item?.ServerId) {
loadThemeMedia(item);
return; return;
} }
@ -100,7 +125,7 @@ document.addEventListener('viewshow', function (e) {
} }
}, true); }, true);
Events.on(playbackManager, 'playbackstart', function (e, player) { Events.on(playbackManager, 'playbackstart', (_e, player) => {
const item = playbackManager.currentItem(player); const item = playbackManager.currentItem(player);
// User played something manually // User played something manually
if (currentThemeIds.indexOf(item.Id) == -1) { if (currentThemeIds.indexOf(item.Id) == -1) {

View file

@ -5,7 +5,6 @@ import IconButton from '@mui/material/IconButton';
import Toolbar from '@mui/material/Toolbar'; import Toolbar from '@mui/material/Toolbar';
import Tooltip from '@mui/material/Tooltip'; import Tooltip from '@mui/material/Tooltip';
import React, { FC, ReactNode } from 'react'; import React, { FC, ReactNode } from 'react';
import { useLocation } from 'react-router-dom';
import { appRouter } from 'components/router/appRouter'; import { appRouter } from 'components/router/appRouter';
import { useApi } from 'hooks/useApi'; import { useApi } from 'hooks/useApi';
@ -17,7 +16,8 @@ interface AppToolbarProps {
buttons?: ReactNode buttons?: ReactNode
isDrawerAvailable: boolean isDrawerAvailable: boolean
isDrawerOpen: boolean isDrawerOpen: boolean
onDrawerButtonClick: (event: React.MouseEvent<HTMLElement>) => void onDrawerButtonClick?: (event: React.MouseEvent<HTMLElement>) => void,
isUserMenuAvailable?: boolean
} }
const onBackButtonClick = () => { const onBackButtonClick = () => {
@ -32,17 +32,14 @@ const AppToolbar: FC<AppToolbarProps> = ({
children, children,
isDrawerAvailable, isDrawerAvailable,
isDrawerOpen, isDrawerOpen,
onDrawerButtonClick onDrawerButtonClick = () => { /* no-op */ },
isUserMenuAvailable = true
}) => { }) => {
const { user } = useApi(); const { user } = useApi();
const isUserLoggedIn = Boolean(user); const isUserLoggedIn = Boolean(user);
const currentLocation = useLocation();
const isBackButtonAvailable = appRouter.canGoBack(); const isBackButtonAvailable = appRouter.canGoBack();
// Handles a specific case to hide the user menu on the select server page while authenticated
const isUserMenuAvailable = currentLocation.pathname !== '/selectserver.html';
return ( return (
<Toolbar <Toolbar
variant='dense' variant='dense'
@ -84,16 +81,14 @@ const AppToolbar: FC<AppToolbarProps> = ({
{children} {children}
{isUserLoggedIn && isUserMenuAvailable && ( <Box sx={{ display: 'flex', flexGrow: 1, justifyContent: 'flex-end' }}>
<> {buttons}
<Box sx={{ display: 'flex', flexGrow: 1, justifyContent: 'flex-end' }}> </Box>
{buttons}
</Box>
<Box sx={{ flexGrow: 0 }}> {isUserLoggedIn && isUserMenuAvailable && (
<UserMenuButton /> <Box sx={{ flexGrow: 0 }}>
</Box> <UserMenuButton />
</> </Box>
)} )}
</Toolbar> </Toolbar>
); );

View file

@ -1,4 +1,4 @@
<div id="encodingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage withTabs"> <div id="encodingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage">
<div> <div>
<div class="content-primary"> <div class="content-primary">
<form class="encodingSettingsForm"> <form class="encodingSettingsForm">

View file

@ -2,7 +2,6 @@ import 'jquery';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';
import globalize from '../../scripts/globalize'; import globalize from '../../scripts/globalize';
import dom from '../../scripts/dom'; import dom from '../../scripts/dom';
import libraryMenu from '../../scripts/libraryMenu';
import Dashboard from '../../utils/dashboard'; import Dashboard from '../../utils/dashboard';
import alert from '../../components/alert'; import alert from '../../components/alert';
@ -167,22 +166,6 @@ function setDecodingCodecsVisible(context, value) {
} }
} }
function getTabs() {
return [{
href: '#/dashboard/playback/transcoding',
name: globalize.translate('Transcoding')
}, {
href: '#/dashboard/playback/resume',
name: globalize.translate('ButtonResume')
}, {
href: '#/dashboard/playback/streaming',
name: globalize.translate('TabStreaming')
}, {
href: '#/dashboard/playback/trickplay',
name: globalize.translate('Trickplay')
}];
}
let systemInfo; let systemInfo;
function getSystemInfo() { function getSystemInfo() {
return systemInfo ? Promise.resolve(systemInfo) : ApiClient.getPublicSystemInfo().then( return systemInfo ? Promise.resolve(systemInfo) : ApiClient.getPublicSystemInfo().then(
@ -292,7 +275,6 @@ $(document).on('pageinit', '#encodingSettingsPage', function () {
$('.encodingSettingsForm').off('submit', onSubmit).on('submit', onSubmit); $('.encodingSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#encodingSettingsPage', function () { }).on('pageshow', '#encodingSettingsPage', function () {
loading.show(); loading.show();
libraryMenu.setTabs('playback', 0, getTabs);
const page = this; const page = this;
ApiClient.getNamedConfiguration('encoding').then(function (config) { ApiClient.getNamedConfiguration('encoding').then(function (config) {
ApiClient.getSystemInfo().then(function (fetchedSystemInfo) { ApiClient.getSystemInfo().then(function (fetchedSystemInfo) {

View file

@ -83,7 +83,11 @@
<div class="verticalSection"> <div class="verticalSection">
<h2>${HeaderPerformance}</h2> <h2>${HeaderPerformance}</h2>
<div class="inputContainer"> <div class="inputContainer">
<input is="emby-input" id="txtParallelImageEncodingLimit" label="${LabelParallelImageEncodingLimit}" type="number" pattern="[0-9]*" min="0" step="1" /> <input is="emby-input" id="txtLibraryScanFanoutConcurrency" label="${LibraryScanFanoutConcurrency}" placeholder="0" type="number" pattern="[0-9]*" min="0" step="1" />
<div class="fieldDescription">${LibraryScanFanoutConcurrencyHelp}</div>
</div>
<div class="inputContainer">
<input is="emby-input" id="txtParallelImageEncodingLimit" label="${LabelParallelImageEncodingLimit}" placeholder="0" type="number" pattern="[0-9]*" min="0" step="1" />
<div class="fieldDescription">${LabelParallelImageEncodingLimitHelp}</div> <div class="fieldDescription">${LabelParallelImageEncodingLimitHelp}</div>
</div> </div>
</div> </div>

View file

@ -19,6 +19,7 @@ function loadPage(page, config, languageOptions, systemInfo) {
$('#selectLocalizationLanguage', page).html(languageOptions.map(function (language) { $('#selectLocalizationLanguage', page).html(languageOptions.map(function (language) {
return '<option value="' + language.Value + '">' + language.Name + '</option>'; return '<option value="' + language.Value + '">' + language.Name + '</option>';
})).val(config.UICulture); })).val(config.UICulture);
page.querySelector('#txtLibraryScanFanoutConcurrency').value = config.LibraryScanFanoutConcurrency || '';
page.querySelector('#txtParallelImageEncodingLimit').value = config.ParallelImageEncodingLimit || ''; page.querySelector('#txtParallelImageEncodingLimit').value = config.ParallelImageEncodingLimit || '';
loading.hide(); loading.hide();
@ -35,6 +36,7 @@ function onSubmit() {
config.MetadataPath = $('#txtMetadataPath', form).val(); config.MetadataPath = $('#txtMetadataPath', form).val();
config.MetadataNetworkPath = $('#txtMetadataNetworkPath', form).val(); config.MetadataNetworkPath = $('#txtMetadataNetworkPath', form).val();
config.QuickConnectAvailable = form.querySelector('#chkQuickConnectAvailable').checked; config.QuickConnectAvailable = form.querySelector('#chkQuickConnectAvailable').checked;
config.LibraryScanFanoutConcurrency = parseInt(form.querySelector('#txtLibraryScanFanoutConcurrency').value || '0', 10);
config.ParallelImageEncodingLimit = parseInt(form.querySelector('#txtParallelImageEncodingLimit').value || '0', 10); config.ParallelImageEncodingLimit = parseInt(form.querySelector('#txtParallelImageEncodingLimit').value || '0', 10);
ApiClient.updateServerConfiguration(config).then(function() { ApiClient.updateServerConfiguration(config).then(function() {

View file

@ -1,4 +1,4 @@
<div id="mediaLibraryPage" data-role="page" class="page type-interior mediaLibraryPage librarySectionPage withTabs fullWidthContent"> <div id="mediaLibraryPage" data-role="page" class="page type-interior mediaLibraryPage librarySectionPage fullWidthContent">
<div> <div>
<div class="content-primary"> <div class="content-primary">
<div class="padded-top padded-bottom"> <div class="padded-top padded-bottom">

View file

@ -2,7 +2,6 @@ import escapeHtml from 'escape-html';
import 'jquery'; import 'jquery';
import taskButton from '../../scripts/taskbutton'; import taskButton from '../../scripts/taskbutton';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize'; import globalize from '../../scripts/globalize';
import dom from '../../scripts/dom'; import dom from '../../scripts/dom';
import imageHelper from '../../utils/image'; import imageHelper from '../../utils/image';
@ -358,22 +357,6 @@ function getVirtualFolderHtml(page, virtualFolder, index) {
return html; return html;
} }
function getTabs() {
return [{
href: '#/dashboard/libraries',
name: globalize.translate('HeaderLibraries')
}, {
href: '#/dashboard/libraries/display',
name: globalize.translate('Display')
}, {
href: '#/dashboard/libraries/metadata',
name: globalize.translate('Metadata')
}, {
href: '#/dashboard/libraries/nfo',
name: globalize.translate('TabNfoSettings')
}];
}
window.WizardLibraryPage = { window.WizardLibraryPage = {
next: function () { next: function () {
Dashboard.navigate('wizardsettings.html'); Dashboard.navigate('wizardsettings.html');
@ -383,8 +366,6 @@ pageClassOn('pageshow', 'mediaLibraryPage', function () {
reloadLibrary(this); reloadLibrary(this);
}); });
pageIdOn('pageshow', 'mediaLibraryPage', function () { pageIdOn('pageshow', 'mediaLibraryPage', function () {
libraryMenu.setTabs('librarysetup', 0, getTabs);
const page = this; const page = this;
taskButton({ taskButton({
mode: 'on', mode: 'on',

View file

@ -1,4 +1,4 @@
<div id="libraryDisplayPage" data-role="page" class="page type-interior librarySectionPage withTabs"> <div id="libraryDisplayPage" data-role="page" class="page type-interior librarySectionPage">
<div> <div>
<div class="content-primary"> <div class="content-primary">
<form> <form>

View file

@ -1,26 +1,8 @@
import globalize from '../../scripts/globalize';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import '../../elements/emby-checkbox/emby-checkbox'; import '../../elements/emby-checkbox/emby-checkbox';
import '../../elements/emby-button/emby-button'; import '../../elements/emby-button/emby-button';
import Dashboard from '../../utils/dashboard'; import Dashboard from '../../utils/dashboard';
function getTabs() {
return [{
href: '#/dashboard/libraries',
name: globalize.translate('HeaderLibraries')
}, {
href: '#/dashboard/libraries/display',
name: globalize.translate('Display')
}, {
href: '#/dashboard/libraries/metadata',
name: globalize.translate('Metadata')
}, {
href: '#/dashboard/libraries/nfo',
name: globalize.translate('TabNfoSettings')
}];
}
export default function(view) { export default function(view) {
function loadData() { function loadData() {
ApiClient.getServerConfiguration().then(function(config) { ApiClient.getServerConfiguration().then(function(config) {
@ -57,7 +39,6 @@ export default function(view) {
}); });
view.addEventListener('viewshow', function() { view.addEventListener('viewshow', function() {
libraryMenu.setTabs('librarysetup', 1, getTabs);
loadData(); loadData();
ApiClient.getSystemInfo().then(function(info) { ApiClient.getSystemInfo().then(function(info) {
if (info.OperatingSystem === 'Windows') { if (info.OperatingSystem === 'Windows') {

View file

@ -3,7 +3,6 @@ import { ImageResolution } from '@jellyfin/sdk/lib/generated-client/models/image
import 'jquery'; import 'jquery';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize'; import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard'; import Dashboard from '../../utils/dashboard';
@ -86,26 +85,9 @@ function onSubmit() {
return false; return false;
} }
function getTabs() {
return [{
href: '#/dashboard/libraries',
name: globalize.translate('HeaderLibraries')
}, {
href: '#/dashboard/libraries/display',
name: globalize.translate('Display')
}, {
href: '#/dashboard/libraries/metadata',
name: globalize.translate('Metadata')
}, {
href: '#/dashboard/libraries/nfo',
name: globalize.translate('TabNfoSettings')
}];
}
$(document).on('pageinit', '#metadataImagesConfigurationPage', function() { $(document).on('pageinit', '#metadataImagesConfigurationPage', function() {
$('.metadataImagesConfigurationForm').off('submit', onSubmit).on('submit', onSubmit); $('.metadataImagesConfigurationForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#metadataImagesConfigurationPage', function() { }).on('pageshow', '#metadataImagesConfigurationPage', function() {
libraryMenu.setTabs('metadata', 2, getTabs);
loading.show(); loading.show();
loadPage(this); loadPage(this);
}); });

View file

@ -1,4 +1,4 @@
<div id="metadataImagesConfigurationPage" data-role="page" class="page type-interior metadataConfigurationPage withTabs"> <div id="metadataImagesConfigurationPage" data-role="page" class="page type-interior metadataConfigurationPage">
<div> <div>

View file

@ -1,4 +1,4 @@
<div id="metadataNfoPage" data-role="page" class="page type-interior metadataConfigurationPage withTabs"> <div id="metadataNfoPage" data-role="page" class="page type-interior metadataConfigurationPage">
<div> <div>

View file

@ -1,7 +1,6 @@
import escapeHtml from 'escape-html'; import escapeHtml from 'escape-html';
import 'jquery'; import 'jquery';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize'; import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard'; import Dashboard from '../../utils/dashboard';
import alert from '../../components/alert'; import alert from '../../components/alert';
@ -44,27 +43,10 @@ function showConfirmMessage() {
}); });
} }
function getTabs() {
return [{
href: '#/dashboard/libraries',
name: globalize.translate('HeaderLibraries')
}, {
href: '#/dashboard/libraries/display',
name: globalize.translate('Display')
}, {
href: '#/dashboard/libraries/metadata',
name: globalize.translate('Metadata')
}, {
href: '#/dashboard/libraries/nfo',
name: globalize.translate('TabNfoSettings')
}];
}
const metadataKey = 'xbmcmetadata'; const metadataKey = 'xbmcmetadata';
$(document).on('pageinit', '#metadataNfoPage', function () { $(document).on('pageinit', '#metadataNfoPage', function () {
$('.metadataNfoForm').off('submit', onSubmit).on('submit', onSubmit); $('.metadataNfoForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#metadataNfoPage', function () { }).on('pageshow', '#metadataNfoPage', function () {
libraryMenu.setTabs('metadata', 3, getTabs);
loading.show(); loading.show();
const page = this; const page = this;
const promise1 = ApiClient.getUsers(); const promise1 = ApiClient.getUsers();

View file

@ -1,4 +1,4 @@
<div id="playbackConfigurationPage" data-role="page" class="page type-interior playbackConfigurationPage withTabs"> <div id="playbackConfigurationPage" data-role="page" class="page type-interior playbackConfigurationPage">
<div> <div>
<div class="content-primary"> <div class="content-primary">
<form class="playbackConfigurationForm"> <form class="playbackConfigurationForm">

View file

@ -1,7 +1,5 @@
import 'jquery'; import 'jquery';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard'; import Dashboard from '../../utils/dashboard';
function loadPage(page, config) { function loadPage(page, config) {
@ -29,27 +27,10 @@ function onSubmit() {
return false; return false;
} }
function getTabs() {
return [{
href: '#/dashboard/playback/transcoding',
name: globalize.translate('Transcoding')
}, {
href: '#/dashboard/playback/resume',
name: globalize.translate('ButtonResume')
}, {
href: '#/dashboard/playback/streaming',
name: globalize.translate('TabStreaming')
}, {
href: '#/dashboard/playback/trickplay',
name: globalize.translate('Trickplay')
}];
}
$(document).on('pageinit', '#playbackConfigurationPage', function () { $(document).on('pageinit', '#playbackConfigurationPage', function () {
$('.playbackConfigurationForm').off('submit', onSubmit).on('submit', onSubmit); $('.playbackConfigurationForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#playbackConfigurationPage', function () { }).on('pageshow', '#playbackConfigurationPage', function () {
loading.show(); loading.show();
libraryMenu.setTabs('playback', 1, getTabs);
const page = this; const page = this;
ApiClient.getServerConfiguration().then(function (config) { ApiClient.getServerConfiguration().then(function (config) {
loadPage(page, config); loadPage(page, config);

View file

@ -1,4 +1,4 @@
<div id="pluginCatalogPage" data-role="page" class="page type-interior pluginConfigurationPage withTabs fullWidthContent"> <div id="pluginCatalogPage" data-role="page" class="page type-interior pluginConfigurationPage fullWidthContent">
<div> <div>
<div class="content-primary"> <div class="content-primary">
<div class="inputContainer"> <div class="inputContainer">

View file

@ -1,7 +1,6 @@
import escapeHTML from 'escape-html'; import escapeHTML from 'escape-html';
import loading from '../../../../components/loading/loading'; import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import globalize from '../../../../scripts/globalize'; import globalize from '../../../../scripts/globalize';
import '../../../../components/cardbuilder/card.scss'; import '../../../../components/cardbuilder/card.scss';
import '../../../../elements/emby-button/emby-button'; import '../../../../elements/emby-button/emby-button';
@ -159,22 +158,8 @@ function getPluginHtml(plugin, options, installedPlugins) {
return html; return html;
} }
function getTabs() {
return [{
href: '#/dashboard/plugins',
name: globalize.translate('TabMyPlugins')
}, {
href: '#/dashboard/plugins/catalog',
name: globalize.translate('TabCatalog')
}, {
href: '#/dashboard/plugins/repositories',
name: globalize.translate('TabRepositories')
}];
}
export default function (view) { export default function (view) {
view.addEventListener('viewshow', function () { view.addEventListener('viewshow', function () {
libraryMenu.setTabs('plugins', 1, getTabs);
reloadList(this); reloadList(this);
}); });
} }

View file

@ -1,4 +1,4 @@
<div id="pluginsPage" data-role="page" class="page type-interior pluginConfigurationPage withTabs fullWidthContent"> <div id="pluginsPage" data-role="page" class="page type-interior pluginConfigurationPage fullWidthContent">
<div> <div>
<div class="content-primary"> <div class="content-primary">
<div class="inputContainer"> <div class="inputContainer">

View file

@ -1,5 +1,4 @@
import loading from '../../../../components/loading/loading'; import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import dom from '../../../../scripts/dom'; import dom from '../../../../scripts/dom';
import globalize from '../../../../scripts/globalize'; import globalize from '../../../../scripts/globalize';
import '../../../../components/cardbuilder/card.scss'; import '../../../../components/cardbuilder/card.scss';
@ -219,19 +218,6 @@ function reloadList(page) {
}); });
} }
function getTabs() {
return [{
href: '#/dashboard/plugins',
name: globalize.translate('TabMyPlugins')
}, {
href: '#/dashboard/plugins/catalog',
name: globalize.translate('TabCatalog')
}, {
href: '#/dashboard/plugins/repositories',
name: globalize.translate('TabRepositories')
}];
}
function onInstalledPluginsClick(e) { function onInstalledPluginsClick(e) {
if (dom.parentWithClass(e.target, 'noConfigPluginCard')) { if (dom.parentWithClass(e.target, 'noConfigPluginCard')) {
showNoConfigurationMessage(); showNoConfigurationMessage();
@ -257,7 +243,6 @@ function onFilterType(page, searchBar) {
} }
pageIdOn('pageshow', 'pluginsPage', function () { pageIdOn('pageshow', 'pluginsPage', function () {
libraryMenu.setTabs('plugins', 0, getTabs);
reloadList(this); reloadList(this);
}); });

View file

@ -1,4 +1,4 @@
<div id="repositories" data-role="page" class="page type-interior withTabs fullWidthContent"> <div id="repositories" data-role="page" class="page type-interior fullWidthContent">
<div> <div>
<div class="content-primary"> <div class="content-primary">
<div class="sectionTitleContainer flex align-items-center"> <div class="sectionTitleContainer flex align-items-center">

View file

@ -1,5 +1,4 @@
import loading from '../../../../components/loading/loading'; import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import globalize from '../../../../scripts/globalize'; import globalize from '../../../../scripts/globalize';
import dialogHelper from '../../../../components/dialogHelper/dialogHelper'; import dialogHelper from '../../../../components/dialogHelper/dialogHelper';
import confirm from '../../../../components/confirm/confirm'; import confirm from '../../../../components/confirm/confirm';
@ -103,22 +102,8 @@ function getRepositoryElement(repository) {
return listItem; return listItem;
} }
function getTabs() {
return [{
href: '#/dashboard/plugins',
name: globalize.translate('TabMyPlugins')
}, {
href: '#/dashboard/plugins/catalog',
name: globalize.translate('TabCatalog')
}, {
href: '#/dashboard/plugins/repositories',
name: globalize.translate('TabRepositories')
}];
}
export default function(view) { export default function(view) {
view.addEventListener('viewshow', function () { view.addEventListener('viewshow', function () {
libraryMenu.setTabs('plugins', 2, getTabs);
reloadList(this); reloadList(this);
const save = this; const save = this;

View file

@ -20,7 +20,7 @@
</div> </div>
</div> </div>
</div> </div>
<div data-role="popup" id="popupAddTrigger" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;"> <div data-role="popup" id="popupAddTrigger" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%; z-index: 999999;">
<form class="addTriggerForm" style="padding:1em;"> <form class="addTriggerForm" style="padding:1em;">
<div class="ui-bar-a"> <div class="ui-bar-a">
<h3>${ButtonAddScheduledTaskTrigger}</h3> <h3>${ButtonAddScheduledTaskTrigger}</h3>

View file

@ -1,4 +1,4 @@
<div id="streamingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage withTabs"> <div id="streamingSettingsPage" data-role="page" class="page type-interior playbackConfigurationPage">
<div> <div>
<div class="content-primary"> <div class="content-primary">
<form class="streamingSettingsForm"> <form class="streamingSettingsForm">

View file

@ -1,7 +1,5 @@
import 'jquery'; import 'jquery';
import libraryMenu from '../../scripts/libraryMenu';
import loading from '../../components/loading/loading'; import loading from '../../components/loading/loading';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard'; import Dashboard from '../../utils/dashboard';
function loadPage(page, config) { function loadPage(page, config) {
@ -20,27 +18,10 @@ function onSubmit() {
return false; return false;
} }
function getTabs() {
return [{
href: '#/dashboard/playback/transcoding',
name: globalize.translate('Transcoding')
}, {
href: '#/dashboard/playback/resume',
name: globalize.translate('ButtonResume')
}, {
href: '#/dashboard/playback/streaming',
name: globalize.translate('TabStreaming')
}, {
href: '#/dashboard/playback/trickplay',
name: globalize.translate('Trickplay')
}];
}
$(document).on('pageinit', '#streamingSettingsPage', function () { $(document).on('pageinit', '#streamingSettingsPage', function () {
$('.streamingSettingsForm').off('submit', onSubmit).on('submit', onSubmit); $('.streamingSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#streamingSettingsPage', function () { }).on('pageshow', '#streamingSettingsPage', function () {
loading.show(); loading.show();
libraryMenu.setTabs('playback', 2, getTabs);
const page = this; const page = this;
ApiClient.getServerConfiguration().then(function (config) { ApiClient.getServerConfiguration().then(function (config) {
loadPage(page, config); loadPage(page, config);

View file

@ -175,14 +175,14 @@
<div id="additionalPartsCollapsible" class="verticalSection detailVerticalSection hide"> <div id="additionalPartsCollapsible" class="verticalSection detailVerticalSection hide">
<h2 class="sectionTitle sectionTitle-cards padded-right">${HeaderAdditionalParts}</h2> <h2 class="sectionTitle sectionTitle-cards padded-right">${HeaderAdditionalParts}</h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true"> <div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale no-padding" data-centerfocus="true">
<div id="additionalPartsContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div> <div id="additionalPartsContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div>
</div> </div>
</div> </div>
<div class="verticalSection detailVerticalSection moreFromSeasonSection hide"> <div class="verticalSection detailVerticalSection moreFromSeasonSection hide">
<h2 class="sectionTitle sectionTitle-cards padded-right"></h2> <h2 class="sectionTitle sectionTitle-cards padded-right"></h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true"> <div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale no-padding" data-centerfocus="true">
<div is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div> <div is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div>
</div> </div>
</div> </div>
@ -194,21 +194,21 @@
<div class="verticalSection detailVerticalSection moreFromArtistSection hide"> <div class="verticalSection detailVerticalSection moreFromArtistSection hide">
<h2 class="sectionTitle sectionTitle-cards padded-right"></h2> <h2 class="sectionTitle sectionTitle-cards padded-right"></h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true"> <div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale no-padding" data-centerfocus="true">
<div is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div> <div is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div>
</div> </div>
</div> </div>
<div id="castCollapsible" class="verticalSection detailVerticalSection hide"> <div id="castCollapsible" class="verticalSection detailVerticalSection hide">
<h2 id="peopleHeader" class="sectionTitle sectionTitle-cards padded-right">${HeaderCastAndCrew}</h2> <h2 id="peopleHeader" class="sectionTitle sectionTitle-cards padded-right">${HeaderCastAndCrew}</h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true"> <div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale no-padding" data-centerfocus="true">
<div id="castContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div> <div id="castContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div>
</div> </div>
</div> </div>
<div id="guestCastCollapsible" class="verticalSection detailVerticalSection hide"> <div id="guestCastCollapsible" class="verticalSection detailVerticalSection hide">
<h2 id="guestCastHeader" class="sectionTitle sectionTitle-cards padded-right">${HeaderGuestCast}</h2> <h2 id="guestCastHeader" class="sectionTitle sectionTitle-cards padded-right">${HeaderGuestCast}</h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true"> <div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale no-padding" data-centerfocus="true">
<div id="guestCastContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div> <div id="guestCastContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div>
</div> </div>
</div> </div>
@ -220,28 +220,28 @@
<div id="specialsCollapsible" class="verticalSection detailVerticalSection hide"> <div id="specialsCollapsible" class="verticalSection detailVerticalSection hide">
<h2 class="sectionTitle sectionTitle-cards padded-right">${SpecialFeatures}</h2> <h2 class="sectionTitle sectionTitle-cards padded-right">${SpecialFeatures}</h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true"> <div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale no-padding" data-centerfocus="true">
<div id="specialsContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div> <div id="specialsContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div>
</div> </div>
</div> </div>
<div id="musicVideosCollapsible" class="verticalSection detailVerticalSection hide"> <div id="musicVideosCollapsible" class="verticalSection detailVerticalSection hide">
<h2 class="sectionTitle sectionTitle-cards padded-right">${MusicVideos}</h2> <h2 class="sectionTitle sectionTitle-cards padded-right">${MusicVideos}</h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true"> <div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale no-padding" data-centerfocus="true">
<div id="musicVideosContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div> <div id="musicVideosContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div>
</div> </div>
</div> </div>
<div id="scenesCollapsible" class="verticalSection detailVerticalSection verticalSection-extrabottompadding hide"> <div id="scenesCollapsible" class="verticalSection detailVerticalSection verticalSection-extrabottompadding hide">
<h2 class="sectionTitle sectionTitle-cards padded-right">${HeaderScenes}</h2> <h2 class="sectionTitle sectionTitle-cards padded-right">${HeaderScenes}</h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true"> <div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale no-padding" data-centerfocus="true">
<div id="scenesContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div> <div id="scenesContent" is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer"></div>
</div> </div>
</div> </div>
<div id="similarCollapsible" class="verticalSection detailVerticalSection verticalSection-extrabottompadding hide"> <div id="similarCollapsible" class="verticalSection detailVerticalSection verticalSection-extrabottompadding hide">
<h2 class="sectionTitle sectionTitle-cards padded-right">${HeaderMoreLikeThis}</h2> <h2 class="sectionTitle sectionTitle-cards padded-right">${HeaderMoreLikeThis}</h2>
<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true"> <div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale no-padding" data-centerfocus="true">
<div is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer similarContent"></div> <div is="emby-itemscontainer" class="scrollSlider focuscontainer-x itemsContainer similarContent"></div>
</div> </div>
</div> </div>

View file

@ -1,3 +1,4 @@
import { PersonKind } from '@jellyfin/sdk/lib/generated-client/models/person-kind';
import { intervalToDuration } from 'date-fns'; import { intervalToDuration } from 'date-fns';
import DOMPurify from 'dompurify'; import DOMPurify from 'dompurify';
import escapeHtml from 'escape-html'; import escapeHtml from 'escape-html';
@ -822,8 +823,18 @@ function setInitialCollapsibleState(page, item, apiClient, context, user) {
page.querySelector('#specialsCollapsible').classList.add('hide'); page.querySelector('#specialsCollapsible').classList.add('hide');
} }
renderCast(page, item); const cast = [];
renderGuestCast(page, item); const guestCast = [];
(item.People || []).forEach(p => {
if (p.Type === PersonKind.GuestStar) {
guestCast.push(p);
} else {
cast.push(p);
}
});
renderCast(page, item, cast);
renderGuestCast(page, item, guestCast);
if (item.PartCount && item.PartCount > 1) { if (item.PartCount && item.PartCount > 1) {
page.querySelector('#additionalPartsCollapsible').classList.remove('hide'); page.querySelector('#additionalPartsCollapsible').classList.remove('hide');
@ -1803,11 +1814,7 @@ function renderSpecials(page, item, user) {
}); });
} }
function renderCast(page, item) { function renderCast(page, item, people) {
const people = (item.People || []).filter(function (p) {
return p.Type === 'Actor';
});
if (!people.length) { if (!people.length) {
page.querySelector('#castCollapsible').classList.add('hide'); page.querySelector('#castCollapsible').classList.add('hide');
return; return;
@ -1827,9 +1834,7 @@ function renderCast(page, item) {
}); });
} }
function renderGuestCast(page, item) { function renderGuestCast(page, item, people) {
const people = (item.People || []).filter(p => p.Type === 'GuestStar');
if (!people.length) { if (!people.length) {
page.querySelector('#guestCastCollapsible').classList.add('hide'); page.querySelector('#guestCastCollapsible').classList.add('hide');
return; return;

View file

@ -28,6 +28,7 @@ import LibraryMenu from '../../../scripts/libraryMenu';
import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components/backdrop/backdrop'; import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components/backdrop/backdrop';
import { pluginManager } from '../../../components/pluginManager'; import { pluginManager } from '../../../components/pluginManager';
import { PluginType } from '../../../types/plugin.ts'; import { PluginType } from '../../../types/plugin.ts';
import { EventType } from 'types/eventType';
const TICKS_PER_MINUTE = 600000000; const TICKS_PER_MINUTE = 600000000;
const TICKS_PER_SECOND = 10000000; const TICKS_PER_SECOND = 10000000;
@ -306,12 +307,14 @@ export default function (view) {
let mouseIsDown = false; let mouseIsDown = false;
function showOsd(focusElement) { function showOsd(focusElement) {
Events.trigger(document, EventType.SHOW_VIDEO_OSD, [ true ]);
slideDownToShow(headerElement); slideDownToShow(headerElement);
showMainOsdControls(focusElement); showMainOsdControls(focusElement);
resetIdle(); resetIdle();
} }
function hideOsd() { function hideOsd() {
Events.trigger(document, EventType.SHOW_VIDEO_OSD, [ false ]);
slideUpToHide(headerElement); slideUpToHide(headerElement);
hideMainOsdControls(); hideMainOsdControls();
mouseManager.hideCursor(); mouseManager.hideCursor();

View file

@ -1,6 +1,8 @@
import type { import {
LibraryUpdateInfo MediaType,
type LibraryUpdateInfo
} from '@jellyfin/sdk/lib/generated-client'; } from '@jellyfin/sdk/lib/generated-client';
import { ApiClient } from 'jellyfin-apiclient';
import React, { type FC, useCallback, useEffect, useRef } from 'react'; import React, { type FC, useCallback, useEffect, useRef } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
@ -20,6 +22,7 @@ import MultiSelect from 'components/multiSelect/multiSelect';
import loading from 'components/loading/loading'; import loading from 'components/loading/loading';
import focusManager from 'components/focusManager'; import focusManager from 'components/focusManager';
import type { ParentId } from 'types/library'; import type { ParentId } from 'types/library';
import type { PlaybackStopInfo } from 'types/playbackStopInfo';
function disableEvent(e: MouseEvent) { function disableEvent(e: MouseEvent) {
e.preventDefault(); e.preventDefault();
@ -221,7 +224,7 @@ const ItemsContainer: FC<ItemsContainerProps> = ({
); );
const onLibraryChanged = useCallback( const onLibraryChanged = useCallback(
(_e: Event, apiClient, data: LibraryUpdateInfo) => { (_e: Event, _apiClient: ApiClient, data: LibraryUpdateInfo) => {
if (eventsToMonitor.includes('seriestimers') || eventsToMonitor.includes('timers')) { if (eventsToMonitor.includes('seriestimers') || eventsToMonitor.includes('timers')) {
// yes this is an assumption // yes this is an assumption
return; return;
@ -253,12 +256,12 @@ const ItemsContainer: FC<ItemsContainerProps> = ({
); );
const onPlaybackStopped = useCallback( const onPlaybackStopped = useCallback(
(_e: Event, apiClient, stopInfo) => { (_e: Event, stopInfo: PlaybackStopInfo) => {
const state = stopInfo.state; const state = stopInfo.state;
if ( if (
state.NowPlayingItem state.NowPlayingItem
&& state.NowPlayingItem.MediaType === 'Video' && state.NowPlayingItem.MediaType === MediaType.Video
) { ) {
if (eventsToMonitor.includes('videoplayback')) { if (eventsToMonitor.includes('videoplayback')) {
notifyRefreshNeeded(true); notifyRefreshNeeded(true);
@ -266,8 +269,8 @@ const ItemsContainer: FC<ItemsContainerProps> = ({
} }
} else if ( } else if (
state.NowPlayingItem state.NowPlayingItem
&& state.NowPlayingItem.MediaType === 'Audio' && state.NowPlayingItem.MediaType === MediaType.Audio
&& eventsToMonitor.includes('videoplayback') && eventsToMonitor.includes('audioplayback')
) { ) {
notifyRefreshNeeded(true); notifyRefreshNeeded(true);
return; return;

View file

@ -26,3 +26,13 @@
margin-left: 1.2em; margin-left: 1.2em;
} }
} }
.no-padding {
[dir="ltr"] & {
padding-left: 0;
}
[dir="rtl"] & {
padding-right: 0;
}
}

View file

@ -138,9 +138,6 @@ function updateValues(isValueSet) {
} }
if (range.markerContainerElement) { if (range.markerContainerElement) {
if (!range.triedAddingMarkers) {
addMarkers(range);
}
updateMarkers(range, value); updateMarkers(range, value);
} }
}); });
@ -206,21 +203,6 @@ function setMarker(range, valueMarker, marker, valueProgress) {
} }
function updateMarkers(range, currentValue) { function updateMarkers(range, currentValue) {
if (range.markerInfo?.length && range.markerElements?.length) {
for (let i = 0, length = range.markerElements.length; i < length; i++) {
if (range.markerInfo.length > i) {
setMarker(range, mapFractionToValue(range, range.markerInfo[i].progress), range.markerElements[i], currentValue);
}
}
}
}
function addMarkers(range) {
range.markerInfo = [];
if (range.getMarkerInfo) {
range.markerInfo = range.getMarkerInfo();
}
function getMarkerHtml(markerInfo) { function getMarkerHtml(markerInfo) {
let markerTypeSpecificClasses = ''; let markerTypeSpecificClasses = '';
@ -236,12 +218,25 @@ function addMarkers(range) {
return `<span class="material-icons sliderMarker ${markerTypeSpecificClasses}" aria-hidden="true"></span>`; return `<span class="material-icons sliderMarker ${markerTypeSpecificClasses}" aria-hidden="true"></span>`;
} }
range.markerInfo.forEach(info => { if (range.getMarkerInfo) {
range.markerContainerElement.insertAdjacentHTML('beforeend', getMarkerHtml(info)); range.markerInfo = range.getMarkerInfo();
});
range.markerElements = range.markerContainerElement.querySelectorAll('.sliderMarker'); range.markerContainerElement.innerHTML = '';
range.triedAddingMarkers = true;
range.markerInfo.forEach(info => {
range.markerContainerElement.insertAdjacentHTML('beforeend', getMarkerHtml(info));
});
range.markerElements = range.markerContainerElement.querySelectorAll('.sliderMarker');
}
if (range.markerInfo?.length && range.markerElements?.length) {
for (let i = 0, length = range.markerElements.length; i < length; i++) {
if (range.markerInfo.length > i) {
setMarker(range, mapFractionToValue(range, range.markerInfo[i].progress), range.markerElements[i], currentValue);
}
}
}
} }
EmbySliderPrototype.attachedCallback = function () { EmbySliderPrototype.attachedCallback = function () {

View file

@ -28,36 +28,6 @@ import { LibraryViewSettings, ParentId } from 'types/library';
import { LibraryTab } from 'types/libraryTab'; import { LibraryTab } from 'types/libraryTab';
import { Section, SectionApiMethod, SectionType } from 'types/sections'; import { Section, SectionApiMethod, SectionType } from 'types/sections';
const fetchGetItem = async (
currentApi: JellyfinApiContext,
parentId: ParentId,
options?: AxiosRequestConfig
) => {
const { api, user } = currentApi;
if (api && user?.Id && parentId) {
const response = await getUserLibraryApi(api).getItem(
{
userId: user.Id,
itemId: parentId
},
{
signal: options?.signal
}
);
return response.data;
}
};
export const useGetItem = (parentId: ParentId) => {
const currentApi = useApi();
const isLivetv = parentId === 'livetv';
return useQuery({
queryKey: ['Item', parentId],
queryFn: ({ signal }) => fetchGetItem(currentApi, parentId, { signal }),
enabled: !!parentId && !isLivetv
});
};
const fetchGetItems = async ( const fetchGetItems = async (
currentApi: JellyfinApiContext, currentApi: JellyfinApiContext,
parametersOptions: ItemsApiGetItemsRequest, parametersOptions: ItemsApiGetItemsRequest,

41
src/hooks/useItem.ts Normal file
View file

@ -0,0 +1,41 @@
import type { Api } from '@jellyfin/sdk/lib/api';
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api/user-library-api';
import { useQuery } from '@tanstack/react-query';
import type { AxiosRequestConfig } from 'axios';
import { queryOptions } from 'utils/query/queryOptions';
import { useApi } from './useApi';
const fetchItem = async (
api?: Api,
userId?: string,
itemId?: string,
options?: AxiosRequestConfig
) => {
if (!api) throw new Error('No API instance available');
if (!itemId) throw new Error('No item ID provided');
const response = await getUserLibraryApi(api)
.getItem({ userId, itemId }, options);
return response.data;
};
export const getItemQuery = (
api?: Api,
userId?: string,
itemId?: string
) => queryOptions({
queryKey: [ 'User', userId, 'Items', itemId ],
queryFn: ({ signal }) => fetchItem(api, userId, itemId, { signal }),
staleTime: 1000, // 1 second
enabled: !!api && !!userId && !!itemId
});
export const useItem = (
itemId?: string
) => {
const apiContext = useApi();
const { api, user } = apiContext;
return useQuery(getItemQuery(api, user?.Id, itemId));
};

View file

@ -2,8 +2,6 @@ import { Update } from 'history';
import { useLayoutEffect, useState } from 'react'; import { useLayoutEffect, useState } from 'react';
import type { History, Router } from '@remix-run/router'; import type { History, Router } from '@remix-run/router';
const normalizePath = (pathname: string) => pathname.replace(/^!/, '');
interface UseLegacyRouterSyncProps { interface UseLegacyRouterSyncProps {
router: Router; router: Router;
history: History; history: History;
@ -20,8 +18,18 @@ export function useLegacyRouterSync({ router, history }: UseLegacyRouterSyncProp
* implementation, so we need to remove the leading `!` from the pathname. React Router already removes the * implementation, so we need to remove the leading `!` from the pathname. React Router already removes the
* hash for us. * hash for us.
*/ */
if (update.location.pathname.startsWith('!')) { if (update.location.pathname.startsWith('/!/')) {
history.replace(normalizePath(update.location.pathname), update.location.state); history.replace(
{ ...update.location, pathname: update.location.pathname.replace(/^\/!/, '') },
update.location.state);
} else if (update.location.pathname.startsWith('/!')) {
history.replace(
{ ...update.location, pathname: update.location.pathname.replace(/^\/!/, '/') },
update.location.state);
} else if (update.location.pathname.startsWith('!')) {
history.replace(
{ ...update.location, pathname: update.location.pathname.replace(/^!/, '') },
update.location.state);
} else if (!isSynced) { } else if (!isSynced) {
await router.navigate(update.location, { replace: true }); await router.navigate(update.location, { replace: true });
} }

View file

@ -12,11 +12,6 @@
<meta name="application-name" content="Jellyfin"> <meta name="application-name" content="Jellyfin">
<meta name="robots" content="noindex, nofollow, noarchive"> <meta name="robots" content="noindex, nofollow, noarchive">
<meta name="referrer" content="no-referrer"> <meta name="referrer" content="no-referrer">
<meta property="og:title" content="Jellyfin">
<meta property="og:site_name" content="Jellyfin">
<meta property="og:url" content="https://jellyfin.org">
<meta property="og:description" content="The Free Software Media System">
<meta property="og:type" content="article">
<meta id="themeColor" name="theme-color" content="#202020"> <meta id="themeColor" name="theme-color" content="#202020">
@ -116,6 +111,7 @@
z-index: 1; z-index: 1;
width: 0.8em; width: 0.8em;
padding-left: env(safe-area-inset-left); padding-left: env(safe-area-inset-left);
caret-color: transparent;
} }
[dir="ltr"] .mainDrawerHandle { [dir="ltr"] .mainDrawerHandle {

View file

@ -193,21 +193,10 @@ async function onAppReady() {
} }
} }
// Apply custom CSS
const apiClient = ServerConnections.currentApiClient(); const apiClient = ServerConnections.currentApiClient();
if (apiClient) { if (apiClient) {
const updateStyle = (css) => { const brandingCss = fetch(apiClient.getUrl('Branding/Css'))
let style = document.querySelector('#cssBranding');
if (!style) {
// Inject the branding css as a dom element in body so it will take
// precedence over other stylesheets
style = document.createElement('style');
style.id = 'cssBranding';
document.body.appendChild(style);
}
style.textContent = css;
};
const style = fetch(apiClient.getUrl('Branding/Css'))
.then(function(response) { .then(function(response) {
if (!response.ok) { if (!response.ok) {
throw new Error(response.status + ' ' + response.statusText); throw new Error(response.status + ' ' + response.statusText);
@ -219,41 +208,33 @@ async function onAppReady() {
}); });
const handleStyleChange = async () => { const handleStyleChange = async () => {
if (currentSettings.disableCustomCss()) { let style = document.querySelector('#cssBranding');
updateStyle(''); if (!style) {
} else { // Inject the branding css as a dom element in body so it will take
updateStyle(await style); // precedence over other stylesheets
style = document.createElement('style');
style.id = 'cssBranding';
document.body.appendChild(style);
} }
const localCss = currentSettings.customCss(); const css = [];
let localStyle = document.querySelector('#localCssBranding'); // Only add branding CSS when enabled
if (localCss) { if (!currentSettings.disableCustomCss()) css.push(await brandingCss);
if (!localStyle) { // Always add user CSS
// Inject the branding css as a dom element in body so it will take css.push(currentSettings.customCss());
// precedence over other stylesheets
localStyle = document.createElement('style'); style.textContent = css.join('\n');
localStyle.id = 'localCssBranding';
document.body.appendChild(localStyle);
}
localStyle.textContent = localCss;
} else if (localStyle) {
localStyle.textContent = '';
}
}; };
const handleUserChange = () => { Events.on(ServerConnections, 'localusersignedin', handleStyleChange);
handleStyleChange(); Events.on(ServerConnections, 'localusersignedout', handleStyleChange);
};
Events.on(ServerConnections, 'localusersignedin', handleUserChange);
Events.on(ServerConnections, 'localusersignedout', handleUserChange);
Events.on(currentSettings, 'change', (e, prop) => { Events.on(currentSettings, 'change', (e, prop) => {
if (prop == 'disableCustomCss' || prop == 'customCss') { if (prop == 'disableCustomCss' || prop == 'customCss') {
handleStyleChange(); handleStyleChange();
} }
}); });
style.then(updateStyle); handleStyleChange();
} }
} }

View file

@ -113,18 +113,22 @@ class HtmlAudioPlayer {
let val = options.url; let val = options.url;
console.debug('playing url: ' + val); console.debug('playing url: ' + val);
import('../../scripts/settings/userSettings').then((userSettings) => { import('../../scripts/settings/userSettings').then((userSettings) => {
if (userSettings.selectAudioNormalization() == 'TrackGain' && options.item.LUFS != null) { let normalizationGain;
const dbGain = -18 - options.item.LUFS; if (userSettings.selectAudioNormalization() == 'TrackGain') {
self.gainNode.gain.value = Math.pow(10, (dbGain / 20)); normalizationGain = options.item.NormalizationGain
console.debug('[HtmlAudioPlayer] Using track gain'); ?? options.mediaSource.albumNormalizationGain;
} else if (userSettings.selectAudioNormalization() == 'AlbumGain' && options.mediaSource.albumLUFS != null) { } else if (userSettings.selectAudioNormalization() == 'AlbumGain') {
const dbGain = -18 - options.mediaSource.albumLUFS; normalizationGain =
self.gainNode.gain.value = Math.pow(10, (dbGain / 20)); options.mediaSource.albumNormalizationGain
console.debug('[HtmlAudioPlayer] Using album gain'); ?? options.item.NormalizationGain;
}
if (normalizationGain) {
self.gainNode.gain.value = Math.pow(10, normalizationGain / 20);
} else { } else {
self.gainNode.gain.value = 1; self.gainNode.gain.value = 1;
} }
console.debug('gain:' + self.gainNode.gain.value); console.debug('gain: ' + self.gainNode.gain.value);
}).catch((err) => { }).catch((err) => {
console.error('Failed to add/change gainNode', err); console.error('Failed to add/change gainNode', err);
}); });

View file

@ -1250,14 +1250,14 @@ export class HtmlVideoPlayer {
*/ */
renderSsaAss(videoElement, track, item) { renderSsaAss(videoElement, track, item) {
const supportedFonts = ['application/vnd.ms-opentype', 'application/x-truetype-font', 'font/otf', 'font/ttf', 'font/woff', 'font/woff2']; const supportedFonts = ['application/vnd.ms-opentype', 'application/x-truetype-font', 'font/otf', 'font/ttf', 'font/woff', 'font/woff2'];
const avaliableFonts = []; const availableFonts = [];
const attachments = this._currentPlayOptions.mediaSource.MediaAttachments || []; const attachments = this._currentPlayOptions.mediaSource.MediaAttachments || [];
const apiClient = ServerConnections.getApiClient(item); const apiClient = ServerConnections.getApiClient(item);
attachments.forEach(i => { attachments.forEach(i => {
// we only require font files and ignore embedded media attachments like covers as there are cases where ffmpeg fails to extract those // we only require font files and ignore embedded media attachments like covers as there are cases where ffmpeg fails to extract those
if (supportedFonts.includes(i.MimeType)) { if (supportedFonts.includes(i.MimeType)) {
// embedded font url // embedded font url
avaliableFonts.push(apiClient.getUrl(i.DeliveryUrl)); availableFonts.push(apiClient.getUrl(i.DeliveryUrl));
} }
}); });
const fallbackFontList = apiClient.getUrl('/FallbackFont/Fonts', { const fallbackFontList = apiClient.getUrl('/FallbackFont/Fonts', {
@ -1268,7 +1268,7 @@ export class HtmlVideoPlayer {
const options = { const options = {
video: videoElement, video: videoElement,
subUrl: getTextTrackUrl(track, item), subUrl: getTextTrackUrl(track, item),
fonts: avaliableFonts, fonts: availableFonts,
workerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker.js`, workerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker.js`,
legacyWorkerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker-legacy.js`, legacyWorkerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker-legacy.js`,
onError() { onError() {
@ -1307,10 +1307,10 @@ export class HtmlVideoPlayer {
if (config.EnableFallbackFont) { if (config.EnableFallbackFont) {
apiClient.getJSON(fallbackFontList).then((fontFiles = []) => { apiClient.getJSON(fallbackFontList).then((fontFiles = []) => {
fontFiles.forEach(font => { fontFiles.forEach(font => {
const fontUrl = apiClient.getUrl(`/FallbackFont/Fonts/${font.Name}`, { const fontUrl = apiClient.getUrl(`/FallbackFont/Fonts/${encodeURIComponent(font.Name)}`, {
api_key: apiClient.accessToken() api_key: apiClient.accessToken()
}); });
avaliableFonts.push(fontUrl); availableFonts.push(fontUrl);
}); });
this.#currentAssRenderer = new SubtitlesOctopus(options); this.#currentAssRenderer = new SubtitlesOctopus(options);
}); });

View file

@ -227,14 +227,14 @@ class QueueCore {
const serverId = apiClient.serverInfo().Id; const serverId = apiClient.serverInfo().Id;
this.scheduleReadyRequestOnPlaybackStart(apiClient, 'startPlayback');
const playerWrapper = this.manager.getPlayerWrapper(); const playerWrapper = this.manager.getPlayerWrapper();
playerWrapper.localPlay({ playerWrapper.localPlay({
ids: this.getPlaylistAsItemIds(), ids: this.getPlaylistAsItemIds(),
startPositionTicks: startPositionTicks, startPositionTicks: startPositionTicks,
startIndex: this.getCurrentPlaylistIndex(), startIndex: this.getCurrentPlaylistIndex(),
serverId: serverId serverId: serverId
}).then(() => {
this.scheduleReadyRequestOnPlaybackStart(apiClient, 'startPlayback');
}).catch((error) => { }).catch((error) => {
console.error(error); console.error(error);
toast(globalize.translate('MessageSyncPlayErrorMedia')); toast(globalize.translate('MessageSyncPlayErrorMedia'));

View file

@ -94,6 +94,25 @@ function supportsAc3(videoTestElement) {
return videoTestElement.canPlayType('audio/mp4; codecs="ac-3"').replace(/no/, ''); return videoTestElement.canPlayType('audio/mp4; codecs="ac-3"').replace(/no/, '');
} }
/**
* Checks if the device supports DTS (DCA).
* @param {HTMLVideoElement} videoTestElement The video test element
* @returns {boolean|null} _true_ if the device supports DTS (DCA). _false_ if the device doesn't support DTS (DCA). _null_ if support status is unknown.
*/
function canPlayDts(videoTestElement) {
// DTS audio is not supported by Samsung TV 2018+ (Tizen 4.0+) and LG TV 2020-2022 (webOS 5.0, 6.0 and 22) models
if (browser.tizenVersion >= 4 || (browser.web0sVersion >= 5 && browser.web0sVersion < 23)) {
return false;
}
if (videoTestElement.canPlayType('video/mp4; codecs="dts-"').replace(/no/, '')
|| videoTestElement.canPlayType('video/mp4; codecs="dts+"').replace(/no/, '')) {
return true;
}
return null;
}
function supportsEac3(videoTestElement) { function supportsEac3(videoTestElement) {
if (browser.tizen || browser.web0s) { if (browser.tizen || browser.web0s) {
return true; return true;
@ -121,6 +140,15 @@ function supportsAc3InHls(videoTestElement) {
return false; return false;
} }
function supportsMp3InHls(videoTestElement) {
if (videoTestElement.canPlayType) {
return videoTestElement.canPlayType('application/x-mpegurl; codecs="avc1.64001E, mp4a.40.34"').replace(/no/, '')
|| videoTestElement.canPlayType('application/vnd.apple.mpegURL; codecs="avc1.64001E, mp4a.40.34"').replace(/no/, '');
}
return false;
}
function canPlayAudioFormat(format) { function canPlayAudioFormat(format) {
let typeString; let typeString;
@ -460,6 +488,7 @@ export default function (options) {
} }
const canPlayAacVideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, ''); const canPlayAacVideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, '');
const canPlayMp3VideoAudioInHls = supportsMp3InHls(videoTestElement);
const canPlayAc3VideoAudio = supportsAc3(videoTestElement); const canPlayAc3VideoAudio = supportsAc3(videoTestElement);
const canPlayEac3VideoAudio = supportsEac3(videoTestElement); const canPlayEac3VideoAudio = supportsEac3(videoTestElement);
const canPlayAc3VideoAudioInHls = supportsAc3InHls(videoTestElement); const canPlayAc3VideoAudioInHls = supportsAc3InHls(videoTestElement);
@ -474,12 +503,15 @@ export default function (options) {
if (supportsMp3VideoAudio) { if (supportsMp3VideoAudio) {
videoAudioCodecs.push('mp3'); videoAudioCodecs.push('mp3');
}
// PS4 fails to load HLS with mp3 audio // Safari supports mp3 with HLS, but only in mpegts container, and the supportsMp3VideoAudio will return false.
if (!browser.ps4) { if (browser.safari || (supportsMp3VideoAudio && !browser.ps4)) {
hlsInTsVideoAudioCodecs.push('mp3'); hlsInTsVideoAudioCodecs.push('mp3');
} }
// Most browsers won't support mp3 with HLS, so this is usually false, but just in case.
if (canPlayMp3VideoAudioInHls) {
hlsInFmp4VideoAudioCodecs.push('mp3'); hlsInFmp4VideoAudioCodecs.push('mp3');
} }
@ -515,14 +547,9 @@ export default function (options) {
hlsInFmp4VideoAudioCodecs.push('mp2'); hlsInFmp4VideoAudioCodecs.push('mp2');
} }
let supportsDts = options.supportsDts; let supportsDts = appSettings.enableDts() || options.supportsDts;
if (supportsDts == null) { if (supportsDts == null) {
supportsDts = browser.tizen || browser.web0sVersion || videoTestElement.canPlayType('video/mp4; codecs="dts-"').replace(/no/, '') || videoTestElement.canPlayType('video/mp4; codecs="dts+"').replace(/no/, ''); supportsDts = canPlayDts(videoTestElement);
// DTS audio is not supported by Samsung TV 2018+ (Tizen 4.0+) and LG TV 2020-2022 (webOS 5.0, 6.0 and 22) models
if (browser.tizenVersion >= 4 || (browser.web0sVersion >= 5 && browser.web0sVersion < 23)) {
supportsDts = false;
}
} }
if (supportsDts) { if (supportsDts) {
@ -535,7 +562,7 @@ export default function (options) {
videoAudioCodecs.push('pcm_s24le'); videoAudioCodecs.push('pcm_s24le');
} }
if (options.supportsTrueHd) { if (appSettings.enableTrueHd() || options.supportsTrueHd) {
videoAudioCodecs.push('truehd'); videoAudioCodecs.push('truehd');
} }

View file

@ -125,7 +125,7 @@ function renderSection(item, element, type) {
ArtistIds: '', ArtistIds: '',
AlbumArtistIds: '', AlbumArtistIds: '',
Limit: 10, Limit: 10,
SortOrder: 'Descending,Desending,Ascending', SortOrder: 'Descending,Descending,Ascending',
SortBy: 'PremiereDate,ProductionYear,SortName' SortBy: 'PremiereDate,ProductionYear,SortName'
}, { }, {
shape: 'overflowPortrait', shape: 'overflowPortrait',
@ -195,7 +195,7 @@ function renderSection(item, element, type) {
PersonTypes: '', PersonTypes: '',
ArtistIds: '', ArtistIds: '',
AlbumArtistIds: '', AlbumArtistIds: '',
SortOrder: 'Descending,Desending,Ascending', SortOrder: 'Descending,Descending,Ascending',
SortBy: 'PremiereDate,ProductionYear,Sortname' SortBy: 'PremiereDate,ProductionYear,Sortname'
}, { }, {
shape: 'overflowSquare', shape: 'overflowSquare',

View file

@ -30,6 +30,7 @@ import '../elements/emby-button/paper-icon-button-light';
import 'material-design-icons-iconfont'; import 'material-design-icons-iconfont';
import '../styles/scrollstyles.scss'; import '../styles/scrollstyles.scss';
import '../styles/flexstyles.scss'; import '../styles/flexstyles.scss';
import { EventType } from 'types/eventType';
function renderHeader() { function renderHeader() {
let html = ''; let html = '';
@ -703,6 +704,8 @@ const skinHeader = document.querySelector('.skinHeader');
let requiresUserRefresh = true; let requiresUserRefresh = true;
function setTabs (type, selectedIndex, builder) { function setTabs (type, selectedIndex, builder) {
Events.trigger(document, EventType.SET_TABS, type ? [ type, selectedIndex, builder()] : []);
import('../components/maintabsmanager').then((mainTabsManager) => { import('../components/maintabsmanager').then((mainTabsManager) => {
if (type) { if (type) {
mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () { mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () {

View file

@ -1,48 +1,74 @@
import listView from '../components/listview/listview'; import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
function getFetchPlaylistItemsFn(itemId) { import ServerConnections from 'components/ServerConnections';
import listView from 'components/listview/listview';
import { toApi } from 'utils/jellyfin-apiclient/compat';
function getFetchPlaylistItemsFn(apiClient, itemId) {
return function () { return function () {
const query = { const query = {
Fields: 'PrimaryImageAspectRatio', Fields: 'PrimaryImageAspectRatio',
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb', EnableImageTypes: 'Primary,Backdrop,Banner,Thumb',
UserId: ApiClient.getCurrentUserId() UserId: apiClient.getCurrentUserId()
}; };
return ApiClient.getJSON(ApiClient.getUrl(`Playlists/${itemId}/Items`, query)); return apiClient.getJSON(apiClient.getUrl(`Playlists/${itemId}/Items`, query));
}; };
} }
function getItemsHtmlFn(itemId) { function getItemsHtmlFn(playlistId, isEditable = false) {
return function (items) { return function (items) {
return listView.getListViewHtml({ return listView.getListViewHtml({
items: items, items,
showIndex: false, showIndex: false,
showRemoveFromPlaylist: true,
playFromHere: true, playFromHere: true,
action: 'playallfromhere', action: 'playallfromhere',
smallIcon: true, smallIcon: true,
dragHandle: true, dragHandle: isEditable,
playlistId: itemId playlistId
}); });
}; };
} }
function init(page, item) { async function init(page, item) {
const apiClient = ServerConnections.getApiClient(item.ServerId);
const api = toApi(apiClient);
let isEditable = false;
const { data } = await getPlaylistsApi(api)
.getPlaylistUser({
playlistId: item.Id,
userId: apiClient.getCurrentUserId()
})
.catch(err => {
// If a user doesn't have access, then the request will 404 and throw
console.info('[PlaylistViewer] Failed to fetch playlist permissions', err);
return { data: {} };
});
isEditable = !!data.CanEdit;
const elem = page.querySelector('#childrenContent .itemsContainer'); const elem = page.querySelector('#childrenContent .itemsContainer');
elem.classList.add('vertical-list'); elem.classList.add('vertical-list');
elem.classList.remove('vertical-wrap'); elem.classList.remove('vertical-wrap');
elem.enableDragReordering(true); elem.enableDragReordering(isEditable);
elem.fetchData = getFetchPlaylistItemsFn(item.Id); elem.fetchData = getFetchPlaylistItemsFn(apiClient, item.Id);
elem.getItemsHtml = getItemsHtmlFn(item.Id); elem.getItemsHtml = getItemsHtmlFn(item.Id, isEditable);
}
function refresh(page) {
page.querySelector('#childrenContent').classList.add('verticalSection-extrabottompadding');
page.querySelector('#childrenContent .itemsContainer').refreshItems();
} }
function render(page, item) { function render(page, item) {
if (!page.playlistInit) { if (!page.playlistInit) {
page.playlistInit = true; page.playlistInit = true;
init(page, item); init(page, item)
.finally(() => {
refresh(page);
});
} else {
refresh(page);
} }
page.querySelector('#childrenContent').classList.add('verticalSection-extrabottompadding');
page.querySelector('#childrenContent .itemsContainer').refreshItems();
} }
const PlaylistViewer = { const PlaylistViewer = {

View file

@ -132,6 +132,44 @@ class AppSettings {
return toBoolean(this.get('limitSupportedVideoResolution'), false); return toBoolean(this.get('limitSupportedVideoResolution'), false);
} }
/**
* Get or set preferred transcode audio codec in video playback.
* @param {string|undefined} val - Preferred transcode audio codec or undefined.
* @return {string} Preferred transcode audio codec.
*/
preferredTranscodeVideoAudioCodec(val) {
if (val !== undefined) {
return this.set('preferredTranscodeVideoAudioCodec', val);
}
return this.get('preferredTranscodeVideoAudioCodec') || '';
}
/**
* Get or set 'Enable DTS' state.
* @param {boolean|undefined} val - Flag to enable 'Enable DTS' or undefined.
* @return {boolean} 'Enable DTS' state.
*/
enableDts(val) {
if (val !== undefined) {
return this.set('enableDts', val.toString());
}
return toBoolean(this.get('enableDts'), false);
}
/**
* Get or set 'Enable TrueHD' state.
* @param {boolean|undefined} val - Flag to enable 'Enable TrueHD' or undefined.
* @return {boolean} 'Enable TrueHD' state.
*/
enableTrueHd(val) {
if (val !== undefined) {
return this.set('enableTrueHd', val.toString());
}
return toBoolean(this.get('enableTrueHd'), false);
}
set(name, value, userId) { set(name, value, userId) {
const currentValue = this.get(name, userId); const currentValue = this.get(name, userId);
localStorage.setItem(this.#getKey(name, userId), value); localStorage.setItem(this.#getKey(name, userId), value);

View file

@ -197,7 +197,7 @@
"HeaderSelectTranscodingPathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم لملفات التشفير البيني. يجب أن يكون هذا المجلد قابل للكتابة فيه.", "HeaderSelectTranscodingPathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم لملفات التشفير البيني. يجب أن يكون هذا المجلد قابل للكتابة فيه.",
"HeaderSendMessage": "أرسل رسالة", "HeaderSendMessage": "أرسل رسالة",
"HeaderServerSettings": "إعدادات الخادم", "HeaderServerSettings": "إعدادات الخادم",
"HeaderSetupLibrary": "ضبط مكتبة المحتوى الخاصة بك", "HeaderSetupLibrary": "ضبط مكاتب المحتوى الخاصة بك",
"HeaderSortBy": "ترتيب حسب", "HeaderSortBy": "ترتيب حسب",
"HeaderSortOrder": "تسلسل الترتيب", "HeaderSortOrder": "تسلسل الترتيب",
"HeaderSpecialEpisodeInfo": "معلومات الحلقة الخاصة", "HeaderSpecialEpisodeInfo": "معلومات الحلقة الخاصة",
@ -255,7 +255,7 @@
"LabelCollection": "المجموعة", "LabelCollection": "المجموعة",
"LabelCommunityRating": "تقييم الجمهور", "LabelCommunityRating": "تقييم الجمهور",
"LabelContentType": "نوع المحتوى", "LabelContentType": "نوع المحتوى",
"LabelCountry": "البلد", "LabelCountry": "البلد/المنطقة",
"LabelCurrentPassword": "كلمة السر الحالية", "LabelCurrentPassword": "كلمة السر الحالية",
"LabelCustomCertificatePath": "مسار شهادة SSL المخصص", "LabelCustomCertificatePath": "مسار شهادة SSL المخصص",
"LabelCustomCertificatePathHelp": "مسار ملف PKCS # 12 يحتوي على شهادة ومفتاح خاص لتمكين دعم TLS على مجال مخصص.", "LabelCustomCertificatePathHelp": "مسار ملف PKCS # 12 يحتوي على شهادة ومفتاح خاص لتمكين دعم TLS على مجال مخصص.",
@ -527,7 +527,7 @@
"MessagePasswordResetForUsers": "تم إعادة تعيين كلمات المرور للمستخدمين التاليين. يمكنهم الآن تسجيل الدخول باستخدام رموز الـPIN التي تم استخدامها لإعادة الضبط.", "MessagePasswordResetForUsers": "تم إعادة تعيين كلمات المرور للمستخدمين التاليين. يمكنهم الآن تسجيل الدخول باستخدام رموز الـPIN التي تم استخدامها لإعادة الضبط.",
"MessagePleaseEnsureInternetMetadata": "الرجاء التأكد من أن إمكانية إنزال واصفات البيانات من الإنترنت ممكنة.", "MessagePleaseEnsureInternetMetadata": "الرجاء التأكد من أن إمكانية إنزال واصفات البيانات من الإنترنت ممكنة.",
"MessagePluginConfigurationRequiresLocalAccess": "لضبط هذا البرنامج المساعد ، يرجى تسجيل الدخول إلى الخادم المحلي الخاص بك مباشرة.", "MessagePluginConfigurationRequiresLocalAccess": "لضبط هذا البرنامج المساعد ، يرجى تسجيل الدخول إلى الخادم المحلي الخاص بك مباشرة.",
"MessagePluginInstallDisclaimer": "تحذير: تنصيب الملحقات التي بناها أعضاء مجتمع Jellyfin هي طريقة رائعة لتحسين متعة استخدام Jellyfin عن طريق أضافة مزايا وخدمات الجديدة. ولكن تنصيب ملحقات من مصادر ثالثة تحمل بعظ الخطورة.\nقبل تثبيت الملحقات، نرجو أخذ العلم بالآثار التي قد تلحقها بخادم Jellyfin الخاص بك، مثل أوقات أطولة لتمشيط مكتبتك، والعمليات الخلفية الإضافية وتقليل استقرار نظامك.", "MessagePluginInstallDisclaimer": "تحذير: تنصيب مكونات إضافية من مصادر طرف ثالث تحمل بعض المخاطر. قد تتضمن بعض البرمجيات الضارة، وقد تتغير في أي وقت. فقط قم بتنصيب المكونات الإضافية من مصدر تثق به، يرجى أخذ العلم بالآثار المحتملة التي قد تلحقها، مثل أوقات أطول لتمشيط مكتبتك، والعمليات الخلفية الإضافية.",
"MessageReenableUser": "أنظر أدناه لإعادة التفعيل", "MessageReenableUser": "أنظر أدناه لإعادة التفعيل",
"MessageTheFollowingLocationWillBeRemovedFromLibrary": "مكان الوسائط التالي سيزال من مكتبة Jellyfin الخاصة بك", "MessageTheFollowingLocationWillBeRemovedFromLibrary": "مكان الوسائط التالي سيزال من مكتبة Jellyfin الخاصة بك",
"MessageUnableToConnectToServer": "لم نستطع الاتصال إلى الخادم المختار في الوقت الحالي. الرجاء التأكد من أنه يعمل ثم المحاولة مرة أخرى.", "MessageUnableToConnectToServer": "لم نستطع الاتصال إلى الخادم المختار في الوقت الحالي. الرجاء التأكد من أنه يعمل ثم المحاولة مرة أخرى.",
@ -1085,7 +1085,7 @@
"Record": "سجل", "Record": "سجل",
"RecentlyWatched": "شاهدت مؤخرا", "RecentlyWatched": "شاهدت مؤخرا",
"Rate": "معدل", "Rate": "معدل",
"QuickConnectAuthorizeSuccess": "تمت الموافقة على الطلب", "QuickConnectAuthorizeSuccess": "تمت المصادقة على الجهاز بنجاح!",
"QuickConnectAuthorizeCode": "أدخل الرمز {0} لتسجيل الدخول", "QuickConnectAuthorizeCode": "أدخل الرمز {0} لتسجيل الدخول",
"QuickConnectActivationSuccessful": "تم التفعيل بنجاح", "QuickConnectActivationSuccessful": "تم التفعيل بنجاح",
"Quality": "الجودة", "Quality": "الجودة",
@ -1553,7 +1553,7 @@
"LabelColorPrimaries": "الألوان الأساسية", "LabelColorPrimaries": "الألوان الأساسية",
"HeaderSyncPlayPlaybackSettings": "التشغيل", "HeaderSyncPlayPlaybackSettings": "التشغيل",
"HeaderNewRepository": "مستودع جديد", "HeaderNewRepository": "مستودع جديد",
"DirectPlayHelp": "الملف المصدر متوافق تمامًا مع هذا العميل ، وتستقبل الجلسة الملف بدون تعديلات.", "DirectPlayHelp": "الملف المصدر متوافق تمامًا مع هذا العميل والجلسة تستقبل الملف بدون تعديلات.",
"LabelDashboardTheme": "قالب لوحة تحكم الخادم", "LabelDashboardTheme": "قالب لوحة تحكم الخادم",
"LabelTonemappingParamHelp": "ضبط خوارزمية تعيين النغمة. القيم الموصى بها والافتراضية هي NaN. اتركه فارغًا بشكل عام.", "LabelTonemappingParamHelp": "ضبط خوارزمية تعيين النغمة. القيم الموصى بها والافتراضية هي NaN. اتركه فارغًا بشكل عام.",
"LabelTonemappingParam": "معلمة تعيين النغمة", "LabelTonemappingParam": "معلمة تعيين النغمة",
@ -1564,7 +1564,7 @@
"LabelAutomaticallyAddToCollection": "إضافة إلى المجموعة تلقائيا", "LabelAutomaticallyAddToCollection": "إضافة إلى المجموعة تلقائيا",
"Console": "وحدة التحكم", "Console": "وحدة التحكم",
"Casual": "غير رسمي", "Casual": "غير رسمي",
"AllowTonemappingHelp": "يمكن أن يؤدي تعيين النغمة إلى تحويل النطاق الديناميكي لمقاطع الفيديو من HDR إلى SDR مع الحفاظ على تفاصيل الصورة والألوان. هذه بينات مهمة جدًا لتمثيل المشهد الأصلي بشكل وفي للمقطع ألأصلي. حاليًا يعمل هذا ألاعداد فقط مع مقاطع فيديو HDR10 أو HLG. يتطلب هذا ألأعداد وقت تشغيل OpenCL أو CUDA.", "AllowTonemappingHelp": "يمكن أن يؤدي تعيين النغمة إلى تحويل النطاق الديناميكي لمقاطع الفيديو من HDR إلى SDR مع الحفاظ على تفاصيل الصورة والألوان. هذه بينات مهمة جدًا لتمثيل المشهد الأصلي بشكل وفي للمقطع ألأصلي. حاليًا يعمل هذا ألاعداد فقط مع مقاطع فيديو HDR10 أو HLG. يتطلب هذا ألأعداد وقت تشغيل GPGPU.",
"RefFramesNotSupported": "الإطارات المرجعية غير مدعومة", "RefFramesNotSupported": "الإطارات المرجعية غير مدعومة",
"InterlacedVideoNotSupported": "الفيديو المتشابك غير مدعوم", "InterlacedVideoNotSupported": "الفيديو المتشابك غير مدعوم",
"AnamorphicVideoNotSupported": "لا يتم دعم الفيديو ذي الصورة المشوهة", "AnamorphicVideoNotSupported": "لا يتم دعم الفيديو ذي الصورة المشوهة",
@ -1663,9 +1663,9 @@
"MediaInfoVideoRangeType": "نوع نطاق الفيديو", "MediaInfoVideoRangeType": "نوع نطاق الفيديو",
"LabelVideoRangeType": "نوع نطاق الفيديو", "LabelVideoRangeType": "نوع نطاق الفيديو",
"VideoRangeTypeNotSupported": "نوع نطاق الفيديو غير مدعوم", "VideoRangeTypeNotSupported": "نوع نطاق الفيديو غير مدعوم",
"LabelVppTonemappingContrastHelp": "تطبيق كسب التباين في تعيين نغمة VPP. القيم الموصى بها والافتراضية هي 1.2 و 1.", "LabelVppTonemappingContrastHelp": "تطبيق كسب التباين في تعيين نغمة VPP. القيم الموصى بها والافتراضية هي 1.",
"LabelVppTonemappingContrast": "كسب تباين تعيين نغمة VPP", "LabelVppTonemappingContrast": "كسب تباين تعيين نغمة VPP",
"LabelVppTonemappingBrightnessHelp": "تطبيق كسب السطوع في تعيين نغمة VPP. كل من القيم الموصى بها والافتراضية هي 0.", "LabelVppTonemappingBrightnessHelp": "تطبيق كسب السطوع في تعيين نغمة VPP. القيم الموصى بها والافتراضية هي 0.",
"LabelVppTonemappingBrightness": "كسب سطوع رسم الخرائط VPP نغمة", "LabelVppTonemappingBrightness": "كسب سطوع رسم الخرائط VPP نغمة",
"ScreenResolution": "تعيين مسار الترجمة على أساس البند السابق", "ScreenResolution": "تعيين مسار الترجمة على أساس البند السابق",
"RememberSubtitleSelectionsHelp": "تعيين مسار الترجمة على أساس البند السابق.", "RememberSubtitleSelectionsHelp": "تعيين مسار الترجمة على أساس البند السابق.",
@ -1715,14 +1715,60 @@
"LogLevel.None": "لا شيئ", "LogLevel.None": "لا شيئ",
"MenuOpen": "أفتح القائمة", "MenuOpen": "أفتح القائمة",
"AllowSegmentDeletion": "ألغاء القسم", "AllowSegmentDeletion": "ألغاء القسم",
"AllowSegmentDeletionHelp": "ألغي الأقسام القديمة بعد أن يتم أرسالها للعميل. هذا يساهم بمنع أن يتم تخزين الملف ألذي تم أعادة ترميزه. ستعمل هذه الميزة فقط عندما يتم كبح الترميز. قم بأيقاف هذا الأعداد في حال واجهت مشاكل بتشغيل ألصوت أو ألفديو.", "AllowSegmentDeletionHelp": "ألغي الأقسام القديمة بعد أن يتم تحميلها من قبل العميل. هذا يساهم بمنع أن يتم تخزين الملف ألذي تم أعادة ترميزه بالكامل. قم بأيقاف هذا الأعداد في حال واجهت مشاكل بتشغيل الصوت أو الفيديو.",
"LabelThrottleDelaySeconds": "أكبح بعد", "LabelThrottleDelaySeconds": "أكبح بعد",
"LabelThrottleDelaySecondsHelp": "ألزمن بالثواني الذي سيتم بعده كبح أعادة الترميز. يجب أن يكون الزمن طويلاً بكفاية ليحافظ العميل على مخزون صحي. يجب أن يكون كفح أعادة الترميز مفعلاً ليعمل هذا ألاعاداد.", "LabelThrottleDelaySecondsHelp": "ألزمن بالثواني الذي سيتم بعده كبح أعادة الترميز. يجب أن يكون الزمن طويلاً بكفاية ليحافظ العميل على مخزون صحي. يجب أن يكون كفح أعادة الترميز مفعلاً ليعمل هذا ألاعاداد.",
"LabelSegmentKeepSeconds": "الممدة للأحتفاظ على الشرائح", "LabelSegmentKeepSeconds": "الممدة للأحتفاظ على الشرائح",
"LabelEnableAudioVbrHelp": "معدل البِت المتغير ينتج على جودة أفضل مقارنة بمعدل البت المتوسط، ولكن في بعض الحالات النادرة قد يسبب مشاكل في التخزين المؤقت والتوافق.", "LabelEnableAudioVbrHelp": "معدل البِت المتغير ينتج على جودة أفضل مقارنة بمعدل البت المتوسط، ولكن في بعض الحالات النادرة قد يسبب مشاكل في التخزين المؤقت والتوافق.",
"LabelSegmentKeepSecondsHelp": "الزمن بالثواني الذي يجب الاحتفاظ به للشرائح قبل أن يتم الكتابة فوقها. يجب أن يكون أكبر من \"بعد الخنق\". يعمل هذا ألأعداد فقط إذا كان حذف الشرائح مفعلًا.", "LabelSegmentKeepSecondsHelp": "الزمن بالثواني الذي يجب الاحتفاظ به للشرائح بعد أن يتم تحميلها من قبل العميل. يعمل هذا ألأعداد فقط إذا كان حذف الشرائح مفعلًا.",
"AiTranslated": "مترجمة من قبل ذكاء اسطناعي", "AiTranslated": "مترجمة من قبل ذكاء اسطناعي",
"SelectAudioNormalizationHelp": "كسب الالبوم-تعديل الصوت لكل مسار لكي يعملون بنفس مستوى- كسب الالبوم- تعديل مستوى الصوت لكل المسارات في البوم واحد مع ابقاء على النطاق الديناميكي للألبوم.", "SelectAudioNormalizationHelp": "كسب الالبوم-تعديل الصوت لكل مسار لكي يعملون بنفس مستوى- كسب الالبوم- تعديل مستوى الصوت لكل المسارات في البوم واحد مع ابقاء على النطاق الديناميكي للألبوم.",
"ButtonEditUser": "تعديل مستخدم", "ButtonEditUser": "تعديل مستخدم",
"AllowSubtitleManagement": "اسمح لهذا المستخدم تعديل الترجمات" "AllowSubtitleManagement": "اسمح لهذا المستخدم تعديل الترجمات",
"HeaderDeleteSeries": "حذف مسلسل",
"DeleteEntireSeries": "حذف {0} حلقات",
"DeleteSeries": "حذف المسلسل",
"HeaderEpisodesStatus": "حالة الحلقات",
"LabelSelectAudioNormalization": "تطبيع الصوت",
"DeleteEpisode": "حذف الحلقة",
"EnableLibrary": "إتاحة المكتبة",
"LabelSyncPlayNoGroups": "لا توجد مجموعات متاحة",
"DeleteName": "حذف {0}",
"LabelServerVersion": "نسخة الخادم",
"LabelWebVersion": "نسخة الويب",
"LabelBuildVersion": "نسخة الإصدار",
"SavePassword": "حفظ كلمة المرور",
"SubtitleBlack": "أسود",
"SubtitleWhite": "أبيض",
"UserMenu": "قائمة المستخدم",
"LabelScanBehavior": "سلوك المسح",
"PriorityHigh": "عالي",
"PriorityBelowNormal": "تحت الطبيعي",
"PriorityNormal": "طبيعي",
"PriorityAboveNormal": "فوق الطبيعي",
"LabelProcessPriority": "أولوية المعالجة",
"PriorityIdle": "خامل",
"PlaybackError.PLAYER_ERROR": "التشغيل فشل لخطأ في مشغل الوسائط.",
"Studio": "الأستيديو",
"SubtitleYellow": "أصفر",
"PlaybackError.NETWORK_ERROR": "التشغيل فشل لخطأ في الشبكة.",
"UnknownError": "حدث خطأ غير معلوم.",
"Select": "اختيار",
"SubtitleRed": "أحمر",
"PasswordRequiredForAdmin": "يجب توفر كلمة مرور لحسابات المسؤول.",
"SecondarySubtitles": "الترجمات الثانوية",
"PlaybackError.NO_MEDIA_ERROR": "غير قايل لإيجاد مصدر موثق للوسائط لللتشغيل.",
"PlaybackError.SERVER_ERROR": "التشغيل فشل لخطأ في الخادم.",
"PlaybackError.MEDIA_NOT_SUPPORTED": "التشغيل فشل لأن الوسائط غير مدعومة من هذا العميل.",
"PlaybackError.NotAllowed": "تشغيل هذه الوسائط غير مسموح.",
"LabelJpegQuality": "جودة JPEG",
"SubtitleGreen": "أخضر",
"SearchResultsEmpty": "نأسف! لا يوجد نتاج لـ \"{0}\"",
"SubtitleLightGray": "رمادي فاتح",
"Short": "قصير",
"SubtitleGray": "رمادي",
"SubtitleBlue": "أزرق",
"AllowContentWithTagsHelp": "اعرض الوسائط التي تحتوي على الأقل واحدة من الاصناف المحددة.",
"AirPlay": "ايربلاي",
"Author": "الكاتب"
} }

View file

@ -749,7 +749,7 @@
"DashboardArchitecture": "Arquitectura: {0}", "DashboardArchitecture": "Arquitectura: {0}",
"DailyAt": "Diariament a {0}", "DailyAt": "Diariament a {0}",
"ClearQueue": "Esborra la cua", "ClearQueue": "Esborra la cua",
"Bwdif": "BWDIF", "Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"ButtonPlayer": "Reproductor", "ButtonPlayer": "Reproductor",
"ButtonCast": "Transmetre a dispositiu", "ButtonCast": "Transmetre a dispositiu",
"ApiKeysCaption": "Llista de les claus API activades actualment", "ApiKeysCaption": "Llista de les claus API activades actualment",
@ -864,7 +864,7 @@
"HeaderSelectFallbackFontPath": "Seleccioneu la ruta de la carpeta de fonts", "HeaderSelectFallbackFontPath": "Seleccioneu la ruta de la carpeta de fonts",
"Yesterday": "Ahir", "Yesterday": "Ahir",
"Yes": "Si", "Yes": "Si",
"Yadif": "YADIF", "Yadif": "Yet Another DeInterlacing Filter (YADIF)",
"XmlTvPathHelp": "Una ruta a un arxiu XMLTV. Jellyfin llegirà aquesta imatge i comprovar periòdicament si hi ha actualitzacions. Vostè és responsable de crear i actualitzar l'arxiu.", "XmlTvPathHelp": "Una ruta a un arxiu XMLTV. Jellyfin llegirà aquesta imatge i comprovar periòdicament si hi ha actualitzacions. Vostè és responsable de crear i actualitzar l'arxiu.",
"XmlDocumentAttributeListHelp": "Aquests atributs s'apliquen a l'element arrel de cada resposta XML.", "XmlDocumentAttributeListHelp": "Aquests atributs s'apliquen a l'element arrel de cada resposta XML.",
"Writers": "Escriptors", "Writers": "Escriptors",
@ -1335,7 +1335,7 @@
"LabelAudioChannels": "Canals d'àudio", "LabelAudioChannels": "Canals d'àudio",
"LabelAudioBitrate": "Taxa de bits d'àudio", "LabelAudioBitrate": "Taxa de bits d'àudio",
"LabelAudioBitDepth": "Profunditat de bits d'àudio", "LabelAudioBitDepth": "Profunditat de bits d'àudio",
"LabelAppNameExample": "Exemple: Sickbeard, Sonarr", "LabelAppNameExample": "Un nom llegible per persones per identificar les claus de l'API. Aquesta configuració no afectarà la funcionalitat.",
"LabelAppName": "Nom de l'aplicació", "LabelAppName": "Nom de l'aplicació",
"LabelAllowHWTranscoding": "Permetre la transcodificació de maquinari", "LabelAllowHWTranscoding": "Permetre la transcodificació de maquinari",
"LabelAllowedRemoteAddressesMode": "La manera de filtre d'adreces IP remota", "LabelAllowedRemoteAddressesMode": "La manera de filtre d'adreces IP remota",
@ -1863,5 +1863,25 @@
"DeleteLyrics": "Esborrar lletres", "DeleteLyrics": "Esborrar lletres",
"HeaderNoLyrics": "No s'ha trobat cap lletra", "HeaderNoLyrics": "No s'ha trobat cap lletra",
"Lyrics": "Lletres", "Lyrics": "Lletres",
"ViewLyrics": "Veure lletres" "ViewLyrics": "Veure lletres",
"SavePassword": "Desar contrasenya",
"EnableDts": "Habilitar DTS (DCA)",
"PlaylistError.AddFailed": "S'ha produït un error en afegir a la llista de reproducció",
"PlaylistError.CreateFailed": "S'ha produït un error en crear la llista de reproducció",
"HeaderVideoAdvanced": "Vídeo Avançat",
"Author": "Autor",
"Colorist": "Colorista",
"CoverArtist": "Artista de portada",
"Creator": "Creador",
"Editor": "Editor",
"EnableDtsHelp": "Habiliteu-lo només si el vostre dispositiu és compatible amb DTS o està connectat a un receptor d'àudio compatible, en cas contrari, pot provocar un error de reproducció.",
"EnableTrueHd": "Habilitar TrueHD",
"EnableTrueHdHelp": "Habiliteu-lo només si el vostre dispositiu és compatible amb TrueHD o està connectat a un receptor d'àudio compatible, en cas contrari, pot provocar un error de reproducció.",
"Illustrator": "Il·lustrador",
"Translator": "Traductor",
"PlaylistPublic": "Permetre accés públic",
"PlaylistPublicDescription": "Permetre que qualsevol usuari registrat vegi aquesta llista de reproducció.",
"HeaderLyricDownloads": "Descàrregues de lletres",
"SaveLyricsIntoMediaFolders": "Desa la lletra a les carpetes multimèdia",
"SaveLyricsIntoMediaFoldersHelp": "Emmagatzemar les lletres al costat dels fitxers d'àudio permetrà gestionar-les més fàcilment."
} }

View file

@ -303,7 +303,7 @@
"HeaderSendMessage": "Poslat zprávu", "HeaderSendMessage": "Poslat zprávu",
"HeaderSeriesOptions": "Nastavení seriálu", "HeaderSeriesOptions": "Nastavení seriálu",
"HeaderServerSettings": "Nastavení serveru", "HeaderServerSettings": "Nastavení serveru",
"HeaderSetupLibrary": "Nastavení Vašich knihoven médií", "HeaderSetupLibrary": "Nastavit knihovny médií",
"HeaderSortBy": "Třídit dle", "HeaderSortBy": "Třídit dle",
"HeaderSortOrder": "Pořadí třídění", "HeaderSortOrder": "Pořadí třídění",
"HeaderSpecialEpisodeInfo": "Infromace o speciální epizodě", "HeaderSpecialEpisodeInfo": "Infromace o speciální epizodě",
@ -362,7 +362,7 @@
"LabelAlbumArtists": "Alba umělce", "LabelAlbumArtists": "Alba umělce",
"LabelAllowHWTranscoding": "Povolit hardwarové překódování", "LabelAllowHWTranscoding": "Povolit hardwarové překódování",
"LabelAppName": "Název aplikace", "LabelAppName": "Název aplikace",
"LabelAppNameExample": "Příklad: Sickbeard, Sonarr", "LabelAppNameExample": "Čitelný název pro identifikaci klíčů k API. Toto nastavení nemá vliv na funkčnost.",
"LabelArtists": "Umělci", "LabelArtists": "Umělci",
"LabelArtistsHelp": "Více interpretů se odděluje pomocí středníku.", "LabelArtistsHelp": "Více interpretů se odděluje pomocí středníku.",
"LabelAudioLanguagePreference": "Preferovaný jazyk zvuku", "LabelAudioLanguagePreference": "Preferovaný jazyk zvuku",
@ -1267,7 +1267,7 @@
"PathNotFound": "Cesta nebyla nalezena. Zkontrolujte, zda je platná a zkuste to znovu.", "PathNotFound": "Cesta nebyla nalezena. Zkontrolujte, zda je platná a zkuste to znovu.",
"WeeklyAt": "V {0} v {1}", "WeeklyAt": "V {0} v {1}",
"LastSeen": "Naposledy zobrazené {0}", "LastSeen": "Naposledy zobrazené {0}",
"Yadif": "YADIF", "Yadif": "Yet Another DeInterlacing Filter (YADIF)",
"LabelLibraryPageSizeHelp": "Počet položek k zobrazení na stránce knihovny. Nastavte na 0 pro vypnutí stránkování.", "LabelLibraryPageSizeHelp": "Počet položek k zobrazení na stránce knihovny. Nastavte na 0 pro vypnutí stránkování.",
"LabelLibraryPageSize": "Velikost stránky knihovny", "LabelLibraryPageSize": "Velikost stránky knihovny",
"LabelDeinterlaceMethod": "Metoda odstranění prokládání", "LabelDeinterlaceMethod": "Metoda odstranění prokládání",
@ -1371,7 +1371,7 @@
"LabelIconMaxResHelp": "Maximální rozlišení ikon daných vlastností 'upnp:icon'.", "LabelIconMaxResHelp": "Maximální rozlišení ikon daných vlastností 'upnp:icon'.",
"LabelAlbumArtMaxResHelp": "Maximální rozlišení obrázku v souboru dané vlastností 'upnp:albumArtURI'.", "LabelAlbumArtMaxResHelp": "Maximální rozlišení obrázku v souboru dané vlastností 'upnp:albumArtURI'.",
"Other": "Ostatní", "Other": "Ostatní",
"Bwdif": "BWDIF", "Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"UseDoubleRateDeinterlacingHelp": "Toto nastavení při odstranění prokládání zdvojnásobuje snímkovou frekvenci, aby výsledné video vypadalo stejně plynule, jako při přehrávání prokládaného obsahu v televizi.", "UseDoubleRateDeinterlacingHelp": "Toto nastavení při odstranění prokládání zdvojnásobuje snímkovou frekvenci, aby výsledné video vypadalo stejně plynule, jako při přehrávání prokládaného obsahu v televizi.",
"UseDoubleRateDeinterlacing": "Zdvojnásobit snímkovou frekvenci při odstranění prokládání", "UseDoubleRateDeinterlacing": "Zdvojnásobit snímkovou frekvenci při odstranění prokládání",
"LabelTonemappingParamHelp": "Pro ladění algoritmu mapování tónů. Doporučená a výchozí hodnota je NaN. Obecně je pole ponecháváno prázdné.", "LabelTonemappingParamHelp": "Pro ladění algoritmu mapování tónů. Doporučená a výchozí hodnota je NaN. Obecně je pole ponecháváno prázdné.",
@ -1719,7 +1719,7 @@
"Short": "Krátký film", "Short": "Krátký film",
"HeaderPerformance": "Výkon", "HeaderPerformance": "Výkon",
"LabelParallelImageEncodingLimit": "Počet paralelních kódování obrázků", "LabelParallelImageEncodingLimit": "Počet paralelních kódování obrázků",
"LabelParallelImageEncodingLimitHelp": "Maximální počet kódování obrázků, které mohou běžet zároveň. Nastavením na 0 bude limit nastaven dle parametrů systému.", "LabelParallelImageEncodingLimitHelp": "Maximální počet kódování obrázků, které mohou běžet zároveň. Nastavením na 0 bude limit nastaven dle počtu jader CPU.",
"LabelEnableAudioVbr": "Povolit kódování zvuku VBR", "LabelEnableAudioVbr": "Povolit kódování zvuku VBR",
"LabelEnableAudioVbrHelp": "Proměnlivý bitový tok (VBR) nabízí lepší poměr mezi kvalitou a průměrným bitovým tokem, ale někdy může způsobit dodatečné načítání či problémy s kompatibilitou.", "LabelEnableAudioVbrHelp": "Proměnlivý bitový tok (VBR) nabízí lepší poměr mezi kvalitou a průměrným bitovým tokem, ale někdy může způsobit dodatečné načítání či problémy s kompatibilitou.",
"Select": "Vybrat", "Select": "Vybrat",
@ -1867,5 +1867,32 @@
"HeaderDeleteLyrics": "Odstranit texty písní", "HeaderDeleteLyrics": "Odstranit texty písní",
"HeaderNoLyrics": "Žádné texty písní nebyly nalezeny", "HeaderNoLyrics": "Žádné texty písní nebyly nalezeny",
"Lyrics": "Texty písní", "Lyrics": "Texty písní",
"ViewLyrics": "Zobrazit texty písní" "ViewLyrics": "Zobrazit texty písní",
"SavePassword": "Uložit heslo",
"PlaylistError.AddFailed": "Přidání do seznamu skladeb se nezdařilo",
"PlaylistError.CreateFailed": "Tvorba seznamu skladeb se nezdařila",
"EnableDts": "Povolit DTS (DCA)",
"EnableDtsHelp": "Povolte pouze pokud zařízení podporuje DTS nebo je připojeno ke kompatibilnímu AV receiveru, jinak by mohlo dojít k potížím s přehráváním.",
"EnableTrueHd": "Povolit TrueHD",
"EnableTrueHdHelp": "Povolte pouze pokud zařízení podporuje TrueHD nebo je připojeno ke kompatibilnímu AV receiveru, jinak by mohlo dojít k potížím s přehráváním.",
"HeaderVideoAdvanced": "Pokročilé video",
"PlaylistPublic": "Povolit veřejný přístup",
"PlaylistPublicDescription": "Tento seznam skladeb bude možné zobrazit i nepřihlášeným uživatelům.",
"HeaderLyricDownloads": "Stažení textů písní",
"SaveLyricsIntoMediaFolders": "Uložit texty písní do složek s médii",
"SaveLyricsIntoMediaFoldersHelp": "Uložení textů písní spolu se zvukovými souboru je snazší pro správu.",
"Author": "Autor",
"Colorist": "Kolorista",
"CoverArtist": "Obálka",
"Creator": "Tvůrce",
"Editor": "Editor",
"Illustrator": "Ilustrátor",
"Inker": "Inker",
"Letterer": "Lettrista",
"Penciller": "Výtvarník",
"Translator": "Překladatel",
"LibraryScanFanoutConcurrencyHelp": "Maximální počet souběžných úloh skenování knihovny. Nastavením na 0 bude počet dán počtem jader CPU. UPOZORNĚNÍ: Příliš vyskoý počet může způsobit problémy se síťovými souborými systémy. V takovém případě počet snižte.",
"LibraryScanFanoutConcurrency": "Maximální počet souběžných skenování knihovny",
"LabelSelectPreferredTranscodeVideoAudioCodec": "Preferovaný audio kodek pro překódovaní při přehrávání videí",
"SelectPreferredTranscodeVideoAudioCodecHelp": "Vybrat preferovaný audio kodek pro překódovaní při přehrávání videí. Pokud preferovaný kodek není podporován, server vybere jiný dostupný kodek."
} }

View file

@ -1186,7 +1186,7 @@
"LabelServerName": "Server navn", "LabelServerName": "Server navn",
"LabelUserLoginAttemptsBeforeLockout": "Mislykkede loginforsøg før bruger lukkes ude", "LabelUserLoginAttemptsBeforeLockout": "Mislykkede loginforsøg før bruger lukkes ude",
"ButtonAddImage": "Tilføj billede", "ButtonAddImage": "Tilføj billede",
"AllowFfmpegThrottlingHelp": "Når en omkodning eller ompakning kommer langt nok foran den nuværende afspilningsposition, pauser processen så der forbruges færre ressourcer. Dette er mest brugbart når man ikke spoler ofte. Slå dette fra hvis du oplever problemer under afspilning.", "AllowFfmpegThrottlingHelp": "Når en transkodning eller remux kommer langt nok foran den aktuelle afspilningsposition, skal du sætte processen på pause, så den bruger færre ressourcer. Dette er mest nyttigt, når du ser uden ofte at søge. Slå dette fra, hvis du oplever afspilningsproblemer.",
"AllowFfmpegThrottling": "Begræns omkodninger", "AllowFfmpegThrottling": "Begræns omkodninger",
"AlbumArtist": "Album Kunstner", "AlbumArtist": "Album Kunstner",
"Album": "Album", "Album": "Album",
@ -1300,7 +1300,7 @@
"YoutubeNotFound": "Video ikke fundet.", "YoutubeNotFound": "Video ikke fundet.",
"ButtonCast": "Cast til enhed", "ButtonCast": "Cast til enhed",
"ClearQueue": "Ryd kø", "ClearQueue": "Ryd kø",
"Bwdif": "BWDIF", "Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"ButtonUseQuickConnect": "Brug Quick Connect", "ButtonUseQuickConnect": "Brug Quick Connect",
"ButtonPlayer": "Afspiller", "ButtonPlayer": "Afspiller",
"Authorize": "Tillad", "Authorize": "Tillad",
@ -1842,5 +1842,19 @@
"MediaInfoElPresentFlag": "DV el forudindstillelses flag", "MediaInfoElPresentFlag": "DV el forudindstillelses flag",
"MediaInfoBlPresentFlag": "DV bl forudindstillelses flag", "MediaInfoBlPresentFlag": "DV bl forudindstillelses flag",
"LabelTonemappingMode": "Tone mapnings metode", "LabelTonemappingMode": "Tone mapnings metode",
"TonemappingModeHelp": "Vælg tonemapnings metode. Hvis du oplever udsprængte højlys, prøv at skifte til RGB metoden." "TonemappingModeHelp": "Vælg tonemapnings metode. Hvis du oplever udsprængte højlys, prøv at skifte til RGB metoden.",
"Author": "Forfatter",
"Colorist": "Farvelægger",
"ConfirmDeleteLyrics": "Sletning af disse tekster vil slette dem både fra filsystemet og dit mediebibliotek. Er du sikker på, at du vil fortsætte?",
"CoverArtist": "Omslagskunstner",
"Creator": "Skaber",
"Editor": "Redaktør",
"DeleteLyrics": "Slet sangtekster",
"ErrorDeletingLyrics": "Der opstod en fejl ved sletning af sangteksterne fra serveren. Kontroller, at Jellyfin har skriveadgang til mediemappen, og prøv igen.",
"EnableDts": "Aktiver DTS (DCA)",
"EnableDtsHelp": "Aktiver kun, hvis din enhed understøtter DTS eller er tilsluttet en kompatibel lydmodtager, ellers kan det medføre afspilningsfejl.",
"EnableTrueHd": "Aktiver TrueHD",
"EnableTrueHdHelp": "Aktiver kun, hvis din enhed understøtter TrueHD eller er tilsluttet en kompatibel lydmodtager, ellers kan det medføre afspilningsfejl.",
"HeaderDeleteLyrics": "Slet Sangtekster",
"HeaderLyricDownloads": "Sangtekst-downloads"
} }

View file

@ -406,7 +406,7 @@
"LabelAllowedRemoteAddresses": "Filter für externe IP-Adressen", "LabelAllowedRemoteAddresses": "Filter für externe IP-Adressen",
"LabelAllowedRemoteAddressesMode": "Filtermodus für externe IP-Adressen", "LabelAllowedRemoteAddressesMode": "Filtermodus für externe IP-Adressen",
"LabelAppName": "Applikationsname", "LabelAppName": "Applikationsname",
"LabelAppNameExample": "Beispiel: Sickbeard, Sonarr", "LabelAppNameExample": "Ein von Menschen lesbarer Name zur Identifizierung von API-Schlüsseln. Diese Einstellung hat keinen Einfluss auf die Funktionalität.",
"LabelArtists": "Interpreten", "LabelArtists": "Interpreten",
"LabelArtistsHelp": "Trenne mehrere Künstler durch ein Semikolon.", "LabelArtistsHelp": "Trenne mehrere Künstler durch ein Semikolon.",
"LabelAudioLanguagePreference": "Bevorzugte Audiosprache", "LabelAudioLanguagePreference": "Bevorzugte Audiosprache",
@ -1366,7 +1366,7 @@
"Poster": "Poster", "Poster": "Poster",
"Photo": "Foto", "Photo": "Foto",
"Other": "Sonstiges", "Other": "Sonstiges",
"Bwdif": "BWDIF", "Bwdif": "Bob Weaver Zeilenentflechtungs Filter (BWDIF)",
"UseDoubleRateDeinterlacingHelp": "Diese Einstellung verwendet die Halbbildrate beim Deinterlacing, oft auch als Bob-Deinterlacing bezeichnet. Dabei wird die Bildrate des Videos verdoppelt, um eine vollständige Bewegung wie beim Betrachten eines Interlaced-Video auf einem Fernseher zu erzielen.", "UseDoubleRateDeinterlacingHelp": "Diese Einstellung verwendet die Halbbildrate beim Deinterlacing, oft auch als Bob-Deinterlacing bezeichnet. Dabei wird die Bildrate des Videos verdoppelt, um eine vollständige Bewegung wie beim Betrachten eines Interlaced-Video auf einem Fernseher zu erzielen.",
"UseDoubleRateDeinterlacing": "Verdoppelung der Bildfrequenz beim Deinterlacing", "UseDoubleRateDeinterlacing": "Verdoppelung der Bildfrequenz beim Deinterlacing",
"LabelIconMaxResHelp": "Maximale Auflösung der Icons, die über die Eigenschaft 'upnp:icon' bereitgestellt wird.", "LabelIconMaxResHelp": "Maximale Auflösung der Icons, die über die Eigenschaft 'upnp:icon' bereitgestellt wird.",
@ -1789,7 +1789,7 @@
"ButtonEditUser": "Editiere Benutzer", "ButtonEditUser": "Editiere Benutzer",
"DlnaMovedMessage": "Die DLNA-Funktion wurde in ein Plugin verschoben.", "DlnaMovedMessage": "Die DLNA-Funktion wurde in ein Plugin verschoben.",
"DeleteName": "Löschen {0}", "DeleteName": "Löschen {0}",
"AllowSubtitleManagement": "Erlaube dem Nutzer Untertitel zu bearbeiten", "AllowSubtitleManagement": "Erlaube diesem Nutzer Untertitel zu bearbeiten",
"LabelUseReplayGainTagsHelp": "Audiodateien nach Replaygain-Tags durchsuchen statt den LUFS-Wert zu Berechnen. (Nutzt weniger Rechenleistung. Wird die \"LUFS Scan\" option überschreiben)", "LabelUseReplayGainTagsHelp": "Audiodateien nach Replaygain-Tags durchsuchen statt den LUFS-Wert zu Berechnen. (Nutzt weniger Rechenleistung. Wird die \"LUFS Scan\" option überschreiben)",
"LabelUseReplayGainTags": "ReplayGain-Tags Benutzen", "LabelUseReplayGainTags": "ReplayGain-Tags Benutzen",
"ChannelResolutionSD": "SD", "ChannelResolutionSD": "SD",
@ -1867,5 +1867,26 @@
"ErrorDeletingLyrics": "Es trat ein Fehler beim Löschen der Songtexte vom Server auf. Bitte stelle sicher, dass Jellyfin die notwendigen Zugriffsrechte besitzt.", "ErrorDeletingLyrics": "Es trat ein Fehler beim Löschen der Songtexte vom Server auf. Bitte stelle sicher, dass Jellyfin die notwendigen Zugriffsrechte besitzt.",
"HeaderDeleteLyrics": "Songtexte löschen", "HeaderDeleteLyrics": "Songtexte löschen",
"HeaderNoLyrics": "Keine Songtexte gefunden", "HeaderNoLyrics": "Keine Songtexte gefunden",
"ViewLyrics": "Songtexte ansehen" "ViewLyrics": "Songtexte ansehen",
"Author": "Autor",
"CoverArtist": "Cover-Künstler",
"Creator": "Ersteller",
"Editor": "Bearbeiter",
"EnableTrueHdHelp": "Schalte dies nur ein wenn dein Gerät TrueHD unterstützt oder an einem kompatiblem audio Empfänger angeschlossen ist, andernfalls könnte es zu Widergabefehlern führen.",
"EnableDts": "DTS zulassen (DCA)",
"EnableDtsHelp": "Schalte dies nur ein, wenn dein Gerät DTS unterstützt oder mit einem kompatiblem audio Empfänger angeschlossen ist, andernfalls könnte es zu Widergabefehlern führen.",
"EnableTrueHd": "TrueHD zulassen",
"Illustrator": "Illustrator",
"HeaderLyricDownloads": "Lyrik Downloads",
"LabelSelectPreferredTranscodeVideoAudioCodec": "Bevorzugter Transcode-Audiocodec bei der Videowiedergabe",
"Translator": "Übersetzer",
"LibraryScanFanoutConcurrency": "Parallele Bibliotheks-Scan-Aufgaben Limit",
"LibraryScanFanoutConcurrencyHelp": "Maximale Anzahl der parallelen Aufgaben während der Bibliotheksscans. Wenn Sie diesen Wert auf 0 setzen, wird eine Begrenzung auf der Grundlage der Kernanzahl Ihres Systems gewählt. WARNUNG: Wenn Sie diese Zahl zu hoch einstellen, kann es zu Problemen mit Netzwerkdateisystemen kommen; wenn Sie Probleme haben, senken Sie diese Zahl.",
"PlaylistError.AddFailed": "Fehler beim Hinzufügen zur Wiedergabeliste",
"PlaylistError.CreateFailed": "Fehler beim Erstellen der Wiedergabeliste",
"SavePassword": "Passwort speichern",
"SelectPreferredTranscodeVideoAudioCodecHelp": "Wählen Sie den bevorzugten Audiocodec für die Transkodierung von Videoinhalten. Wenn der bevorzugte Codec nicht unterstützt wird, verwendet der Server den nächstbesten verfügbaren Codec.",
"PlaylistPublicDescription": "Erlauben Sie, dass diese Wiedergabeliste für jeden eingeloggten Benutzer sichtbar ist.",
"SaveLyricsIntoMediaFolders": "Liedtexte in Medienordnern speichern",
"SaveLyricsIntoMediaFoldersHelp": "Das Speichern von Liedtexten zusammen mit den Audiodateien ermöglicht eine einfachere Verwaltung."
} }

View file

@ -1196,7 +1196,7 @@
"HeaderSpecialEpisodeInfo": "Special Episode Info", "HeaderSpecialEpisodeInfo": "Special Episode Info",
"HeaderSortOrder": "Sort Order", "HeaderSortOrder": "Sort Order",
"HeaderSortBy": "Sort By", "HeaderSortBy": "Sort By",
"HeaderSetupLibrary": "Setup your media libraries", "HeaderSetupLibrary": "Set up your media libraries",
"HeaderServerSettings": "Server Settings", "HeaderServerSettings": "Server Settings",
"HeaderSeriesStatus": "Programmes Status", "HeaderSeriesStatus": "Programmes Status",
"HeaderSeriesOptions": "Programmes Options", "HeaderSeriesOptions": "Programmes Options",
@ -1249,7 +1249,7 @@
"NoCreatedLibraries": "Seems like you haven't created any libraries yet. {0}Would you like to create one now?{1}", "NoCreatedLibraries": "Seems like you haven't created any libraries yet. {0}Would you like to create one now?{1}",
"AskAdminToCreateLibrary": "Ask an administrator to create a library.", "AskAdminToCreateLibrary": "Ask an administrator to create a library.",
"PlaybackErrorNoCompatibleStream": "This client isn't compatible with the media and the server isn't sending a compatible media format.", "PlaybackErrorNoCompatibleStream": "This client isn't compatible with the media and the server isn't sending a compatible media format.",
"AllowFfmpegThrottlingHelp": "When a transcode or remux gets far enough ahead from the current playback position, pause the process so it will consume less resources. This is most useful when watching without seeking often. Turn this off if you experience playback issues.", "AllowFfmpegThrottlingHelp": "When a transcode or remux gets far enough ahead from the current playback position, pause the process so it will consume fewer resources. This is most useful when watching without seeking often. Turn this off if you experience playback issues.",
"AllowFfmpegThrottling": "Throttle Transcodes", "AllowFfmpegThrottling": "Throttle Transcodes",
"OnApplicationStartup": "On application startup", "OnApplicationStartup": "On application startup",
"EveryXHours": "Every {0} hours", "EveryXHours": "Every {0} hours",
@ -1860,5 +1860,13 @@
"PlaybackError.SERVER_ERROR": "Playback failed due to a server error.", "PlaybackError.SERVER_ERROR": "Playback failed due to a server error.",
"PlaybackError.NotAllowed": "Playback of this media is not allowed.", "PlaybackError.NotAllowed": "Playback of this media is not allowed.",
"PlaybackError.RateLimitExceeded": "This media cannot be played at this time due to rate limits.", "PlaybackError.RateLimitExceeded": "This media cannot be played at this time due to rate limits.",
"Lyric": "Lyric" "Lyric": "Lyric",
"SavePassword": "Save Password",
"ViewLyrics": "View lyrics",
"ConfirmDeleteLyrics": "Deleting these lyrics will delete them from both the file system and your media library. Are you sure you wish to continue?",
"DeleteLyrics": "Delete lyrics",
"ErrorDeletingLyrics": "There was an error deleting the lyrics from the server. Please check that Jellyfin has write access to the media folder and try again.",
"HeaderDeleteLyrics": "Delete Lyrics",
"HeaderNoLyrics": "No lyrics found",
"Lyrics": "Lyrics"
} }

View file

@ -27,7 +27,7 @@
"AllowContentWithTagsHelp": "Only show media with at least one of the specified tags.", "AllowContentWithTagsHelp": "Only show media with at least one of the specified tags.",
"AllowSubtitleManagement": "Allow this user to edit subtitles", "AllowSubtitleManagement": "Allow this user to edit subtitles",
"AllowFfmpegThrottling": "Throttle Transcodes", "AllowFfmpegThrottling": "Throttle Transcodes",
"AllowFfmpegThrottlingHelp": "When a transcode or remux gets far enough ahead from the current playback position, pause the process so it will consume less resources. This is most useful when watching without seeking often. Turn this off if you experience playback issues.", "AllowFfmpegThrottlingHelp": "When a transcode or remux gets far enough ahead from the current playback position, pause the process so it will consume fewer resources. This is most useful when watching without seeking often. Turn this off if you experience playback issues.",
"AllowSegmentDeletion": "Delete segments", "AllowSegmentDeletion": "Delete segments",
"AllowSegmentDeletionHelp": "Delete old segments after they have been downloaded by the client. This prevents having to store the entire transcoded file on disk. Turn this off if you experience playback issues.", "AllowSegmentDeletionHelp": "Delete old segments after they have been downloaded by the client. This prevents having to store the entire transcoded file on disk. Turn this off if you experience playback issues.",
"LabelThrottleDelaySeconds": "Throttle after", "LabelThrottleDelaySeconds": "Throttle after",
@ -57,6 +57,7 @@
"AsManyAsPossible": "As many as possible", "AsManyAsPossible": "As many as possible",
"AspectRatio": "Aspect Ratio", "AspectRatio": "Aspect Ratio",
"Audio": "Audio", "Audio": "Audio",
"Author": "Author",
"Authorize": "Authorize", "Authorize": "Authorize",
"AuthProviderHelp": "Select an authentication provider to be used to authenticate this user's password.", "AuthProviderHelp": "Select an authentication provider to be used to authenticate this user's password.",
"Auto": "Auto", "Auto": "Auto",
@ -131,7 +132,7 @@
"ButtonUninstall": "Uninstall", "ButtonUninstall": "Uninstall",
"ButtonUseQuickConnect": "Use Quick Connect", "ButtonUseQuickConnect": "Use Quick Connect",
"ButtonWebsite": "Website", "ButtonWebsite": "Website",
"Bwdif": "BWDIF", "Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"CancelRecording": "Cancel recording", "CancelRecording": "Cancel recording",
"CancelSeries": "Cancel series", "CancelSeries": "Cancel series",
"Casual": "Casual", "Casual": "Casual",
@ -151,6 +152,7 @@
"ClearQueue": "Clear queue", "ClearQueue": "Clear queue",
"ClientSettings": "Client Settings", "ClientSettings": "Client Settings",
"Collections": "Collections", "Collections": "Collections",
"Colorist": "Colorist",
"ColorPrimaries": "Color primaries", "ColorPrimaries": "Color primaries",
"ColorSpace": "Color space", "ColorSpace": "Color space",
"ColorTransfer": "Color transfer", "ColorTransfer": "Color transfer",
@ -174,6 +176,8 @@
"CopyFailed": "Could not copy", "CopyFailed": "Could not copy",
"CopyStreamURL": "Copy Stream URL", "CopyStreamURL": "Copy Stream URL",
"CopyStreamURLSuccess": "URL copied successfully.", "CopyStreamURLSuccess": "URL copied successfully.",
"CoverArtist": "Cover artist",
"Creator": "Creator",
"CriticRating": "Critics rating", "CriticRating": "Critics rating",
"Cursive": "Cursive", "Cursive": "Cursive",
"DailyAt": "Daily at {0}", "DailyAt": "Daily at {0}",
@ -232,6 +236,7 @@
"DrmChannelsNotImported": "Channels with DRM will not be imported.", "DrmChannelsNotImported": "Channels with DRM will not be imported.",
"DropShadow": "Drop Shadow", "DropShadow": "Drop Shadow",
"Edit": "Edit", "Edit": "Edit",
"Editor": "Editor",
"EditImages": "Edit images", "EditImages": "Edit images",
"EditMetadata": "Edit metadata", "EditMetadata": "Edit metadata",
"EditSubtitles": "Edit subtitles", "EditSubtitles": "Edit subtitles",
@ -246,6 +251,8 @@
"EnableDetailsBanner": "Details Banner", "EnableDetailsBanner": "Details Banner",
"EnableDetailsBannerHelp": "Display a banner image at the top of the item details page.", "EnableDetailsBannerHelp": "Display a banner image at the top of the item details page.",
"EnableDisplayMirroring": "Display mirroring", "EnableDisplayMirroring": "Display mirroring",
"EnableDts": "Enable DTS (DCA)",
"EnableDtsHelp": "Only enable if your device supports DTS or is connected to a compatible audio receiver, otherwise it may cause playback failure.",
"EnableExternalVideoPlayers": "External video players", "EnableExternalVideoPlayers": "External video players",
"EnableExternalVideoPlayersHelp": "An external player menu will be shown when starting video playback.", "EnableExternalVideoPlayersHelp": "An external player menu will be shown when starting video playback.",
"EnableFasterAnimations": "Faster animations", "EnableFasterAnimations": "Faster animations",
@ -267,6 +274,8 @@
"EnableThemeSongsHelp": "Play the theme songs in background while browsing the library.", "EnableThemeSongsHelp": "Play the theme songs in background while browsing the library.",
"EnableThemeVideosHelp": "Play theme videos in the background while browsing the library.", "EnableThemeVideosHelp": "Play theme videos in the background while browsing the library.",
"EnableTonemapping": "Enable Tone mapping", "EnableTonemapping": "Enable Tone mapping",
"EnableTrueHd": "Enable TrueHD",
"EnableTrueHdHelp": "Only enable if your device supports TrueHD or is connected to a compatible audio receiver, otherwise it may cause playback failure.",
"EncoderPresetHelp": "Pick a faster value to improve performance, or a slower value to improve quality.", "EncoderPresetHelp": "Pick a faster value to improve performance, or a slower value to improve quality.",
"Ended": "Ended", "Ended": "Ended",
"EndsAtValue": "Ends at {0}", "EndsAtValue": "Ends at {0}",
@ -417,6 +426,7 @@
"HeaderLibrarySettings": "Library Settings", "HeaderLibrarySettings": "Library Settings",
"HeaderLiveTvTunerSetup": "Live TV Tuner Setup", "HeaderLiveTvTunerSetup": "Live TV Tuner Setup",
"HeaderLoginFailure": "Login Failure", "HeaderLoginFailure": "Login Failure",
"HeaderLyricDownloads": "Lyric Downloads",
"HeaderMedia": "Media", "HeaderMedia": "Media",
"HeaderMediaFolders": "Media Folders", "HeaderMediaFolders": "Media Folders",
"HeaderMetadataSettings": "Metadata Settings", "HeaderMetadataSettings": "Metadata Settings",
@ -474,7 +484,7 @@
"HeaderSeriesOptions": "Series Options", "HeaderSeriesOptions": "Series Options",
"HeaderSeriesStatus": "Series Status", "HeaderSeriesStatus": "Series Status",
"HeaderServerAddressSettings": "Server Address Settings", "HeaderServerAddressSettings": "Server Address Settings",
"HeaderSetupLibrary": "Setup your media libraries", "HeaderSetupLibrary": "Set up your media libraries",
"HeaderSortBy": "Sort By", "HeaderSortBy": "Sort By",
"HeaderSortOrder": "Sort Order", "HeaderSortOrder": "Sort Order",
"HeaderSpecialEpisodeInfo": "Special Episode Info", "HeaderSpecialEpisodeInfo": "Special Episode Info",
@ -501,6 +511,7 @@
"HeaderUploadSubtitle": "Upload Subtitle", "HeaderUploadSubtitle": "Upload Subtitle",
"HeaderUser": "User", "HeaderUser": "User",
"HeaderUsers": "Users", "HeaderUsers": "Users",
"HeaderVideoAdvanced": "Video Advanced",
"HeaderVideoQuality": "Video Quality", "HeaderVideoQuality": "Video Quality",
"HeaderVideos": "Videos", "HeaderVideos": "Videos",
"HeaderVideoType": "Video Type", "HeaderVideoType": "Video Type",
@ -516,9 +527,11 @@
"Identify": "Identify", "Identify": "Identify",
"IgnoreDts": "Ignore DTS (decoding timestamp)", "IgnoreDts": "Ignore DTS (decoding timestamp)",
"IgnoreDtsHelp": "Disabling this option may resolve some issues, e.g. missing audio on channels with separate audio and video streams.", "IgnoreDtsHelp": "Disabling this option may resolve some issues, e.g. missing audio on channels with separate audio and video streams.",
"Illustrator": "Illustrator",
"Image": "Image", "Image": "Image",
"Images": "Images", "Images": "Images",
"ImportFavoriteChannelsHelp": "Only channels that are marked as favorite on the tuner device will be imported.", "ImportFavoriteChannelsHelp": "Only channels that are marked as favorite on the tuner device will be imported.",
"Inker": "Inker",
"InstallingPackage": "Installing {0} (version {1})", "InstallingPackage": "Installing {0} (version {1})",
"InstantMix": "Instant mix", "InstantMix": "Instant mix",
"ItemCount": "{0} items", "ItemCount": "{0} items",
@ -544,7 +557,7 @@
"LabelAllowedRemoteAddressesMode": "Remote IP address filter mode", "LabelAllowedRemoteAddressesMode": "Remote IP address filter mode",
"LabelAllowHWTranscoding": "Allow hardware transcoding", "LabelAllowHWTranscoding": "Allow hardware transcoding",
"LabelAppName": "App name", "LabelAppName": "App name",
"LabelAppNameExample": "Example: Sickbeard, Sonarr", "LabelAppNameExample": "A human readable name for identifying API keys. This setting will not affect functionality.",
"LabelArtists": "Artists", "LabelArtists": "Artists",
"LabelArtistsHelp": "Separate multiple artists with a semicolon.", "LabelArtistsHelp": "Separate multiple artists with a semicolon.",
"LabelAudioBitDepth": "Audio bit depth", "LabelAudioBitDepth": "Audio bit depth",
@ -553,6 +566,7 @@
"LabelAudioCodec": "Audio codec", "LabelAudioCodec": "Audio codec",
"LabelAudioLanguagePreference": "Preferred audio language", "LabelAudioLanguagePreference": "Preferred audio language",
"LabelSelectAudioNormalization": "Audio Normalization", "LabelSelectAudioNormalization": "Audio Normalization",
"LabelSelectPreferredTranscodeVideoAudioCodec": "Preferred transcode audio codec in video playback",
"LabelAudioSampleRate": "Audio sample rate", "LabelAudioSampleRate": "Audio sample rate",
"LabelAuthProvider": "Authentication Provider", "LabelAuthProvider": "Authentication Provider",
"LabelAutomaticallyAddToCollection": "Automatically add to collection", "LabelAutomaticallyAddToCollection": "Automatically add to collection",
@ -759,7 +773,7 @@
"LabelOriginalTitle": "Original title", "LabelOriginalTitle": "Original title",
"LabelOverview": "Overview", "LabelOverview": "Overview",
"LabelParallelImageEncodingLimit": "Parallel image encoding limit", "LabelParallelImageEncodingLimit": "Parallel image encoding limit",
"LabelParallelImageEncodingLimitHelp": "Maximum amount of image encodings that are allowed to run in parallel. Setting this to 0 will choose a limit based on your system specs.", "LabelParallelImageEncodingLimitHelp": "Maximum number of image encodings that are allowed to run in parallel. Setting this to 0 will choose a limit based on your systems core count.",
"LabelParentalRating": "Parental rating", "LabelParentalRating": "Parental rating",
"LabelParentNumber": "Parent number", "LabelParentNumber": "Parent number",
"LabelPassword": "Password", "LabelPassword": "Password",
@ -944,7 +958,10 @@
"LatestFromLibrary": "Recently Added in {0}", "LatestFromLibrary": "Recently Added in {0}",
"LearnHowYouCanContribute": "Learn how you can contribute.", "LearnHowYouCanContribute": "Learn how you can contribute.",
"LeaveBlankToNotSetAPassword": "You can leave this field blank to set no password.", "LeaveBlankToNotSetAPassword": "You can leave this field blank to set no password.",
"Letterer": "Letterer",
"LibraryAccessHelp": "Select the libraries to share with this user. Administrators will be able to edit all folders using the metadata manager.", "LibraryAccessHelp": "Select the libraries to share with this user. Administrators will be able to edit all folders using the metadata manager.",
"LibraryScanFanoutConcurrency": "Parallel library scan tasks limit",
"LibraryScanFanoutConcurrencyHelp": "Maximum number of parallel tasks during library scans. Setting this to 0 will choose a limit based on your systems core count. WARNING: Setting this number too high may cause issues with network file systems; if you encounter problems lower this number.",
"LimitSupportedVideoResolution": "Limit maximum supported video resolution", "LimitSupportedVideoResolution": "Limit maximum supported video resolution",
"LimitSupportedVideoResolutionHelp": "Use 'Maximum Allowed Video Transcoding Resolution' as maximum supported video resolution.", "LimitSupportedVideoResolutionHelp": "Use 'Maximum Allowed Video Transcoding Resolution' as maximum supported video resolution.",
"List": "List", "List": "List",
@ -1230,6 +1247,7 @@
"PasswordResetProviderHelp": "Pick a password reset provider to be used when this user requests a password reset.", "PasswordResetProviderHelp": "Pick a password reset provider to be used when this user requests a password reset.",
"PasswordSaved": "Password saved.", "PasswordSaved": "Password saved.",
"PathNotFound": "The path could not be found. Please ensure the path is valid and try again.", "PathNotFound": "The path could not be found. Please ensure the path is valid and try again.",
"Penciller": "Penciler",
"People": "People", "People": "People",
"PerfectMatch": "Perfect match", "PerfectMatch": "Perfect match",
"Person": "Person", "Person": "Person",
@ -1257,6 +1275,10 @@
"PlayCount": "Play count", "PlayCount": "Play count",
"Played": "Played", "Played": "Played",
"PlayFromBeginning": "Play from beginning", "PlayFromBeginning": "Play from beginning",
"PlaylistError.AddFailed": "Error adding to playlist",
"PlaylistError.CreateFailed": "Error creating playlist",
"PlaylistPublic": "Allow public access",
"PlaylistPublicDescription": "Allow this playlist to be viewed by any logged in user.",
"Playlists": "Playlists", "Playlists": "Playlists",
"PlayNext": "Play next", "PlayNext": "Play next",
"PlayNextEpisodeAutomatically": "Play next episode automatically", "PlayNextEpisodeAutomatically": "Play next episode automatically",
@ -1347,6 +1369,9 @@
"Saturday": "Saturday", "Saturday": "Saturday",
"Save": "Save", "Save": "Save",
"SaveChanges": "Save changes", "SaveChanges": "Save changes",
"SaveLyricsIntoMediaFolders": "Save lyrics into media folders",
"SaveLyricsIntoMediaFoldersHelp": "Storing lyrics next to audio files will allow them to be more easily managed.",
"SavePassword": "Save Password",
"SaveRecordingNFO": "Save recording EPG metadata in NFO", "SaveRecordingNFO": "Save recording EPG metadata in NFO",
"SaveRecordingNFOHelp": "Save metadata from EPG listings provider along side media.", "SaveRecordingNFOHelp": "Save metadata from EPG listings provider along side media.",
"SaveRecordingImages": "Save recording EPG images", "SaveRecordingImages": "Save recording EPG images",
@ -1366,6 +1391,7 @@
"Season": "Season", "Season": "Season",
"SecondarySubtitles": "Secondary Subtitles", "SecondarySubtitles": "Secondary Subtitles",
"SelectAdminUsername": "Please select a username for the admin account.", "SelectAdminUsername": "Please select a username for the admin account.",
"SelectPreferredTranscodeVideoAudioCodecHelp": "Select the preferred audio codec to transcode to for video content. If the preferred codec is not supported, the server will use the next best available codec.",
"SelectServer": "Select Server", "SelectServer": "Select Server",
"SendMessage": "Send message", "SendMessage": "Send message",
"Series": "Series", "Series": "Series",
@ -1472,6 +1498,7 @@
"TrackCount": "{0} tracks", "TrackCount": "{0} tracks",
"Trailers": "Trailers", "Trailers": "Trailers",
"Transcoding": "Transcoding", "Transcoding": "Transcoding",
"Translator": "Translator",
"Tuesday": "Tuesday", "Tuesday": "Tuesday",
"TV": "TV", "TV": "TV",
"TvLibraryHelp": "Review the {0}TV naming guide{1}.", "TvLibraryHelp": "Review the {0}TV naming guide{1}.",
@ -1542,7 +1569,7 @@
"XmlTvNewsCategoriesHelp": "Programs with these categories will be displayed as news programs. Separate multiple with '|'.", "XmlTvNewsCategoriesHelp": "Programs with these categories will be displayed as news programs. Separate multiple with '|'.",
"XmlTvPathHelp": "A path to a XMLTV file. Jellyfin will read this file and periodically check it for updates. You are responsible for creating and updating the file.", "XmlTvPathHelp": "A path to a XMLTV file. Jellyfin will read this file and periodically check it for updates. You are responsible for creating and updating the file.",
"XmlTvSportsCategoriesHelp": "Programs with these categories will be displayed as sports programs. Separate multiple with '|'.", "XmlTvSportsCategoriesHelp": "Programs with these categories will be displayed as sports programs. Separate multiple with '|'.",
"Yadif": "YADIF", "Yadif": "Yet Another DeInterlacing Filter (YADIF)",
"Yes": "Yes", "Yes": "Yes",
"Yesterday": "Yesterday", "Yesterday": "Yesterday",
"HeaderSelectFallbackFontPath": "Select Fallback Font Folder Path", "HeaderSelectFallbackFontPath": "Select Fallback Font Folder Path",

View file

@ -171,7 +171,7 @@
"GuestStar": "Estrella invitada", "GuestStar": "Estrella invitada",
"Guide": "Guía", "Guide": "Guía",
"GuideProviderSelectListings": "Seleccionar listados", "GuideProviderSelectListings": "Seleccionar listados",
"H264CrfHelp": "El 'Factor de Velocidad Constante' (CRF) es el ajuste de calidad predeterminado para los codificadores x264 y x265. Puede establecer los valores entre 0 y 51, donde valores más bajos resultarían en una mejor calidad (a expensas de tamaños de archivo más altos). Los valores sanos están entre 18 y 28. El valor predeterminado para x264 es 23, y para x265 es 28, por lo que puede utilizar esto como punto de partida.", "H264CrfHelp": "El 'Factor de Velocidad Constante' (CRF) es el ajuste de calidad predeterminado para los codificadores de software x264 y x265. Puede establecer los valores entre 0 y 51, donde valores más bajos resultarían en una mejor calidad (a expensas de tamaños de archivo más altos). Los valores sanos están entre 18 y 28. El valor predeterminado para x264 es 23, y para x265 es 28, por lo que puede utilizar esto como punto de partida. Los codificadores de hardware no usan estos ajustes.",
"EncoderPresetHelp": "Elige un valor más rápido para mejorar el rendimiento o un valor más lento para mejorar la calidad.", "EncoderPresetHelp": "Elige un valor más rápido para mejorar el rendimiento o un valor más lento para mejorar la calidad.",
"HDPrograms": "Programas en HD", "HDPrograms": "Programas en HD",
"HardwareAccelerationWarning": "Activar la aceleración por hardware puede producir inestabilidades en algunos entornos. Asegúrate de que tu sistema operativo y tus controladores de vídeo están actualizados. Si tienes dificultades para reproducir los vídeos después de activar esto, tendrás que volver a poner este ajuste en None.", "HardwareAccelerationWarning": "Activar la aceleración por hardware puede producir inestabilidades en algunos entornos. Asegúrate de que tu sistema operativo y tus controladores de vídeo están actualizados. Si tienes dificultades para reproducir los vídeos después de activar esto, tendrás que volver a poner este ajuste en None.",
@ -311,7 +311,7 @@
"HeaderSendMessage": "Enviar mensaje", "HeaderSendMessage": "Enviar mensaje",
"HeaderSeriesOptions": "Opciones de series", "HeaderSeriesOptions": "Opciones de series",
"HeaderServerSettings": "Ajustes del servidor", "HeaderServerSettings": "Ajustes del servidor",
"HeaderSetupLibrary": "Configure sus bibliotecas de medios", "HeaderSetupLibrary": "Configure su biblioteca multimedia",
"HeaderSortBy": "Ordenar por", "HeaderSortBy": "Ordenar por",
"HeaderSortOrder": "Orden", "HeaderSortOrder": "Orden",
"HeaderSpecialEpisodeInfo": "Información del episodio especial", "HeaderSpecialEpisodeInfo": "Información del episodio especial",
@ -374,7 +374,7 @@
"LabelAllowedRemoteAddresses": "Filtro de dirección IP remota", "LabelAllowedRemoteAddresses": "Filtro de dirección IP remota",
"LabelAllowedRemoteAddressesMode": "Modo de filtro de dirección IP remota", "LabelAllowedRemoteAddressesMode": "Modo de filtro de dirección IP remota",
"LabelAppName": "Nombre de la aplicación", "LabelAppName": "Nombre de la aplicación",
"LabelAppNameExample": "Ejemplo: Sickbeard, Sonarr", "LabelAppNameExample": "Un nombre leible por humanos para identificar llaves de API. Esta opcion no afectara funcionalidad. Ejemplo: Sickbeard, Sonarr",
"LabelArtists": "Artistas", "LabelArtists": "Artistas",
"LabelArtistsHelp": "Separar múltiples artistas utilizando punto y coma.", "LabelArtistsHelp": "Separar múltiples artistas utilizando punto y coma.",
"LabelAudioLanguagePreference": "Idioma de audio preferido", "LabelAudioLanguagePreference": "Idioma de audio preferido",
@ -1275,7 +1275,7 @@
"LabelLibraryPageSize": "Tamaño de la página de la biblioteca", "LabelLibraryPageSize": "Tamaño de la página de la biblioteca",
"LabelLibraryPageSizeHelp": "Establece la cantidad de ítems a mostrar en una página de la biblioteca. Ponlo en 0 para desactivar la paginación.", "LabelLibraryPageSizeHelp": "Establece la cantidad de ítems a mostrar en una página de la biblioteca. Ponlo en 0 para desactivar la paginación.",
"UnsupportedPlayback": "Jellyfin no puede desencriptar contenido protegido por DRM aunque intentará reproducirlo de todas formas. Algunos archivos pueden aparecer completamente negros debido a encriptación u otras características no soportadas, como títulos interactivos.", "UnsupportedPlayback": "Jellyfin no puede desencriptar contenido protegido por DRM aunque intentará reproducirlo de todas formas. Algunos archivos pueden aparecer completamente negros debido a encriptación u otras características no soportadas, como títulos interactivos.",
"Yadif": "YADIF", "Yadif": "Otro filtro de desentrelazado YADIF",
"ButtonTogglePlaylist": "Lista de reproducción", "ButtonTogglePlaylist": "Lista de reproducción",
"Filter": "Filtro", "Filter": "Filtro",
"New": "Nuevo", "New": "Nuevo",
@ -1363,7 +1363,7 @@
"Restart": "Reiniciar", "Restart": "Reiniciar",
"ResetPassword": "Reiniciar Contraseña", "ResetPassword": "Reiniciar Contraseña",
"Profile": "Perfil", "Profile": "Perfil",
"Bwdif": "BWDIF", "Bwdif": "Filtro de Desentrelazado de Bob Weaver",
"UseDoubleRateDeinterlacing": "Duplicar el número de cuadros por segundo al desentrelazar", "UseDoubleRateDeinterlacing": "Duplicar el número de cuadros por segundo al desentrelazar",
"Photo": "Fotografía", "Photo": "Fotografía",
"MusicVideos": "Vídeos musicales", "MusicVideos": "Vídeos musicales",
@ -1708,7 +1708,7 @@
"PreferEmbeddedExtrasTitlesOverFileNames": "Prefiere títulos incrustados sobre nombres de archivo para extras", "PreferEmbeddedExtrasTitlesOverFileNames": "Prefiere títulos incrustados sobre nombres de archivo para extras",
"LabelDummyChapterCountHelp": "Número máximo de imágenes de capítulos que se extraerán para cada archivo multimedia.", "LabelDummyChapterCountHelp": "Número máximo de imágenes de capítulos que se extraerán para cada archivo multimedia.",
"LabelEnableAudioVbrHelp": "La tasa de bits variable ofrece una mejor relación entre calidad y tasa de bits promedio, pero en algunos casos raros puede causar problemas de almacenamiento de búfer y compatibilidad.", "LabelEnableAudioVbrHelp": "La tasa de bits variable ofrece una mejor relación entre calidad y tasa de bits promedio, pero en algunos casos raros puede causar problemas de almacenamiento de búfer y compatibilidad.",
"LabelParallelImageEncodingLimitHelp": "Cantidad máxima de codificaciones de imágenes que pueden ejecutarse en paralelo. Establecer esto en 0 elegirá un límite basado en las especificaciones de su sistema.", "LabelParallelImageEncodingLimitHelp": "Cantidad máxima de codificaciones de imágenes que pueden ejecutarse en paralelo. Establecer esto en 0 elegirá un límite basado en la cantidad de nucleos (CPU) en su sistema.",
"ResolutionMatchSource": "Coincidir fuente", "ResolutionMatchSource": "Coincidir fuente",
"SubtitleMagenta": "Magenta", "SubtitleMagenta": "Magenta",
"SubtitleWhite": "Blanco", "SubtitleWhite": "Blanco",
@ -1801,7 +1801,7 @@
"LabelServerVersion": "Versión del servidor", "LabelServerVersion": "Versión del servidor",
"AllowMjpegEncoding": "Permitir codificación en formato MJPEG (utilizado durante la generación de trickplay)", "AllowMjpegEncoding": "Permitir codificación en formato MJPEG (utilizado durante la generación de trickplay)",
"Trickplay": "Trickplay", "Trickplay": "Trickplay",
"LabelTrickplayAccel": "Habilitar aceleración por hardware", "LabelTrickplayAccel": "Habilitar descodificación por hardware",
"LabelScanBehavior": "Comportamiento de Escaneo", "LabelScanBehavior": "Comportamiento de Escaneo",
"ConfirmDeleteSeries": "Eliminar esta serie eliminará TODOS {0} episodios tanto del sistema de archivos como de tu biblioteca de medios. ¿Estás seguro de que deseas continuar?", "ConfirmDeleteSeries": "Eliminar esta serie eliminará TODOS {0} episodios tanto del sistema de archivos como de tu biblioteca de medios. ¿Estás seguro de que deseas continuar?",
"DeleteEntireSeries": "Eliminar {0} Episodios", "DeleteEntireSeries": "Eliminar {0} Episodios",
@ -1827,8 +1827,69 @@
"PlaybackError.SERVER_ERROR": "La reproducción falló por un error del servidor.", "PlaybackError.SERVER_ERROR": "La reproducción falló por un error del servidor.",
"PlaybackError.NotAllowed": "La reproducción de este medio no está permitida.", "PlaybackError.NotAllowed": "La reproducción de este medio no está permitida.",
"PlaybackError.RateLimitExceeded": "Este medio no puede reproducirse en este momento debído a límites.", "PlaybackError.RateLimitExceeded": "Este medio no puede reproducirse en este momento debído a límites.",
"EnableLibrary": "Activar la librería", "EnableLibrary": "Activar la biblioteca",
"EnableLibraryHelp": "Desactivando la biblioteca hará que no sea visible para ningún usuario.", "EnableLibraryHelp": "Desactivar la biblioteca hará que no sea visible para ningún usuario.",
"ConfirmDeleteLyrics": "Borrando estas letras también las borrará tanto del sistema de ficheros como de tu librería de medios. ¿Estás seguro de que deseas continuar?", "ConfirmDeleteLyrics": "Borrando estas letras también las borrará tanto del sistema de ficheros como de tu biblioteca de medios. ¿Estás seguro de que deseas continuar?",
"DeleteLyrics": "Borrar letras" "DeleteLyrics": "Borrar letras",
"HeaderNoLyrics": "No se han encontrado letras",
"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 y VAAPI, esta opción no tiene ningún efecto en otros métodos de aceleración por 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",
"BlockingScan": "Bloqueante - encola la generación, el escaneo se bloquea hasta que termine",
"LabelTrickplayAccelEncoding": "Habilitar la codificación MJPEG por hardware",
"PriorityHigh": "Alto",
"LabelScanBehaviorHelp": "El comportamiento por defecto es no bloqueante, que añadirá los medios a la biblioteca antes de que la generación trickplay se complete. El comportamiento bloqueante se asegurará de que los archivos de trickplay hayan sido generados antes de que los medios se añadan a la biblioteca, pero hará los escaneos significativamente más largos.",
"PriorityBelowNormal": "Más bajo de lo normal",
"LabelProcessPriority": "Prioridad de procesos",
"PriorityIdle": "Libre",
"LabelImageInterval": "Intervalo de imágenes",
"LabelAllowContentWithTags": "Permitir items con etiquetas",
"ViewLyrics": "Ver letras",
"PriorityAboveNormal": "Superior a lo normal",
"PriorityNormal": "Normal",
"SavePassword": "Guardar contraseña",
"LabelTileWidth": "Anchura de pieza",
"LabelTileHeight": "Altura de pieza",
"LabelTileHeightHelp": "Máximo número de imágenes por pieza en la dirección Y.",
"LabelJpegQuality": "Calidad JPEG",
"ExtractTrickplayImagesHelp": "Las imágenes de trickplay son similares a las de los capítulos, excepto por que se distribuyen a lo largo del contenido y se usan para mostrarse como una previsualización mientras te mueves por el vídeo.",
"LabelImageIntervalHelp": "Intervalo de tiempo (ms) entre cada nueva imagen de trickplay.",
"LabelWidthResolutions": "Resoluciones de anchura",
"LabelQscale": "Qscale",
"LabelJpegQualityHelp": "La calidad de compresión JPEG para las imágenes de trickplay.",
"LabelExtractTrickplayDuringLibraryScan": "Extraer imágenes para trickplay durante el escaneo de la biblioteca",
"LabelTileWidthHelp": "Máximo número de imágenes por pieza en la dirección X.",
"LabelQscaleHelp": "La escala de calidad de imágenes de la salida de ffmpeg, siendo 2 la mayor calidad y 31 la peor.",
"LabelTrickplayThreads": "Hilos FFmpeg",
"LabelTrickplayThreadsHelp": "El número de hilos para pasar al argumento '-threads' de ffmpeg.",
"LabelWidthResolutionsHelp": "Lista separada por comas de las anchuras (px) en las que se generarán las imágenes de trickplay. Todas las imágenes se generarán proporcionalmente a la fuente, así que una anchura de 320 en un vídeo 16:9 acabará sobre 320x180.",
"OptionExtractTrickplayImage": "Habilitar la extracción de imágenes de trickplay",
"LabelExtractTrickplayDuringLibraryScanHelp": "Generar imágenes de trickplay cuando los vídeos se importan durante el escaneo de la biblioteca. De lo contrario se extraerán durante la tarea programada de extracción de imágenes de trickplay. Si la generación es no bloqueante esto no afectará al tiempo que la biblioteca tarda en escanearse.",
"Author": "Autor",
"LibraryScanFanoutConcurrencyHelp": "Numero maximo de tareas de escaneos en librerias paralelas. Marcar esta opcion en 0 escogera un limite basado en la cantidad de nucleos (CPU) en sus sistema. ADVERTENCIA: Marcar este numero demasiado alto puede causar problemas con sistemas de archivos de red; si encuentra problemas baje este numero.",
"LibraryScanFanoutConcurrency": "Limite de escaneos de tareas en librerias paralelas",
"Penciller": "Lapicista",
"PlaylistError.CreateFailed": "Error creando lista de reproduccion",
"PlaylistError.AddFailed": "Error agregando a lista de reproduccion",
"SaveLyricsIntoMediaFoldersHelp": "Almacenar letras junto a archivos de audio les permitira ser administradas mas facilmente.",
"Colorist": "Colorista",
"CoverArtist": "Caratula de Artista",
"Creator": "Creador",
"Editor": "Editor",
"Illustrator": "Ilustrador",
"Letterer": "Rotulador",
"Translator": "Traductor",
"EnableTrueHdHelp": "Solo habilitar si su dispositivo soporta TrueHD o esta conectado a receptor de audio compatible, de otra manera puede causar fallos de reproduccion.",
"EnableDts": "Habilitar DTS (DCA)",
"EnableDtsHelp": "Solo habilitar si su dispositivo soporta DTS o esta conectado a un receptor de audio compatible, de otra forma puede causar fallos de reproduccion.",
"EnableTrueHd": "Habilitar TrueHD",
"PlaylistPublic": "Permitir acceso publico",
"PlaylistPublicDescription": "Permitir que esta lista de reproduccion sea vista por cualquier usuario que este logueado.",
"HeaderLyricDownloads": "Descarga de letras",
"HeaderVideoAdvanced": "Video Avanzado",
"SaveLyricsIntoMediaFolders": "Guardar letras en carpeta de media"
} }

View file

@ -285,7 +285,7 @@
"CustomDlnaProfilesHelp": "Loo kohandatud profiil uue seadme jaoks või tühista süsteemi profiil.", "CustomDlnaProfilesHelp": "Loo kohandatud profiil uue seadme jaoks või tühista süsteemi profiil.",
"ConfigureDateAdded": "Seadista, kuidas lisamise kuupäev kuvatakse juhtpaneeli meediakogu seadetes", "ConfigureDateAdded": "Seadista, kuidas lisamise kuupäev kuvatakse juhtpaneeli meediakogu seadetes",
"Banner": "Bänner", "Banner": "Bänner",
"AllowTonemappingHelp": "Värvikaardistus võib muuta video dünaamilise ulatuse HDR -st SDR -ks, säilitades samal ajal pildi üksikasjad ja värvid, mis on algse stseeni kujutamisel väga oluline teave. Praegu töötab ainult 10-bitise HDR10, HLG ja DoVi videotega. See vajab vastavat OpenGL või CUDA käitusaega.", "AllowTonemappingHelp": "Värvikaardistus võib muuta video dünaamilise ulatuse HDR -st SDR -ks, säilitades samal ajal pildi üksikasjad ja värvid, mis on algse stseeni kujutamisel väga oluline teave. Praegu töötab ainult 10-bitise HDR10, HLG ja DoVi videotega. See vajab vastavat GPGPU käitusaega.",
"HeaderDirectPlayProfile": "Otse-esituse profiil", "HeaderDirectPlayProfile": "Otse-esituse profiil",
"HeaderContainerProfileHelp": "Konteineri profiilid näitavad seadme piiranguid teatud vormingute esitamisel. Kui kehtib piirang, meedium transkooditakse, isegi kui vorming on seadistatud otse-esituseks.", "HeaderContainerProfileHelp": "Konteineri profiilid näitavad seadme piiranguid teatud vormingute esitamisel. Kui kehtib piirang, meedium transkooditakse, isegi kui vorming on seadistatud otse-esituseks.",
"HeaderCodecProfileHelp": "Koodeki profiilid näitavad seadme piiranguid konkreetsete koodekite esitamisel. Kui kehtivad piirangud, transkooditakse meedia, isegi kui koodek on seadistatud otse-esituseks.", "HeaderCodecProfileHelp": "Koodeki profiilid näitavad seadme piiranguid konkreetsete koodekite esitamisel. Kui kehtivad piirangud, transkooditakse meedia, isegi kui koodek on seadistatud otse-esituseks.",
@ -1656,7 +1656,7 @@
"Select": "Vali", "Select": "Vali",
"EnableIntelLowPowerH264HwEncoder": "Luba Intel Low-Power H.264 riistvara kodeerija", "EnableIntelLowPowerH264HwEncoder": "Luba Intel Low-Power H.264 riistvara kodeerija",
"HeaderDummyChapter": "Peatükipildid", "HeaderDummyChapter": "Peatükipildid",
"LabelSegmentKeepSecondsHelp": "Aeg sekundites, mille jooksul segmente tuleks hoida, enne kui need üle kirjutatakse. Peab olema suurem kui „Drossel pärast”. Töötab ainult siis, kui segmendi kustutamine on lubatud.", "LabelSegmentKeepSecondsHelp": "Aeg sekundites, kui kaua segmente tuleks hoida, peale seda kui need on kliendi poolt alla laetud. Töötab ainult siis, kui segmendi kustutamine on lubatud.",
"DeinterlaceMethodHelp": "Valige lahtipõimitud sisu tarkvara ümberkodeerimisel kasutatav lahtipõimimis meetod. Kui riistvaralist lahtipõimimist toetav riistvarakiirendus on lubatud, kasutatakse selle sätte asemel riistvaralist lahtipõimijat.", "DeinterlaceMethodHelp": "Valige lahtipõimitud sisu tarkvara ümberkodeerimisel kasutatav lahtipõimimis meetod. Kui riistvaralist lahtipõimimist toetav riistvarakiirendus on lubatud, kasutatakse selle sätte asemel riistvaralist lahtipõimijat.",
"EnableRewatchingNextUp": "Luba uuesti vaatamine Järgmises", "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.", "IntelLowPowerEncHelp": "Madala võimsusega kodeering võib hoida tarbetut CPU-GPU sünkroonimist. Linuxis tuleb need keelata, kui i915 HuC püsivara pole konfigureeritud.",
@ -1666,8 +1666,12 @@
"PreferSystemNativeHwDecoder": "Eelistage OS-i DXVA või VA-API riistvaradekoodereid", "PreferSystemNativeHwDecoder": "Eelistage OS-i DXVA või VA-API riistvaradekoodereid",
"AllowCollectionManagement": "Luba sellel kasutajal kogusid hallata", "AllowCollectionManagement": "Luba sellel kasutajal kogusid hallata",
"AllowSegmentDeletion": "Kustuta segmendid", "AllowSegmentDeletion": "Kustuta segmendid",
"AllowSegmentDeletionHelp": "Kustuta vanad segmendid pärast nende kliendile saatmist. See hoiab ära kogu ümberkodeeritud faili kettale salvestamise. Töötab ainult siis, kui drossel on lubatud. Kui teil on taasesitusprobleeme, lülitage see välja.", "AllowSegmentDeletionHelp": "Kustuta vanad segmendid pärast nende alla laadimist kliendi poolt. See hoiab ära kogu ümberkodeeritud faili kettale salvestamise. Lülita see välja, kui taasesitusega tekib probleeme.",
"LabelThrottleDelaySeconds": "Drossel pärast", "LabelThrottleDelaySeconds": "Drossel pärast",
"LabelThrottleDelaySecondsHelp": "Aeg sekundites, mille järel transkooder pidurdatakse. Peab olema piisavalt suur, et klient säilitaks terve puhvri. Töötab ainult siis, kui drossel on lubatud.", "LabelThrottleDelaySecondsHelp": "Aeg sekundites, mille järel transkooder pidurdatakse. Peab olema piisavalt suur, et klient säilitaks terve puhvri. Töötab ainult siis, kui drossel on lubatud.",
"LabelSegmentKeepSeconds": "Aeg hoida segmente" "LabelSegmentKeepSeconds": "Aeg hoida segmente",
"Author": "Autor",
"BlockContentWithTagsHelp": "Peida meedia, millel on vähemalt üks antud silt.",
"ButtonEditUser": "Redigeeri kasutajat",
"AllowSubtitleManagement": "Luba sellel kasutajal subtiitreid muuta"
} }

View file

@ -1605,7 +1605,7 @@
"EnableAudioNormalization": "معمول سازی صوت", "EnableAudioNormalization": "معمول سازی صوت",
"AllowCollectionManagement": "به این کاربر اجازه مدیریت مجموعه را بده", "AllowCollectionManagement": "به این کاربر اجازه مدیریت مجموعه را بده",
"AllowSegmentDeletion": "تکه ها را پاک کن", "AllowSegmentDeletion": "تکه ها را پاک کن",
"AllowSegmentDeletionHelp": "پاک کردن تکه های قدیمی را بعد از فرستادن به کلاینت. این کار از ذخیره کل فایل transcode شده بر روی هارد جلوگیری می‌ کند. این تنها زمانی کار می کند که throttling فعال باشد. درصورت مشکل در هنگام پخش این ویژگی را غیرفعال کنید.", "AllowSegmentDeletionHelp": "پاک کردن تکه های قدیمی بعد از دریافت کلاینت. این کار از ذخیره کل فایل transcode شده بر روی هارد جلوگیری می‌ کند. این تنها زمانی کار می کند که throttling فعال باشد. درصورت مشکل در هنگام پخش این ویژگی را غیرفعال کنید.",
"LogLevel.Error": "خطا", "LogLevel.Error": "خطا",
"LogLevel.Critical": "بحرانی", "LogLevel.Critical": "بحرانی",
"LogLevel.None": "هیچکدام", "LogLevel.None": "هیچکدام",
@ -1625,5 +1625,8 @@
"LogLevel.Information": "اطلاعات", "LogLevel.Information": "اطلاعات",
"LogoScreensaver": "محافظ صفحه لوگو", "LogoScreensaver": "محافظ صفحه لوگو",
"Localization": "محلی‌سازی", "Localization": "محلی‌سازی",
"HeaderGuestCast": "بازیگران مهمان" "HeaderGuestCast": "بازیگران مهمان",
"AirPlay": "پخش هوا",
"AllowContentWithTagsHelp": "فقط رسانه های دارای حداقل یکی از برچسب های مشخص شده را نشان بده.",
"AllowSubtitleManagement": "به این کاربر اجازه ویرایش زیرنویس بده"
} }

View file

@ -1856,5 +1856,23 @@
"ExtractTrickplayImagesHelp": "Trickplay-kuvat ovat samankaltaisia kuin kappalekuvat, paitsi että ne ovat koko sisällön pituudelta ja niitä käytetään esikatseluna videota selattaessa.", "ExtractTrickplayImagesHelp": "Trickplay-kuvat ovat samankaltaisia kuin kappalekuvat, paitsi että ne ovat koko sisällön pituudelta ja niitä käytetään esikatseluna videota selattaessa.",
"NonBlockingScan": "Ei-blokkaava - asettaa generoinnin jonoon ja palaa", "NonBlockingScan": "Ei-blokkaava - asettaa generoinnin jonoon ja palaa",
"BlockingScan": "Blokkaava - asettaa generoinnin jonoon ja estää skannauksen ennen kuin se on valmis", "BlockingScan": "Blokkaava - asettaa generoinnin jonoon ja estää skannauksen ennen kuin se on valmis",
"LabelExtractTrickplayDuringLibraryScan": "Luo trickplay-kuvat kirjaston skannauksen yhteydessä" "LabelExtractTrickplayDuringLibraryScan": "Luo trickplay-kuvat kirjaston skannauksen yhteydessä",
"PlaylistError.AddFailed": "Virhe soittolistaan lisättäessä",
"PlaylistError.CreateFailed": "Virhe soittolistaa luodessa",
"LibraryScanFanoutConcurrency": "Rinnakkaisten kirjastopäivityksien raja",
"Lyrics": "Sanoitukset",
"SavePassword": "Tallenna salasana",
"Author": "Tekijä",
"Colorist": "Koloristi",
"CoverArtist": "Kansitaiteilija",
"Creator": "Luoja",
"Illustrator": "Kuvittaja",
"Translator": "Kääntäjä",
"DeleteLyrics": "Poista sanoitukset",
"HeaderDeleteLyrics": "Poista sanoitukset",
"HeaderNoLyrics": "Sanoituksia ei löydy",
"ViewLyrics": "Näytä sanoitukset",
"HeaderLyricDownloads": "Sanoitusten lataaminen",
"SaveLyricsIntoMediaFolders": "Tallenna sanoitukset mediakansioon",
"PlaylistPublic": "Salli julkinen pääsy"
} }

View file

@ -6,7 +6,7 @@
"ButtonGotIt": "J'ai compris", "ButtonGotIt": "J'ai compris",
"ButtonQuickStartGuide": "Guide de démarrage rapide", "ButtonQuickStartGuide": "Guide de démarrage rapide",
"ButtonSignOut": "Sign out", "ButtonSignOut": "Sign out",
"ConfirmDeleteItem": "La suppression de cet élément le supprimera à la fois du système de fichiers et de votre médiathèque. Êtes-vous sûr de vouloir continuer ?", "ConfirmDeleteItem": "Supprimer cet élément l'effacera à la fois du système de fichiers et de votre médiathèque. Êtes-vous sûr de vouloir continuer?",
"Delete": "Supprimer", "Delete": "Supprimer",
"Disconnect": "Se déconnecter", "Disconnect": "Se déconnecter",
"Download": "Télécharger", "Download": "Télécharger",
@ -70,7 +70,7 @@
"WelcomeToProject": "Bienvenue dans Jellyfin !", "WelcomeToProject": "Bienvenue dans Jellyfin !",
"WizardCompleted": "C'est tout ce dont nous avons besoin pour le moment. Jellyfin a commencé à récolter les informations de votre bibliothèque de médias. Jetez un oeil à quelques unes de nos applications, puis cliquez sur <b>Terminer</b> pour consulter le <b>Tableau de bord</b>.", "WizardCompleted": "C'est tout ce dont nous avons besoin pour le moment. Jellyfin a commencé à récolter les informations de votre bibliothèque de médias. Jetez un oeil à quelques unes de nos applications, puis cliquez sur <b>Terminer</b> pour consulter le <b>Tableau de bord</b>.",
"Absolute": "Absolu", "Absolute": "Absolu",
"AccessRestrictedTryAgainLater": "L'accès est présentement limité. Veuillez réessayer plus tard.", "AccessRestrictedTryAgainLater": "L'accès est présentement restreint. Veuillez réessayer plus tard.",
"Actor": "Acteur", "Actor": "Acteur",
"AddToPlayQueue": "Ajouter à la file d'attente", "AddToPlayQueue": "Ajouter à la file d'attente",
"AddedOnValue": "Ajouté le {0}", "AddedOnValue": "Ajouté le {0}",
@ -88,7 +88,7 @@
"AllowMediaConversion": "Autoriser la conversion des médias", "AllowMediaConversion": "Autoriser la conversion des médias",
"AllowMediaConversionHelp": "Autoriser ou refuser l'accès à la fonctionnalité de conversion des médias.", "AllowMediaConversionHelp": "Autoriser ou refuser l'accès à la fonctionnalité de conversion des médias.",
"AllowOnTheFlySubtitleExtraction": "Autoriser l'extraction des sous-titres à la volée", "AllowOnTheFlySubtitleExtraction": "Autoriser l'extraction des sous-titres à la volée",
"AllowOnTheFlySubtitleExtractionHelp": "Les sous-titres intégrés peuvent être extraits des vidéos et distribués aux clients en texte brut pour éviter le transcodage. Sur certains systèmes, cela peut prendre du temps et arrêter la lecture de la vidéo pendant le processus d'extraction. Désactivez cette option pour graver les sous-titres avec un transcodage quand l'appareil ne les prend pas en charge nativement.", "AllowOnTheFlySubtitleExtractionHelp": "Les sous-titres intégrés peuvent être extraits des vidéos et distribués aux clients en texte brut pour éviter le transcodage. Sur certains systèmes, cela peut prendre du temps et arrêter la lecture de la vidéo pendant le processus d'extraction. Désactivez cette option pour graver les sous-titres avec un transcodage quand l'appareil ne le supporte pas nativement.",
"AllowRemoteAccess": "Autoriser les connexions à distance sur ce serveur Jellyfin", "AllowRemoteAccess": "Autoriser les connexions à distance sur ce serveur Jellyfin",
"AllowRemoteAccessHelp": "Si l'option est désactivée, toutes les connexions distantes seront bloquées.", "AllowRemoteAccessHelp": "Si l'option est désactivée, toutes les connexions distantes seront bloquées.",
"Artists": "Artistes", "Artists": "Artistes",
@ -128,7 +128,7 @@
"BoxRear": "Dos de boîtier", "BoxRear": "Dos de boîtier",
"Browse": "Parcourir", "Browse": "Parcourir",
"MessageBrowsePluginCatalog": "Explorer notre catalogue des plugins pour voir les plugins disponibles.", "MessageBrowsePluginCatalog": "Explorer notre catalogue des plugins pour voir les plugins disponibles.",
"AllowHWTranscodingHelp": "Permet au récepteur TV de transcoder les flux à la volée. Cela peut aider à réduire le transcodage requis par le serveur .", "AllowHWTranscodingHelp": "Permet au syntoniseur TV de transcoder les streams à la volée. Cela peut aider à réduire le transcodage requis par le serveur .",
"BurnSubtitlesHelp": "Détermine si le serveur doit incruster les sous-titres lors du transcodage vidéo. Éviter ceci améliorera les performances du serveur. Sélectionnez Auto pour incruster les formats basés sur l'image (par exemple, VobSub, PGS, SUB/IDX, etc.) ainsi que certains sous-titres ASS/SSA.", "BurnSubtitlesHelp": "Détermine si le serveur doit incruster les sous-titres lors du transcodage vidéo. Éviter ceci améliorera les performances du serveur. Sélectionnez Auto pour incruster les formats basés sur l'image (par exemple, VobSub, PGS, SUB/IDX, etc.) ainsi que certains sous-titres ASS/SSA.",
"ButtonAddMediaLibrary": "Ajouter une médiathèque", "ButtonAddMediaLibrary": "Ajouter une médiathèque",
"ButtonAddScheduledTaskTrigger": "Ajouter un déclencheur", "ButtonAddScheduledTaskTrigger": "Ajouter un déclencheur",
@ -150,15 +150,15 @@
"AspectRatio": "Format de l'image", "AspectRatio": "Format de l'image",
"AskAdminToCreateLibrary": "Demander un administrateur de créer une médiathèque.", "AskAdminToCreateLibrary": "Demander un administrateur de créer une médiathèque.",
"Artist": "Artiste", "Artist": "Artiste",
"AllowFfmpegThrottlingHelp": "Quand un transcodage ou un remultiplexage a traité une période suffisamment longue depuis la position de lecture, le processus sera interrompu afin d'économiser des ressources. Ceci est utile principalement lors de lectures continues. À désactiver si vous éprouvez des problèmes de lecture.", "AllowFfmpegThrottlingHelp": "Quand un transcodage ou un remultiplexage est suffisament en avance sur la position de lecture actuelle, le processus sera interrompu afin d'économiser des ressources. Ceci est utile principalement lors de lectures continues. À désactiver si vous éprouvez des problèmes de lecture.",
"AllowFfmpegThrottling": "Limiter la vitesse de transcodage", "AllowFfmpegThrottling": "Limiter le transcodage",
"AlbumArtist": "Artiste de l'album", "AlbumArtist": "Artiste de l'album",
"Album": "Album", "Album": "Album",
"AuthProviderHelp": "Sélectionner un fournisseur d'authentification pour authentifier le mot de passe de cet utilisateur.", "AuthProviderHelp": "Sélectionner un fournisseur d'authentification pour authentifier le mot de passe de cet utilisateur.",
"ButtonSyncPlay": "SyncPlay", "ButtonSyncPlay": "SyncPlay",
"Default": "Par défaut", "Default": "Par défaut",
"DeathDateValue": "Mort: {0}", "DeathDateValue": "Mort: {0}",
"DatePlayed": "Date écoutée", "DatePlayed": "Date d'écoute",
"DateAdded": "Date d'ajout", "DateAdded": "Date d'ajout",
"CriticRating": "Évaluation des critiques", "CriticRating": "Évaluation des critiques",
"CopyStreamURLSuccess": "URL copié avec succès.", "CopyStreamURLSuccess": "URL copié avec succès.",
@ -167,7 +167,7 @@
"Connect": "Connexion", "Connect": "Connexion",
"ConfirmEndPlayerSession": "Voulez-vous éteindre Jellyfin sur {0}?", "ConfirmEndPlayerSession": "Voulez-vous éteindre Jellyfin sur {0}?",
"ConfirmDeletion": "Confirmer la suppression", "ConfirmDeletion": "Confirmer la suppression",
"ConfirmDeleteItems": "Supprimer ceux-ci les effacera du système de fichiers et votre médiathèque. Êtes-vous sûr de vouloir continuer?", "ConfirmDeleteItems": "Supprimer ces éléments les effaceront du disque et de votre médiathèque. Êtes-vous sûr de vouloir continuer?",
"ConfirmDeleteImage": "Effacer l'image?", "ConfirmDeleteImage": "Effacer l'image?",
"ClientSettings": "Paramètres du client", "ClientSettings": "Paramètres du client",
"ChannelNumber": "Numéro de canal", "ChannelNumber": "Numéro de canal",
@ -186,7 +186,7 @@
"ButtonShutdown": "Éteindre", "ButtonShutdown": "Éteindre",
"ButtonSend": "Envoyer", "ButtonSend": "Envoyer",
"ButtonSelectDirectory": "Sélectionner le répertoire", "ButtonSelectDirectory": "Sélectionner le répertoire",
"ButtonScanAllLibraries": "Analyser toutes les médiathèques", "ButtonScanAllLibraries": "Scanner toutes les médiathèques",
"ButtonRevoke": "Révoquer", "ButtonRevoke": "Révoquer",
"ButtonResume": "Reprendre la lecture", "ButtonResume": "Reprendre la lecture",
"ButtonResetEasyPassword": "Remettre à nouveau le code NIP Facile", "ButtonResetEasyPassword": "Remettre à nouveau le code NIP Facile",
@ -215,7 +215,7 @@
"LabelVideo": "Vidéo", "LabelVideo": "Vidéo",
"DashboardArchitecture": "Architecture : {0}", "DashboardArchitecture": "Architecture : {0}",
"DashboardOperatingSystem": "Système d'exploitation: {0}", "DashboardOperatingSystem": "Système d'exploitation: {0}",
"ConfigureDateAdded": "Définissez la façon dont les métadonnées de la date dajout sont déterminées dans le tableau de bord > Bibliothèques > Paramètres NFO", "ConfigureDateAdded": "Définissez la façon dont la métadonnée \"Date d'ajout\" est déterminée dans le Tableau de bord > Bibliothèques > Paramètres NFO",
"Composer": "Compositeur(trice)", "Composer": "Compositeur(trice)",
"CommunityRating": "Évaluation de la communauté", "CommunityRating": "Évaluation de la communauté",
"ColorTransfer": "Transfert de couleur", "ColorTransfer": "Transfert de couleur",
@ -234,9 +234,9 @@
"Episodes": "Épisodes", "Episodes": "Épisodes",
"Episode": "Épisode", "Episode": "Épisode",
"Ended": "Terminé", "Ended": "Terminé",
"EnableThemeSongsHelp": "Jouer les bandes sonores en arrière-plan pendant la navigation de la médiathèque.", "EnableThemeSongsHelp": "Jouer les chansons thème en arrière-plan pendant la navigation de la médiathèque.",
"EnableStreamLoopingHelp": "À activer si le contenu de la diffusion en continu ne contient que quelques secondes de données et doit être rafraîchi sans arrêt. Ne pas activer cette option sans raison car elle peut causer des problèmes.", "EnableStreamLoopingHelp": "Activer cette option si les streams en direct ne contiennent que quelques secondes de données et doivent être continuellement redemandés. Ne pas activer sans raison car cette option peut causer des problèmes.",
"EnableStreamLooping": "Lecture en boucle des diffusions en direct", "EnableStreamLooping": "Lecture en boucle des streams en direct",
"EnableQuickConnect": "Activer la connexion rapide sur ce serveur", "EnableQuickConnect": "Activer la connexion rapide sur ce serveur",
"EnablePhotosHelp": "Les images seront détectées et affichées avec les autres fichiers multimédia.", "EnablePhotosHelp": "Les images seront détectées et affichées avec les autres fichiers multimédia.",
"EnableExternalVideoPlayers": "Lecteurs vidéo externes", "EnableExternalVideoPlayers": "Lecteurs vidéo externes",
@ -257,9 +257,9 @@
"HeaderApiKeys": "Clés API", "HeaderApiKeys": "Clés API",
"HeaderApiKey": "Clé API", "HeaderApiKey": "Clé API",
"DeinterlaceMethodHelp": "Sélectionnez la méthode de désentrelacement à utiliser lorsque le logiciel transcode le contenu entrelacé. Lorsque l'accélération matérielle prenant en charge le désentrelacement matériel est activée, le désentrelaceur matériel sera utilisé à la place de ce paramètre.", "DeinterlaceMethodHelp": "Sélectionnez la méthode de désentrelacement à utiliser lorsque le logiciel transcode le contenu entrelacé. Lorsque l'accélération matérielle prenant en charge le désentrelacement matériel est activée, le désentrelaceur matériel sera utilisé à la place de ce paramètre.",
"DefaultSubtitlesHelp": "Les sous-titres seront chargés selon les marqueurs par défaut et forcé dans les métadonnées intégrées. Les langues préférées seront utilisées quand plusieurs options seront disponibles.", "DefaultSubtitlesHelp": "Les sous-titres seront chargés selon les marqueurs par défaut et forcé dans les métadonnées intégrées. Les préférences de langues seront utilisées quand plusieurs options sont disponibles.",
"DailyAt": "Tous les jours à {0}", "DailyAt": "Tous les jours à {0}",
"Bwdif": "BWDIF", "Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"ButtonPlayer": "Joueur", "ButtonPlayer": "Joueur",
"ButtonCast": "Diffuser sur l'appareil", "ButtonCast": "Diffuser sur l'appareil",
"ApiKeysCaption": "Liste des clés API actuellement activées", "ApiKeysCaption": "Liste des clés API actuellement activées",
@ -284,7 +284,7 @@
"HeaderAccessSchedule": "Programme d'accès", "HeaderAccessSchedule": "Programme d'accès",
"HardwareAccelerationWarning": "L'activation de l'accélération matérielle peut provoquer une instabilité dans certains environnements. Assurez-vous que votre système d'exploitation et vos pilotes vidéo sont complètement à jour. Si vous avez des difficultés pour lire des vidéos après l'activation, vous devrez régler à nouveau ce paramètre sur « Aucun ».", "HardwareAccelerationWarning": "L'activation de l'accélération matérielle peut provoquer une instabilité dans certains environnements. Assurez-vous que votre système d'exploitation et vos pilotes vidéo sont complètement à jour. Si vous avez des difficultés pour lire des vidéos après l'activation, vous devrez régler à nouveau ce paramètre sur « Aucun ».",
"HDPrograms": "Programmes HD", "HDPrograms": "Programmes HD",
"H264CrfHelp": "Le facteur de débit constant (CRF) est le paramètre de qualité par défaut pour les encodeurs x264 et x265. Vous pouvez définir les valeurs entre 0 et 51, où des valeurs inférieures entraîneraient une meilleure qualité (au prix de fichiers lourds). Les valeurs saines sont comprises entre 18 et 28. La valeur par défaut pour x264 est 23 et pour x265 est 28, vous pouvez donc l'utiliser comme point de départ.", "H264CrfHelp": "Le facteur de débit constant (CRF) est le paramètre de qualité par défaut pour les encodeurs logiciels x264 et x265. Vous pouvez définir les valeurs entre 0 et 51, où des valeurs inférieures entraîneraient une meilleure qualité (au prix de fichiers lourds). Les valeurs saines sont comprises entre 18 et 28. La valeur par défaut pour x264 est 23 et pour x265 est 28, vous pouvez donc l'utiliser comme point de départ. Les encodeurs matériels n'utilisent pas ces paramètres.",
"GuideProviderLogin": "Connexion", "GuideProviderLogin": "Connexion",
"Guide": "Guide", "Guide": "Guide",
"GroupVersions": "Regrouper les versions", "GroupVersions": "Regrouper les versions",
@ -312,15 +312,15 @@
"EveryNDays": "Tous les {0} jours", "EveryNDays": "Tous les {0} jours",
"EveryHour": "A chaque heure", "EveryHour": "A chaque heure",
"ErrorStartHourGreaterThanEnd": "La date de fin doit être postérieure à la date de début.", "ErrorStartHourGreaterThanEnd": "La date de fin doit être postérieure à la date de début.",
"ErrorSavingTvProvider": "Une erreur est survenue lors de la sauvegarde du fournisseur TV. Assurez-vous qu'il est accessible et réessayez.", "ErrorSavingTvProvider": "Une erreur est survenue lors de la sauvegarde du fournisseur télé. Assurez-vous qu'il est accessible et réessayez.",
"ErrorPleaseSelectLineup": "Veuillez sélectionner une programmation et réessayer. Si aucune programmation n'est disponible, veuillez vérifier que vos identifiant, mot de passe et code postal sont corrects.", "ErrorPleaseSelectLineup": "Veuillez sélectionner une programmation et réessayer. Si aucune programmation n'est disponible, veuillez vérifier que vos identifiant, mot de passe et code postal sont corrects.",
"ErrorGettingTvLineups": "Une erreur est survenue lors du téléchargement des programmes TV. Assurez-vous que vos informations soient correctes et réessayez.", "ErrorGettingTvLineups": "Une erreur est survenue lors du téléchargement des programmes télé. Assurez-vous que vos informations soient correctes et réessayez.",
"ErrorDeletingItem": "Une erreur s'est produite lors de la suppression de l'élément du serveur. Vérifiez que Jellyfin a un accès en écriture au dossier multimédia et réessayez.", "ErrorDeletingItem": "Une erreur s'est produite lors de la suppression de l'élément du serveur. Vérifiez que Jellyfin a un accès en écriture au dossier multimédia et réessayez.",
"ErrorAddingXmlTvFile": "Une erreur est survenue lors de l'accès au fichier XMLTV. Assurez-vous que le fichier existe et réessayez.", "ErrorAddingXmlTvFile": "Une erreur est survenue lors de l'accès au fichier XMLTV. Assurez-vous que le fichier existe et réessayez.",
"ErrorAddingMediaPathToVirtualFolder": "Une erreur est survenue pendant l'ajout du chemin des médias. Veuillez vérifier que le chemin est valide et que Jellyfin peut y accéder.", "ErrorAddingMediaPathToVirtualFolder": "Une erreur est survenue pendant l'ajout du chemin des médias. Veuillez vérifier que le chemin est valide et que Jellyfin peut y accéder.",
"ErrorAddingListingsToSchedulesDirect": "Une erreur est survenue pendant l'ajout de la programmation avec votre compte Schedules Direct. Schedules Direct autorise uniquement un nombre limité de programmations par compte. Vous devez vous connecter au site Schedules Direct et supprimer d'autres programmations depuis votre compte avant de pouvoir réessayer.", "ErrorAddingListingsToSchedulesDirect": "Une erreur est survenue pendant l'ajout de la programmation avec votre compte Schedules Direct. Schedules Direct autorise uniquement un nombre limité de programmations par compte. Vous devez vous connecter au site Schedules Direct et supprimer d'autres programmations depuis votre compte avant de pouvoir réessayer.",
"EncoderPresetHelp": "Choisissez une valeur plus rapide pour améliorer la performance, ou plus lente pour améliorer la qualité.", "EncoderPresetHelp": "Choisissez une valeur plus rapide pour améliorer la performance, ou plus lente pour améliorer la qualité.",
"EnableThemeVideosHelp": "Lire les thèmes vidéos en arrière-plan en parcourant la médiathèque.", "EnableThemeVideosHelp": "Jouer les génériques en arrière-plan en parcourant la médiathèque.",
"EnableNextVideoInfoOverlayHelp": "À la fin d'une vidéo, afficher les informations sur la vidéo suivante dans la liste de lecture.", "EnableNextVideoInfoOverlayHelp": "À la fin d'une vidéo, afficher les informations sur la vidéo suivante dans la liste de lecture.",
"EnableNextVideoInfoOverlay": "Voir les informations de la vidéo suivante pendant la lecture", "EnableNextVideoInfoOverlay": "Voir les informations de la vidéo suivante pendant la lecture",
"EnableHardwareEncoding": "Activer l'encodage matériel", "EnableHardwareEncoding": "Activer l'encodage matériel",
@ -362,9 +362,9 @@
"DisplayInMyMedia": "Afficher sur lécran daccueil", "DisplayInMyMedia": "Afficher sur lécran daccueil",
"Display": "Affichage", "Display": "Affichage",
"Disc": "Disque", "Disc": "Disque",
"DirectStreaming": "Diffusion en continu directe", "DirectStreaming": "Streaming direct",
"DirectStreamHelp2": "La puissance utilisée par la diffusion en continue directe dépend en général du profil audio. Seul le flux vidéo est sans perte.", "DirectStreamHelp2": "La puissance nécéssaire pour le streaming directe dépend en général du profil audio. Seul le flux vidéo est sans perte.",
"DirectStreamHelp1": "Le flux vidéo est compatible avec l'appareil, mais utilise un format audio (DTS, Dolby TrueHD, etc.) ou un nombre de canaux audio incompatibles. Le média va être rempaqueté à la volée avant d'être diffusé sur l'appareil. Seul le flux audio va être transcodé.", "DirectStreamHelp1": "Le stream vidéo est compatible avec l'appareil, mais utilise un format audio (DTS, Dolby TrueHD, etc.) ou un nombre de canaux audio incompatibles. La vidéo sera rempaqueté à la volée avant d'être envoyé à l'appareil. Seul le stream audio sera transcodé.",
"DirectPlaying": "Lecture directe", "DirectPlaying": "Lecture directe",
"DeviceAccessHelp": "Ceci ne s'applique qu'aux appareils qui peuvent être identifiés de manière unique et n'empêchera pas l'accès par navigateur. Bloquer l'accès aux appareils par utilisateur empêchera l'utilisation de nouveaux appareils jusqu'à ce qu'ils soient approuvés ici.", "DeviceAccessHelp": "Ceci ne s'applique qu'aux appareils qui peuvent être identifiés de manière unique et n'empêchera pas l'accès par navigateur. Bloquer l'accès aux appareils par utilisateur empêchera l'utilisation de nouveaux appareils jusqu'à ce qu'ils soient approuvés ici.",
"Descending": "Décroissant", "Descending": "Décroissant",
@ -388,7 +388,7 @@
"LabelDay": "Jour de la semaine", "LabelDay": "Jour de la semaine",
"LabelDateAdded": "Date d'ajout", "LabelDateAdded": "Date d'ajout",
"LabelCustomDeviceDisplayNameHelp": "Entrez un nom d'affichage personnalisé ou laissez vide pour utiliser le nom rapporté par l'appareil.", "LabelCustomDeviceDisplayNameHelp": "Entrez un nom d'affichage personnalisé ou laissez vide pour utiliser le nom rapporté par l'appareil.",
"LabelCustomCssHelp": "Appliquer votre code CSS pour le thème/l'image de marque personnalisé à l'interface web.", "LabelCustomCssHelp": "Appliquer votre code CSS personalisé pour le thème/branding de l'interface web.",
"LabelCustomCss": "Code CSS personnalisé", "LabelCustomCss": "Code CSS personnalisé",
"LabelCurrentStatus": "État actuel", "LabelCurrentStatus": "État actuel",
"LabelCurrentPassword": "Mot de passe actuel", "LabelCurrentPassword": "Mot de passe actuel",
@ -399,13 +399,13 @@
"LabelCollection": "Collection", "LabelCollection": "Collection",
"LabelChromecastVersion": "Version de Google Cast", "LabelChromecastVersion": "Version de Google Cast",
"LabelChannels": "Chaînes", "LabelChannels": "Chaînes",
"LabelCertificatePasswordHelp": "Si votre certificat nécessite un mot de passe, veuillez le saisir ici.", "LabelCertificatePasswordHelp": "Si votre certificat nécessite un mot de passe, veuillez l'entrer ici.",
"LabelCertificatePassword": "Mot de passe du certificat", "LabelCertificatePassword": "Mot de passe du certificat",
"LabelCancelled": "Annulé", "LabelCancelled": "Annulé",
"LabelBirthYear": "Année de naissance", "LabelBirthYear": "Année de naissance",
"LabelBirthDate": "Date de naissance", "LabelBirthDate": "Date de naissance",
"LabelArtists": "Artistes", "LabelArtists": "Artistes",
"LabelAppNameExample": "Examples : Sickbeard, Sonarr", "LabelAppNameExample": "Nom humainement lisible pour identifier les clés API. Ce paramètre n'affecte pas les fonctionnalités.",
"LabelAppName": "Nom de l'application", "LabelAppName": "Nom de l'application",
"LabelAlbum": "Album", "LabelAlbum": "Album",
"LabelAirTime": "Heure de diffusion", "LabelAirTime": "Heure de diffusion",
@ -501,7 +501,7 @@
"HeaderIdentificationHeader": "En-tête d'identification", "HeaderIdentificationHeader": "En-tête d'identification",
"HeaderIdentificationCriteriaHelp": "Saisissez au moins un critère d'identification.", "HeaderIdentificationCriteriaHelp": "Saisissez au moins un critère d'identification.",
"HeaderIdentification": "Identification", "HeaderIdentification": "Identification",
"HeaderGuideProviders": "Fournisseurs de données de guides TV", "HeaderGuideProviders": "Fournisseurs de données des guides télé",
"HeaderForKids": "Pour les enfants", "HeaderForKids": "Pour les enfants",
"HeaderFetchImages": "Récupérer les images", "HeaderFetchImages": "Récupérer les images",
"HeaderFetcherSettings": "Paramètres du récupérateur", "HeaderFetcherSettings": "Paramètres du récupérateur",
@ -542,17 +542,17 @@
"DoNotRecord": "Ne pas enregistrer", "DoNotRecord": "Ne pas enregistrer",
"HeaderDebugging": "Débogage et traçage", "HeaderDebugging": "Débogage et traçage",
"HeaderContainerProfile": "Profile conteneur", "HeaderContainerProfile": "Profile conteneur",
"EnableTonemapping": "Activer la correspondance de tonalité", "EnableTonemapping": "Activer le mappage tonal",
"EnableBlurHash": "Activer remplacements brouillés pour les images", "EnableBlurHash": "Activer remplacements brouillés pour les images",
"DisplayMissingEpisodesWithinSeasonsHelp": "Ceci doit aussi être activé pour les médiathèques télé dans la configuration serveur.", "DisplayMissingEpisodesWithinSeasonsHelp": "Ceci doit aussi être activé pour les médiathèques télé dans la configuration serveur.",
"InstantMix": "Mix instantanné", "InstantMix": "Mix instantanné",
"ImportFavoriteChannelsHelp": "Seul les chaînes favorites du syntoniseur seront importé.", "ImportFavoriteChannelsHelp": "Seul les chaînes favorites du syntoniseur seront importées.",
"HttpsRequiresCert": "Pour activer les connections sécurisées, il faut fournir un certificat SSL reconnu, tel Let's Encrypt. Fournissez un certificat ou désactivez les connections sécurisées.", "HttpsRequiresCert": "Pour activer les connections sécurisées, il faut fournir un certificat SSL reconnu, tel Let's Encrypt. Fournissez un certificat ou désactivez les connections sécurisées.",
"HeaderXmlSettings": "Options XML", "HeaderXmlSettings": "Options XML",
"HeaderXmlDocumentAttributes": "Attributs de document XML", "HeaderXmlDocumentAttributes": "Attributs de document XML",
"HeaderXmlDocumentAttribute": "Attribut de document XML", "HeaderXmlDocumentAttribute": "Attribut de document XML",
"HeaderUploadSubtitle": "téléverser des sous-titres", "HeaderUploadSubtitle": "Uploader des sous-titres",
"HeaderUploadImage": "téléverser une image", "HeaderUploadImage": "Uploader une image",
"HeaderUpcomingOnTV": "Bientôt à la télé", "HeaderUpcomingOnTV": "Bientôt à la télé",
"HeaderUninstallPlugin": "Désinstaller l'extension", "HeaderUninstallPlugin": "Désinstaller l'extension",
"HeaderTunerDevices": "Syntoniseur", "HeaderTunerDevices": "Syntoniseur",
@ -576,7 +576,7 @@
"HeaderOnNow": "Jouant maintenant", "HeaderOnNow": "Jouant maintenant",
"HeaderNewApiKey": "Nouvelle clef d'API", "HeaderNewApiKey": "Nouvelle clef d'API",
"HeaderNetworking": "Protocoles IP", "HeaderNetworking": "Protocoles IP",
"HeaderLiveTvTunerSetup": "Configuration syntoniseur télé en direct", "HeaderLiveTvTunerSetup": "Configuration du syntoniseur télé en direct",
"HeaderKodiMetadataHelp": "Pour (dés)activer les métadonnées NFO, modifiez une médiathèque et teouver la section \"Métadonnées\".", "HeaderKodiMetadataHelp": "Pour (dés)activer les métadonnées NFO, modifiez une médiathèque et teouver la section \"Métadonnées\".",
"LabelMetadataSavers": "Métadonnées", "LabelMetadataSavers": "Métadonnées",
"HeaderInstantMix": "Mix instantané", "HeaderInstantMix": "Mix instantané",
@ -591,7 +591,7 @@
"HeaderConfirmRevokeApiKey": "Révoquer la clef d'API", "HeaderConfirmRevokeApiKey": "Révoquer la clef d'API",
"HeaderCodecProfileHelp": "Les profils de codec indiquent les codecs supportés par un appareil. Si un codec n'est pas supporté, le média sera transcodé, même si ce codec est configuré pour la lecture directe.", "HeaderCodecProfileHelp": "Les profils de codec indiquent les codecs supportés par un appareil. Si un codec n'est pas supporté, le média sera transcodé, même si ce codec est configuré pour la lecture directe.",
"HeaderCastAndCrew": "Distribution et équipe", "HeaderCastAndCrew": "Distribution et équipe",
"HeaderBranding": "Slogan", "HeaderBranding": "Branding",
"LabelAudioSampleRate": "Taux déchantillonnage audio", "LabelAudioSampleRate": "Taux déchantillonnage audio",
"LabelAudioLanguagePreference": "Langue audio préférée", "LabelAudioLanguagePreference": "Langue audio préférée",
"LabelAudioCodec": "Codec audio", "LabelAudioCodec": "Codec audio",
@ -639,7 +639,7 @@
"ShowYear": "Montrer l'année", "ShowYear": "Montrer l'année",
"ShowTitle": "Montrer le titre", "ShowTitle": "Montrer le titre",
"LabelDropImageHere": "Glisser l'image ici, ou cliquez pour parcourir vos fichiers.", "LabelDropImageHere": "Glisser l'image ici, ou cliquez pour parcourir vos fichiers.",
"LabelDownMixAudioScale": "Booster l'audio lors du downmix", "LabelDownMixAudioScale": "Boost audio lors du downmixage",
"LabelBitrate": "Débit binaire", "LabelBitrate": "Débit binaire",
"LabelDidlMode": "Mode DIDL", "LabelDidlMode": "Mode DIDL",
"LabelDeinterlaceMethod": "Méthode pour désentrelacer", "LabelDeinterlaceMethod": "Méthode pour désentrelacer",
@ -701,7 +701,7 @@
"Small": "Petit", "Small": "Petit",
"SkipEpisodesAlreadyInMyLibraryHelp": "Les épisodes seront comparés en utilisant les saisons et les numéros d'épisodes, si disponibles.", "SkipEpisodesAlreadyInMyLibraryHelp": "Les épisodes seront comparés en utilisant les saisons et les numéros d'épisodes, si disponibles.",
"Schedule": "Planning", "Schedule": "Planning",
"ScanLibrary": "Scanner la bibliothèque", "ScanLibrary": "Scanner la médiathèque",
"SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Ces paramètres s'appliquent également à toute lecture Google Cast démarrée par cet appareil.", "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "Ces paramètres s'appliquent également à toute lecture Google Cast démarrée par cet appareil.",
"Subtitle": "Sous-titres", "Subtitle": "Sous-titres",
"Studios": "Studios", "Studios": "Studios",
@ -709,12 +709,12 @@
"Sports": "Sports", "Sports": "Sports",
"LabelEmbedAlbumArtDidl": "Intégrer la couverture d'album dans le DIDL", "LabelEmbedAlbumArtDidl": "Intégrer la couverture d'album dans le DIDL",
"DisableCustomCss": "Désactiver le CSS personnalisé fourni par le serveur", "DisableCustomCss": "Désactiver le CSS personnalisé fourni par le serveur",
"LabelDisableCustomCss": "Désactiver le code CSS personnalisé pour theme/l'image de marque fournie par le serveur.", "LabelDisableCustomCss": "Désactiver le code CSS personnalisé pour le thème/branding fournie par le serveur.",
"HeaderContinueReading": "Continuer à lire", "HeaderContinueReading": "Continuer à lire",
"MusicVideos": "Vidéos musicales", "MusicVideos": "Vidéoclips",
"OptionBluray": "BD", "OptionBluray": "BD",
"LabelEnableDlnaPlayTo": "Activer la fonction DLNA \"Play To\"", "LabelEnableDlnaPlayTo": "Activer la fonction DLNA \"Play To\"",
"Engineer": "Ingénieur du son", "Engineer": "Ingénieur de son",
"Conductor": "Conducteur", "Conductor": "Conducteur",
"Casual": "Casuel", "Casual": "Casuel",
"LabelEnableDlnaPlayToHelp": "Détecte les appareils de votre réseau et offre la possibilité de les contrôler à distance.", "LabelEnableDlnaPlayToHelp": "Détecte les appareils de votre réseau et offre la possibilité de les contrôler à distance.",
@ -728,13 +728,13 @@
"ErrorPlayerNotFound": "Aucun lecteur trouvé pour le média demandé.", "ErrorPlayerNotFound": "Aucun lecteur trouvé pour le média demandé.",
"Console": "Console", "Console": "Console",
"AgeValue": "({0} an(s))", "AgeValue": "({0} an(s))",
"MessagePasswordResetForUsers": "Les utilisateurs suivants ont vu leur mot de passe réinitialisé. Ils peuvent se connecter avec les codes PIN qui ont été utilisés pour faire la réinitialisation.", "MessagePasswordResetForUsers": "Les utilisateurs suivants ont vu leur mot de passes réinitialisés. Ils peuvent se connecter avec les codes PIN qui ont été utilisés pendant la réinitialisation.",
"MessageNothingHere": "Rien ici.", "MessageNothingHere": "Rien ici.",
"MessageNoServersAvailable": "Aucun serveur n'a été trouvé par la découverte de serveur automatique.", "MessageNoServersAvailable": "Aucun serveur n'a été trouvé par la découverte de serveur automatique.",
"MessageNoRepositories": "Pas de dépôts.", "MessageNoRepositories": "Pas de dépôts.",
"MessageNoPluginsInstalled": "Vous n'avez pas de plugins installés.", "MessageNoPluginsInstalled": "Vous n'avez pas de plugins installés.",
"MessageNoPluginConfiguration": "Ce plugin n'a pas de paramètres à configurer.", "MessageNoPluginConfiguration": "Ce plugin n'a pas de paramètres à configurer.",
"MessageNoNextUpItems": "Rien n'a été trouvé. Commencez à regarder vos émissions !", "MessageNoNextUpItems": "Rien à continuer. Commencez à regarder vos émissions !",
"MessageNoMovieSuggestionsAvailable": "Aucune suggestion de film n'est disponible. Commencez à regarder et noter vos films, pour avoir des recommandations.", "MessageNoMovieSuggestionsAvailable": "Aucune suggestion de film n'est disponible. Commencez à regarder et noter vos films, pour avoir des recommandations.",
"MessageNoGenresAvailable": "Utiliser des fournisseurs de métadonnées pour récupérer les genres depuis internet.", "MessageNoGenresAvailable": "Utiliser des fournisseurs de métadonnées pour récupérer les genres depuis internet.",
"MessageNoAvailablePlugins": "Aucun plugin disponible.", "MessageNoAvailablePlugins": "Aucun plugin disponible.",
@ -742,11 +742,11 @@
"MessageInvalidUser": "Nom d'utilisateur ou mot de passe invalide. Réessayer.", "MessageInvalidUser": "Nom d'utilisateur ou mot de passe invalide. Réessayer.",
"MessageInvalidForgotPasswordPin": "Un code PIN invalide ou expiré a été saisi. Réessayer.", "MessageInvalidForgotPasswordPin": "Un code PIN invalide ou expiré a été saisi. Réessayer.",
"MessageImageTypeNotSelected": "Sélectionner un type d'image dans le menu déroulant.", "MessageImageTypeNotSelected": "Sélectionner un type d'image dans le menu déroulant.",
"MessageImageFileTypeAllowed": "Seuls les fichiers JPEG et PNG sont pris en charge.", "MessageImageFileTypeAllowed": "Seuls les fichiers JPEG et PNG sont supportés.",
"MessageGetInstalledPluginsError": "Une erreur s'est produite lors de la récupération de la liste des plugins actuellement installés.", "MessageGetInstalledPluginsError": "Une erreur s'est produite lors de la récupération de la liste des plugins actuellement installés.",
"MessageForgotPasswordFileCreated": "Le fichier suivant a été créé sur votre serveur et contient des instructions sur la marche à suivre", "MessageForgotPasswordFileCreated": "Le fichier suivant a été créé sur votre serveur et contient des instructions sur la marche à suivre",
"MessageFileReadError": "Une erreur s'est produite lors de la lecture du fichier. Réessayer.", "MessageFileReadError": "Une erreur s'est produite lors de la lecture du fichier. Réessayer.",
"MessageEnablingOptionLongerScans": "L'activation de cette option peut entraîner des analyses de bibliothèque beaucoup plus longues.", "MessageEnablingOptionLongerScans": "L'activation de cette option peut entraîner des scans de médiathèque beaucoup plus longues.",
"MessageDownloadQueued": "Téléchargement en file d'attente.", "MessageDownloadQueued": "Téléchargement en file d'attente.",
"MessageDeleteTaskTrigger": "Es-tu sûr de vouloir supprimer ce déclencheur de tâche ?", "MessageDeleteTaskTrigger": "Es-tu sûr de vouloir supprimer ce déclencheur de tâche ?",
"MessageCreateAccountAt": "Créez un compte sur {0}", "MessageCreateAccountAt": "Créez un compte sur {0}",
@ -757,7 +757,7 @@
"MessageConfirmRemoveMediaLocation": "Es-tu sûr de vouloir supprimer cet emplacement ?", "MessageConfirmRemoveMediaLocation": "Es-tu sûr de vouloir supprimer cet emplacement ?",
"MessageConfirmRecordingCancellation": "Canceller l'enregistrement ?", "MessageConfirmRecordingCancellation": "Canceller l'enregistrement ?",
"MessageConfirmDeleteTunerDevice": "Es-tu sûr de vouloir supprimer cet appareil ?", "MessageConfirmDeleteTunerDevice": "Es-tu sûr de vouloir supprimer cet appareil ?",
"MessageConfirmDeleteGuideProvider": "Es-tu sûr de vouloir supprimer ce fournisseur de guide TV ?", "MessageConfirmDeleteGuideProvider": "Es-tu sûr de vouloir supprimer ce fournisseur de guide télé ?",
"MessageConfirmAppExit": "Veux-tu sortir ?", "MessageConfirmAppExit": "Veux-tu sortir ?",
"MessageAreYouSureYouWishToRemoveMediaFolder": "Es-tu sûr de vouloir supprimer ce dossier multimédia ?", "MessageAreYouSureYouWishToRemoveMediaFolder": "Es-tu sûr de vouloir supprimer ce dossier multimédia ?",
"MessageAreYouSureDeleteSubtitles": "Es-tu sûr de vouloir supprimer ce fichier de sous-titre ?", "MessageAreYouSureDeleteSubtitles": "Es-tu sûr de vouloir supprimer ce fichier de sous-titre ?",
@ -783,7 +783,7 @@
"MediaInfoChannels": "Chaînes", "MediaInfoChannels": "Chaînes",
"MediaInfoBitDepth": "Profondeur de couleur", "MediaInfoBitDepth": "Profondeur de couleur",
"MediaInfoAspectRatio": "Rapport de forme", "MediaInfoAspectRatio": "Rapport de forme",
"LiveTV": "TV en direct", "LiveTV": "Télé en direct",
"MediaInfoBitrate": "Débit binaire", "MediaInfoBitrate": "Débit binaire",
"MediaInfoForced": "Forcé", "MediaInfoForced": "Forcé",
"MediaInfoTitle": "Titre", "MediaInfoTitle": "Titre",
@ -793,10 +793,10 @@
"MessageForgotPasswordInNetworkRequired": "Veuillez réessayer sur votre réseau local pour lancer la réinitialisation du mot de passe.", "MessageForgotPasswordInNetworkRequired": "Veuillez réessayer sur votre réseau local pour lancer la réinitialisation du mot de passe.",
"LabelInNetworkSignInWithEasyPasswordHelp": "Utilisez le code PIN pour vous connecter sur votre réseau local. Votre mot de passe habituel ne sera nécessaire qu'à l'extérieur de du réseau local. Si le code PIN est vide, vous n'aurez pas besoin d'un mot de passe sur votre réseau local.", "LabelInNetworkSignInWithEasyPasswordHelp": "Utilisez le code PIN pour vous connecter sur votre réseau local. Votre mot de passe habituel ne sera nécessaire qu'à l'extérieur de du réseau local. Si le code PIN est vide, vous n'aurez pas besoin d'un mot de passe sur votre réseau local.",
"LabelHDHomerunPortRange": "Plage de ports HDHomeRun", "LabelHDHomerunPortRange": "Plage de ports HDHomeRun",
"OptionEnableExternalContentInSuggestionsHelp": "Autoriser l'inclusion de bandes-annonces sur Internet et d'émissions de TV en direct dans le contenu suggéré.", "OptionEnableExternalContentInSuggestionsHelp": "Autoriser l'inclusion de bandes-annonces sur Internet et d'émissions de télé en direct dans le contenu suggéré.",
"OptionEnableExternalContentInSuggestions": "Activer le contenu externe dans les suggestions", "OptionEnableExternalContentInSuggestions": "Activer le contenu externe dans les suggestions",
"OptionEnableAccessToAllLibraries": "Activer l'accès à toutes les bibliothèques", "OptionEnableAccessToAllLibraries": "Activer l'accès à toutes les bibliothèques",
"OptionEnableAccessToAllChannels": "Activer l'accès à toutes les chaînes de TV", "OptionEnableAccessToAllChannels": "Activer l'accès à toutes les chaînes",
"OptionEnableAccessFromAllDevices": "Autoriser l'accès depuis tous les appareils", "OptionEnableAccessFromAllDevices": "Autoriser l'accès depuis tous les appareils",
"OptionEmbedSubtitles": "Intégrer dans l'conteneur", "OptionEmbedSubtitles": "Intégrer dans l'conteneur",
"OptionDvd": "DVD", "OptionDvd": "DVD",
@ -824,14 +824,14 @@
"OptionAllowRemoteSharedDevicesHelp": "Les appareils DLNA sont considérés comme partagés tant qu'un utilisateur ne commence pas à les contrôler.", "OptionAllowRemoteSharedDevicesHelp": "Les appareils DLNA sont considérés comme partagés tant qu'un utilisateur ne commence pas à les contrôler.",
"OptionAllowRemoteSharedDevices": "Autoriser le contrôle à distance des appareils partagés", "OptionAllowRemoteSharedDevices": "Autoriser le contrôle à distance des appareils partagés",
"OptionAllowRemoteControlOthers": "Autoriser le contrôle à distance de d'autres utilisateurs", "OptionAllowRemoteControlOthers": "Autoriser le contrôle à distance de d'autres utilisateurs",
"OptionAllowMediaPlaybackTranscodingHelp": "Limiter l'accès au transcodage peut entraîner des échecs de lecture dans les clients en raison de formats multimédias non pris en charge.", "OptionAllowMediaPlaybackTranscodingHelp": "Limiter l'accès au transcodage peut entraîner des échecs de lecture sur les clients en raison de formats multimédias non supportés.",
"OptionAllowMediaPlayback": "Autoriser la lecture multimédia", "OptionAllowMediaPlayback": "Autoriser la lecture multimédia",
"OptionAllowManageLiveTv": "Autoriser la gestion des enregistrements TV en direct", "OptionAllowManageLiveTv": "Autoriser la gestion des enregistrements télé en direct",
"OptionAllowLinkSharingHelp": "Seules les pages Web contenant des informations sur les médias sont partagées. Les fichiers multimédias ne sont jamais partagés publiquement. Les partages sont limités dans le temps et expireront après {0} jours.", "OptionAllowLinkSharingHelp": "Seules les pages Web contenant des informations sur les médias sont partagées. Les fichiers multimédias ne sont jamais partagés publiquement. Les partages sont limités dans le temps et expireront après {0} jours.",
"OptionAllowLinkSharing": "Autoriser le partage sur les réseaux sociaux", "OptionAllowLinkSharing": "Autoriser le partage sur les réseaux sociaux",
"OptionAllowContentDownloadHelp": "Les utilisateurs peuvent télécharger les médias pour les stocker sur leurs appareils. Ce n'est pas la même chose qu'une fonction de synchronisation. Les bibliothèques de livres nécessitent cette activation pour fonctionner correctement.", "OptionAllowContentDownloadHelp": "Les utilisateurs peuvent télécharger les médias pour les stocker sur leurs appareils. Ce n'est pas la même chose qu'une fonction de synchronisation. Les bibliothèques de livres nécessitent cette activation pour fonctionner correctement.",
"OptionAllowContentDownload": "Autoriser les téléchargements des fichiers", "OptionAllowContentDownload": "Autoriser les téléchargements des fichiers",
"OptionAllowBrowsingLiveTv": "Autoriser l'accès à la TV en direct", "OptionAllowBrowsingLiveTv": "Autoriser l'accès à la télé en direct",
"OptionAllowAudioPlaybackTranscoding": "Autoriser la lecture audio nécessitant un transcodage", "OptionAllowAudioPlaybackTranscoding": "Autoriser la lecture audio nécessitant un transcodage",
"OptionAdminUsers": "Administrateurs", "OptionAdminUsers": "Administrateurs",
"Option3D": "3D", "Option3D": "3D",
@ -860,7 +860,7 @@
"Name": "Nom", "Name": "Nom",
"MySubtitles": "Mes sous-titres", "MySubtitles": "Mes sous-titres",
"Mute": "Muet", "Mute": "Muet",
"MusicVideo": "Vidéo musical", "MusicVideo": "Vidéoclip",
"MusicLibraryHelp": "Consultez le {0}guide de dénomination de musique{1}.", "MusicLibraryHelp": "Consultez le {0}guide de dénomination de musique{1}.",
"MusicArtist": "Artiste de musique", "MusicArtist": "Artiste de musique",
"MusicAlbum": "Album de musique", "MusicAlbum": "Album de musique",
@ -908,17 +908,17 @@
"MessagePlayAccessRestricted": "La lecture de ce contenu est restreinte. Contactez l'administrateur pour plus d'informations.", "MessagePlayAccessRestricted": "La lecture de ce contenu est restreinte. Contactez l'administrateur pour plus d'informations.",
"Arranger": "Arrangeur", "Arranger": "Arrangeur",
"AddToFavorites": "Ajouter aux favoris", "AddToFavorites": "Ajouter aux favoris",
"EnableRewatchingNextUp": "Activer le re-visionnage dans \"La Prochaine\"", "EnableRewatchingNextUp": "Activer le re-visionnage dans \"À suivre\"",
"Digital": "Numérique", "Digital": "Numérique",
"Cursive": "Cursive", "Cursive": "Cursive",
"CopyFailed": "Me pouvait pas copier", "CopyFailed": "Impossible de copier",
"Copy": "Copie", "Copy": "Copier",
"Copied": "Copié", "Copied": "Copié",
"ButtonSpace": "Espace", "ButtonSpace": "Espace",
"ButtonExitApp": "Ferme l'application", "ButtonExitApp": "Ferme l'application",
"ButtonClose": "Fermer", "ButtonClose": "Fermer",
"ButtonBackspace": "Retour Arrière", "ButtonBackspace": "Retour Arrière",
"LabelEnableRealtimeMonitorHelp": "Les modifications apportées aux fichiers seront traitées immédiatement sur les systèmes de fichiers pris en charge.", "LabelEnableRealtimeMonitorHelp": "Les modifications apportées aux fichiers seront traitées immédiatement sur les systèmes de fichiers supportés.",
"LabelEnableRealtimeMonitor": "Activer la surveillance en temps réel", "LabelEnableRealtimeMonitor": "Activer la surveillance en temps réel",
"LabelEnableIP6Help": "Activez la fonctionnalité IPv6.", "LabelEnableIP6Help": "Activez la fonctionnalité IPv6.",
"LabelEnableIP6": "Activez IPv6", "LabelEnableIP6": "Activez IPv6",
@ -930,7 +930,7 @@
"LabelEnableDlnaServerHelp": "Autoriser les appareils UPnP de votre réseau à parcourir et à lire du contenu.", "LabelEnableDlnaServerHelp": "Autoriser les appareils UPnP de votre réseau à parcourir et à lire du contenu.",
"LabelEnableDlnaServer": "Activer le serveur DLNA", "LabelEnableDlnaServer": "Activer le serveur DLNA",
"LabelEnableBlastAliveMessages": "Diffuser les messages 'vivante'", "LabelEnableBlastAliveMessages": "Diffuser les messages 'vivante'",
"LabelDownMixAudioScaleHelp": "Amplifiez l'audio lors du sous-mixage. Une valeur de un préservera le volume d'origine.", "LabelDownMixAudioScaleHelp": "Amplifier l'audio lors du downmix. Une valeur de 1 préservera le volume d'origine.",
"LabelAutomaticallyAddToCollectionHelp": "Lorsqu'au moins 2 films ont le même nom de collection, ils seront automatiquement ajoutés à la collection.", "LabelAutomaticallyAddToCollectionHelp": "Lorsqu'au moins 2 films ont le même nom de collection, ils seront automatiquement ajoutés à la collection.",
"LabelAutomaticallyAddToCollection": "Ajouter automatiquement à la collection", "LabelAutomaticallyAddToCollection": "Ajouter automatiquement à la collection",
"ItemDetails": "Détails de L'article", "ItemDetails": "Détails de L'article",
@ -938,7 +938,7 @@
"HeaderSyncPlayTimeSyncSettings": "Synchronisation de L'heure", "HeaderSyncPlayTimeSyncSettings": "Synchronisation de L'heure",
"HeaderSyncPlayPlaybackSettings": "Relecture", "HeaderSyncPlayPlaybackSettings": "Relecture",
"GoogleCastUnsupported": "Google cast non supporté", "GoogleCastUnsupported": "Google cast non supporté",
"EnableRewatchingNextUpHelp": "Activezr l'affichage des épisodes déjà regardés dans les sections \"La Prochaine\".", "EnableRewatchingNextUpHelp": "Activer l'affichage des épisodes déjà regardés dans les sections \"À suivre\".",
"MessageLeaveEmptyToInherit": "Laisser vide pour hériter des paramètres d'un item parent ou de la valeur par défaut globale.", "MessageLeaveEmptyToInherit": "Laisser vide pour hériter des paramètres d'un item parent ou de la valeur par défaut globale.",
"IgnoreDts": "Désactiver DTS (horodateur de décodage)", "IgnoreDts": "Désactiver DTS (horodateur de décodage)",
"IgnoreDtsHelp": "Désactiver cette option peu résoudre des problèmes, par exemple: canal audio manquant sur des chaînes ayant des flux audios et vidéos distincts.", "IgnoreDtsHelp": "Désactiver cette option peu résoudre des problèmes, par exemple: canal audio manquant sur des chaînes ayant des flux audios et vidéos distincts.",
@ -951,8 +951,8 @@
"LabelEpisodeNumber": "Numéro d'épisode", "LabelEpisodeNumber": "Numéro d'épisode",
"LabelEvent": "Évènement", "LabelEvent": "Évènement",
"LabelEveryXMinutes": "Tous les", "LabelEveryXMinutes": "Tous les",
"LabelExtractChaptersDuringLibraryScan": "Extraire les images des chapitres pendant l'actualisation de la médiathèque", "LabelExtractChaptersDuringLibraryScan": "Extraire les images des chapitres pendant le scan de la médiathèque",
"LabelExtractChaptersDuringLibraryScanHelp": "Générer les images des chapitres lors de l'importation de vidéos pendant l'actualisation de la médiathèque. Sinon, elles seront extraites pendant la tâche planifiée des images des chapitres, permettant de terminer plus rapidement l'actualisation de la médiathèque.", "LabelExtractChaptersDuringLibraryScanHelp": "Générer les images des chapitres lors de l'importation de vidéos pendant le scan de la médiathèque. Sinon, elles seront extraites pendant la tâche planifiée des images des chapitres, permettant de terminer plus rapidement l'actualisation de la médiathèque.",
"LabelFailed": "Échoué", "LabelFailed": "Échoué",
"LabelForgotPasswordUsernameHelp": "Saisissez votre nom d'utilisateur, si vous vous en souvenez.", "LabelForgotPasswordUsernameHelp": "Saisissez votre nom d'utilisateur, si vous vous en souvenez.",
"LabelImportOnlyFavoriteChannels": "Restreindre aux chaînes ajoutées aux favoris", "LabelImportOnlyFavoriteChannels": "Restreindre aux chaînes ajoutées aux favoris",
@ -977,12 +977,12 @@
"LabelFormat": "Format", "LabelFormat": "Format",
"LabelFolder": "Dossier", "LabelFolder": "Dossier",
"Experimental": "Expérimental", "Experimental": "Expérimental",
"LabelStereoDownmixAlgorithm": "Algorithme de mixage réducteur en stéréo", "LabelStereoDownmixAlgorithm": "Algorithme de downmixage en stéréo",
"LabelHardwareAccelerationTypeHelp": "L'accélération matérielle nécessite une configuration supplémentaire.", "LabelHardwareAccelerationTypeHelp": "L'accélération matérielle nécessite une configuration supplémentaire.",
"LabelHDHomerunPortRangeHelp": "Restreint la plage de ports UDP pour HDHomeRun à cette valeur. (La plage par défaut est 1024 - 65535).", "LabelHDHomerunPortRangeHelp": "Restreint la plage de ports UDP pour HDHomeRun à cette valeur. (La plage par défaut est 1024 - 65535).",
"LabelHttpsPortHelp": "Le numéro de port TCP pour le serveur HTTPS.", "LabelHttpsPortHelp": "Le numéro de port TCP pour le serveur HTTPS.",
"DownloadAll": "Tout télécharger", "DownloadAll": "Tout télécharger",
"LabelMaxDaysForNextUpHelp": "Définir le nombre maximal de jours sans regarder une série avant qu'elle ne soit enlevée de la section « À suivre ».", "LabelMaxDaysForNextUpHelp": "Définir le nombre maximal de jours sans regarder une série avant qu'elle ne soit enlevée de la section \"À suivre\".",
"LabelLocalCustomCss": "Style CSS personnalisé sappliquant à ce client seul. Désactiver le code CSS personnalisé fourni par le serveur pourrait s'avérer nécessaire.", "LabelLocalCustomCss": "Style CSS personnalisé sappliquant à ce client seul. Désactiver le code CSS personnalisé fourni par le serveur pourrait s'avérer nécessaire.",
"LabelMaxAudiobookResumeHelp": "Les titres seront assumés entièrement lus lorsque le temps restant est inférieur à cette valeur.", "LabelMaxAudiobookResumeHelp": "Les titres seront assumés entièrement lus lorsque le temps restant est inférieur à cette valeur.",
"LabelMaxMuxingQueueSizeHelp": "Nombre maximal de paquets pouvant être mis en mémoire tampon lors de l'initialisation des flux. Augmenter la valeur si des messages \"Trop de paquets en mémoire tampon pour le flux de sortie\" apparaissent dans les journaux FFmpeg. La valeur recommandée est 2048.", "LabelMaxMuxingQueueSizeHelp": "Nombre maximal de paquets pouvant être mis en mémoire tampon lors de l'initialisation des flux. Augmenter la valeur si des messages \"Trop de paquets en mémoire tampon pour le flux de sortie\" apparaissent dans les journaux FFmpeg. La valeur recommandée est 2048.",
@ -1000,7 +1000,7 @@
"LabelMaxBackdropsPerItem": "Nombre maximal d'images d'arrière-plan par élément", "LabelMaxBackdropsPerItem": "Nombre maximal d'images d'arrière-plan par élément",
"LabelLineup": "Programmation", "LabelLineup": "Programmation",
"LabelMatchType": "Type recherché", "LabelMatchType": "Type recherché",
"LabelMaxDaysForNextUp": "Nombre de jours maximal dans « À suivre »", "LabelMaxDaysForNextUp": "Nombre de jours maximal dans \"À suivre\"",
"LabelIsForced": "Forcé", "LabelIsForced": "Forcé",
"LabelInNetworkSignInWithEasyPassword": "Activer l'authentification locale avec un code Easy PIN", "LabelInNetworkSignInWithEasyPassword": "Activer l'authentification locale avec un code Easy PIN",
"LabelInternetQuality": "Qualité d'Internet", "LabelInternetQuality": "Qualité d'Internet",
@ -1052,7 +1052,7 @@
"LabelProfileCodecs": "Codecs", "LabelProfileCodecs": "Codecs",
"LabelProtocolInfoHelp": "La valeur utilisée pour répondre aux requêtes « GetProtocolInfo » de lappareil.", "LabelProtocolInfoHelp": "La valeur utilisée pour répondre aux requêtes « GetProtocolInfo » de lappareil.",
"Bold": "Gras", "Bold": "Gras",
"LabelTonemappingDesatHelp": "Applique la desaturation pour les points points forts qui dépassent ce niveau de luminosité. Plus la valeur est élevée, plus les informations de couleur seront préservées. Ce paramètre permet dempêcher les couleurs super saturées en les tournant graduellement au blanc afin de produire une image plus naturelle, au détriment de la fidélité de couleurs. Les valeurs recommandées sont 0 et 0.5.", "LabelTonemappingDesatHelp": "Applique la désaturation pour les points forts qui dépassent ce niveau de luminosité. Plus la valeur est élevée, plus les informations de couleur seront préservées. Ce paramètre permet dempêcher les couleurs super saturées en les tournant graduellement au blanc afin de produire une image plus naturelle, au détriment de la fidélité de couleurs. Les valeurs recommandées sont 0 et 0.5.",
"LabelTonemappingParamHelp": "Paramètre pour régler lalgorithme de tone-mapping. La valeur par défaut et recommandée est vide.", "LabelTonemappingParamHelp": "Paramètre pour régler lalgorithme de tone-mapping. La valeur par défaut et recommandée est vide.",
"LabelSyncPlayLeaveGroupDescription": "Désactiver SyncPlay", "LabelSyncPlayLeaveGroupDescription": "Désactiver SyncPlay",
"LabelSyncPlayNewGroup": "Nouveau groupe", "LabelSyncPlayNewGroup": "Nouveau groupe",
@ -1132,7 +1132,7 @@
"LabelPostProcessorArguments": "Arguments de ligne de commande pour lapplication de post-traitement", "LabelPostProcessorArguments": "Arguments de ligne de commande pour lapplication de post-traitement",
"EnableAudioNormalizationHelp": "La normalisation audio ajoutera un gain constant pour maintenir un niveau désiré (-18dB).", "EnableAudioNormalizationHelp": "La normalisation audio ajoutera un gain constant pour maintenir un niveau désiré (-18dB).",
"EnableAudioNormalization": "Normalisation audio", "EnableAudioNormalization": "Normalisation audio",
"LabelEnableLUFSScanHelp": "Les clients peuvent normaliser la lecture audio pour obtenir une intensité sonore égale parmi les pistes. Cela rendra les analyses de la bibliothèque plus longues et plus demandantes en ressources.", "LabelEnableLUFSScanHelp": "Les clients peuvent normaliser la lecture audio pour obtenir une intensité sonore égale parmi les pistes. Cela rendra les scan de la médiathèque plus longues et plus demandantes en ressources.",
"LabelParallelImageEncodingLimit": "Limite dencodage dimage en parallèle", "LabelParallelImageEncodingLimit": "Limite dencodage dimage en parallèle",
"LabelProtocolInfo": "Infos protocole", "LabelProtocolInfo": "Infos protocole",
"LabelRecordingPath": "Chemin par défaut pour lenregistrement", "LabelRecordingPath": "Chemin par défaut pour lenregistrement",
@ -1183,7 +1183,7 @@
"LabelNumberOfGuideDays": "Nombre de jours de guide à télécharger", "LabelNumberOfGuideDays": "Nombre de jours de guide à télécharger",
"LabelOpenclDeviceHelp": "Le périphérique OpenCL qui sera utilisé pour le « tone mapping » HDR. Le chiffre a gauche du point est le numéro de plateforme, et celui de droite est le numéro de périphérique sur la plateforme. La valeur par défaut est 0.0. Le fichier dapplication FFmpeg prenant en charge laccélération OpenCL est requis.", "LabelOpenclDeviceHelp": "Le périphérique OpenCL qui sera utilisé pour le « tone mapping » HDR. Le chiffre a gauche du point est le numéro de plateforme, et celui de droite est le numéro de périphérique sur la plateforme. La valeur par défaut est 0.0. Le fichier dapplication FFmpeg prenant en charge laccélération OpenCL est requis.",
"LabelParentNumber": "Numéro parent", "LabelParentNumber": "Numéro parent",
"LabelParallelImageEncodingLimitHelp": "Nombre maximal dencodages dimages qui peuvent être exécutés en parallèle. Une valeur de 0 entraînera une sélection automatique dune limite selon les caractéristiques de votre système.", "LabelParallelImageEncodingLimitHelp": "Nombre maximal dencodages dimages qui peuvent être exécutés en parallèle. Une valeur de 0 entraînera une sélection automatique dune limite selon le nombre de coeurs de votre système.",
"LabelPasswordResetProvider": "Fournisseur de récupération de mot de passe", "LabelPasswordResetProvider": "Fournisseur de récupération de mot de passe",
"LabelPlayDefaultAudioTrack": "Lire la piste audio par défaut peu importe la langue", "LabelPlayDefaultAudioTrack": "Lire la piste audio par défaut peu importe la langue",
"LabelPostProcessor": "Application de post-traitement", "LabelPostProcessor": "Application de post-traitement",
@ -1248,7 +1248,7 @@
"Large": "Grand", "Large": "Grand",
"LabelWeb": "Réseau", "LabelWeb": "Réseau",
"LabelUnstable": "Instable", "LabelUnstable": "Instable",
"LabelUserAgent": "Agent d'utilisateur", "LabelUserAgent": "Agent utilisateur",
"LabelTranscodingThreadCount": "Nombre de threads de transcodage", "LabelTranscodingThreadCount": "Nombre de threads de transcodage",
"LiveBroadcasts": "Diffusions en direct", "LiveBroadcasts": "Diffusions en direct",
"LabelVaapiDevice": "Appareil VA-API", "LabelVaapiDevice": "Appareil VA-API",
@ -1262,19 +1262,19 @@
"LabelZipCode": "Code postal", "LabelZipCode": "Code postal",
"LabelTriggerType": "Type de déclencheur", "LabelTriggerType": "Type de déclencheur",
"LabelTranscodingThreadCountHelp": "Sélectionnez le nombre maximum de threads à utiliser lors du transcodage. La réduction du nombre de threads réduira l'utilisation du processeur, mais risque de ne pas convertir assez rapidement pour une expérience de lecture fluide.", "LabelTranscodingThreadCountHelp": "Sélectionnez le nombre maximum de threads à utiliser lors du transcodage. La réduction du nombre de threads réduira l'utilisation du processeur, mais risque de ne pas convertir assez rapidement pour une expérience de lecture fluide.",
"LabelTunerIpAddress": "Adresse IP du tuner", "LabelTunerIpAddress": "Adresse IP du syntoniseur",
"LabelUserLoginAttemptsBeforeLockout": "Échec des tentatives de connexion avant que l'utilisateur ne soit verrouillé", "LabelUserLoginAttemptsBeforeLockout": "Échec des tentatives de connexion avant que l'utilisateur ne soit verrouillé",
"LogLevel.Debug": "Déboguer", "LogLevel.Debug": "Déboguer",
"LogLevel.Information": "Information", "LogLevel.Information": "Information",
"LogLevel.Error": "Erreur", "LogLevel.Error": "Erreur",
"LogLevel.Critical": "Critique", "LogLevel.Critical": "Critique",
"LabelXDlnaDoc": "ID de classe d'appareil", "LabelXDlnaDoc": "ID de classe d'appareil",
"LabelTonemappingPeak": "Pic de mappage de tonalité", "LabelTonemappingPeak": "Pic de mappage de tons",
"LabelXDlnaCap": "ID de capacité de l'appareil", "LabelXDlnaCap": "ID de capacité de l'appareil",
"LabelTranscodes": "Transcodages", "LabelTranscodes": "Transcodages",
"LabelBackdropScreensaverIntervalHelp": "Le temps en secondes entre les différents arrière-plans lors de l'utilisation de l'écran de veille des arrière-plans.", "LabelBackdropScreensaverIntervalHelp": "Temps en secondes entre les différents arrière-plans de l'écran de veille.",
"LabelVideoResolution": "Résolution vidéo", "LabelVideoResolution": "Résolution vidéo",
"LabelTunerType": "Type de tuner", "LabelTunerType": "Type de syntoniseur",
"LabelUserLibrary": "Librairie d'utilisateur", "LabelUserLibrary": "Librairie d'utilisateur",
"LabelVaapiDeviceHelp": "Il s'agit du nœud de rendu utilisé pour l'accélération matérielle.", "LabelVaapiDeviceHelp": "Il s'agit du nœud de rendu utilisé pour l'accélération matérielle.",
"Larger": "Plus grand", "Larger": "Plus grand",
@ -1294,11 +1294,11 @@
"SelectAudioNormalizationHelp": "Gain de piste - ajuste le volume de chaque piste pour qu'elles jouent au même niveau d'intensité. Gain d'album - ajuste le volume de toutes les pistes uniquement dans un album, conservant la plage dynamique de l'album.", "SelectAudioNormalizationHelp": "Gain de piste - ajuste le volume de chaque piste pour qu'elles jouent au même niveau d'intensité. Gain d'album - ajuste le volume de toutes les pistes uniquement dans un album, conservant la plage dynamique de l'album.",
"LabelTonemappingRange": "Plage de mappage de tons", "LabelTonemappingRange": "Plage de mappage de tons",
"LabelTonemappingThreshold": "Seuil de mappage de tons", "LabelTonemappingThreshold": "Seuil de mappage de tons",
"LabelTVHomeScreen": "Écran d'accueil du mode TV", "LabelTVHomeScreen": "Écran d'accueil du mode télé",
"LanNetworksHelp": "Liste d'adresses IP ou d'entrées IP/masque de réseau séparées par des virgules pour les réseaux qui seront prises en compte sur le réseau local lors de l'application des restrictions de bande passante. Si elles sont définies, toutes les autres adresses IP seront considérées comme se trouvant sur le réseau externe et seront soumises aux restrictions de bande passante externe. Si ce champ est laissé vide, seul le sous-réseau du serveur est considéré comme faisant partie du réseau local.", "LanNetworksHelp": "Liste d'adresses IP ou d'entrées IP/masque de réseau séparées par des virgules pour les réseaux qui seront prises en compte sur le réseau local lors de l'application des restrictions de bande passante. Si elles sont définies, toutes les autres adresses IP seront considérées comme se trouvant sur le réseau externe et seront soumises aux restrictions de bande passante externe. Si ce champ est laissé vide, seul le sous-réseau du serveur est considéré comme faisant partie du réseau local.",
"LearnHowYouCanContribute": "Découvrez comment vous pouvez contribuer.", "LearnHowYouCanContribute": "Découvrez comment vous pouvez contribuer.",
"Localization": "Localisation", "Localization": "Localisation",
"LogLevel.Trace": "Tracer", "LogLevel.Trace": "Trace",
"LibraryAccessHelp": "Sélectionnez les bibliothèques à partager avec cet utilisateur. Les administrateurs pourront modifier tous les dossiers à l'aide du gestionnaire de métadonnées.", "LibraryAccessHelp": "Sélectionnez les bibliothèques à partager avec cet utilisateur. Les administrateurs pourront modifier tous les dossiers à l'aide du gestionnaire de métadonnées.",
"LabelTrackNumber": "Numéro de piste", "LabelTrackNumber": "Numéro de piste",
"LabelTonemappingThresholdHelp": "Les paramètres de lalgorithme de mappage de tons sont affinés pour chaque scène. Et un seuil permet de détecter si la scène a changé ou non. Si la distance entre la luminosité moyenne de l'image actuelle et la moyenne courante actuelle dépasse une valeur seuil, nous recalculerons la moyenne de la scène et la luminosité maximale. Les valeurs recommandées et par défaut sont 0,8 et 0,2.", "LabelTonemappingThresholdHelp": "Les paramètres de lalgorithme de mappage de tons sont affinés pour chaque scène. Et un seuil permet de détecter si la scène a changé ou non. Si la distance entre la luminosité moyenne de l'image actuelle et la moyenne courante actuelle dépasse une valeur seuil, nous recalculerons la moyenne de la scène et la luminosité maximale. Les valeurs recommandées et par défaut sont 0,8 et 0,2.",
@ -1308,7 +1308,7 @@
"LabelUseReplayGainTagsHelp": "Scanne les fichiers audio pour des étiquettes « ReplayGain » et les utilise au lieu de calculer la valeur LUFS. (Utilise moins de ressources. Passe outre l'option du « scan LUFS »)", "LabelUseReplayGainTagsHelp": "Scanne les fichiers audio pour des étiquettes « ReplayGain » et les utilise au lieu de calculer la valeur LUFS. (Utilise moins de ressources. Passe outre l'option du « scan LUFS »)",
"DeleteName": "Supprimer {0}", "DeleteName": "Supprimer {0}",
"MediaInfoLevel": "Niveau", "MediaInfoLevel": "Niveau",
"HeaderAllRecordings": "Tous les Enregistrements", "HeaderAllRecordings": "Tous les enregistrements",
"ChannelResolutionSD": "SD", "ChannelResolutionSD": "SD",
"ChannelResolutionHD": "HD", "ChannelResolutionHD": "HD",
"ChannelResolutionFullHD": "Full HD", "ChannelResolutionFullHD": "Full HD",
@ -1337,11 +1337,377 @@
"MenuClose": "Fermer le menu", "MenuClose": "Fermer le menu",
"AllowContentWithTagsHelp": "Afficher uniquement les médias avec un ou plusieurs des étiquettes spécifiées.", "AllowContentWithTagsHelp": "Afficher uniquement les médias avec un ou plusieurs des étiquettes spécifiées.",
"BlockContentWithTagsHelp": "Masquer les médias avec un ou plusieurs des étiquettes spécifiées.", "BlockContentWithTagsHelp": "Masquer les médias avec un ou plusieurs des étiquettes spécifiées.",
"ConfirmDeleteSeries": "La suppression de cette série entraînera la suppression des {0} épisodes depuis le disque ET la médiathèque. Êtes-vous sûr de vouloir continuer?", "ConfirmDeleteSeries": "La suppression de cette série entraînera la suppression des {0} épisodes du disque et de la médiathèque. Êtes-vous sûr de vouloir continuer?",
"DeleteEpisode": "Supprimer lépisode", "DeleteEpisode": "Supprimer lépisode",
"EnableLibrary": "Activer la médiathèque", "EnableLibrary": "Activer la médiathèque",
"EnableLibraryHelp": "La désactivation de la médiathèque la masquera depuis toutes les vues utilisateur.", "EnableLibraryHelp": "La désactivation de la médiathèque la masquera depuis toutes les vues utilisateur.",
"AirPlay": "AirPlay", "AirPlay": "AirPlay",
"DeleteEntireSeries": "Supprimer {0} épisodes", "DeleteEntireSeries": "Supprimer {0} épisodes",
"DeleteSeries": "Supprimer la série" "DeleteSeries": "Supprimer la série",
"ConfirmDeleteLyrics": "Supprimer ces paroles les effaceront du disque et de votre médiathèque. Êtes-vous sûr de vouloir continuer?",
"DeleteLyrics": "Supprimer les paroles",
"EnableTrueHdHelp": "N'activez cette option que si votre appareil prend en charge la norme TrueHD ou s'il est connecté à un lecteur audio compatible, sinon la lecture risque d'échouer.",
"ErrorDeletingLyrics": "Une erreur est survenu lors de la suppression des paroles du serveur. Verifiez que Jellyfin peut modifier les fichier dans le dossier média et réessayez à nouveau.",
"HeaderDeleteLyrics": "Supprimer les paroles",
"MixedMoviesShows": "Films et séries mélangés",
"Letterer": "Lettreur",
"Next": "Suivant",
"LibraryScanFanoutConcurrency": "Limite de tâches de scan de médiathèque en parallèle",
"LibraryScanFanoutConcurrencyHelp": "Nombre maximal de tâches en parallèle pour les scans de médiathèque. Une valeur de 0 laissera le système choisir une limite en fonction du nombre de coeurs. ATTENTION: Définir une valeur trop élevée peut causer des problèmes avec les systèmes de fichers réseau. Si vous avez des problèmes, réduisez cette valeur.",
"OptionEveryday": "Tous les jours",
"OptionForceRemoteSourceTranscoding": "Forcer le transcodage pour les sources de média externes comme la télé en direct",
"OptionHasThemeVideo": "Générique",
"OptionHideUser": "Cacher cet utilisateur des écrans de connexion",
"MapChannels": "Associer les canaux",
"NotificationsMovedMessage": "Les notifications ont été déplacé vers l'extension Webhook.",
"EnableSmoothScroll": "Activer le défilement fluide",
"HeaderDeleteSeries": "Supprimer les séries",
"LabelAllowContentWithTags": "Autoriser les éléments comportants des étiquettes",
"MessageNoItemsAvailable": "Aucun élément n'est actuellement disponible.",
"MessageNoTrailersFound": "Installez la chaîne \"Bande-Annonces\" pour agrémenter votre expérience de cinéma par l'ajout de bandes-annonces disponibles sur internet.",
"Author": "Auteur",
"Colorist": "Coloriste",
"CoverArtist": "Artiste de pochette",
"Creator": "Créateur",
"Editor": "Éditeur",
"EnableTrueHd": "Activer le TrueHD",
"HeaderNoLyrics": "Aucune parole trouvée",
"Illustrator": "Illustrateur",
"Inker": "Encreur",
"OptionDateEpisodeAdded": "Date d'ajout de l'épisode",
"MessageChangeRecordingPath": "Modifier votre dossier d'enregistrement ne déplacera pas les enregistrements existants de l'ancien emplacement vers le nouveau. Vous devrez les déplacer manuellement si vous le souhaitez.",
"MessageNoFavoritesAvailable": "Aucun favori n'est actuellement disponible.",
"OptionHasThemeSong": "Chansons thème",
"OptionExtractChapterImage": "Activer l'extraction des images de chapitres",
"OptionHideUserFromLoginHelp": "Utile pour les comptes privés ou d'administrateurs. L'utilisateur devra se connecter manuellement en saisissant son nom d'utilisateur et son mot de passe.",
"OptionImdbRating": "Évaluation IMDB",
"OptionIsSD": "SD",
"EnableDts": "Activer DTS (DCA)",
"EnableDtsHelp": "N'activez cette option que si votre appareil prend en charge le DTS ou s'il est connecté à un lecteur audio compatible, sinon la lecture risque d'échouer.",
"HeaderVideoAdvanced": "Vidéo avancée",
"MessageNoCollectionsAvailable": "Les collections vous permettent de profiter de groupes personnalisés de films, de séries et d'albums. Cliquer sur le bouton '+' pour commencer à créer des collections.",
"MessageRenameMediaFolder": "Renommer une médiathèque entraînera la perte de toutes les métadonnées, procédez avec prudence.",
"MessageRepositoryInstallDisclaimer": "ATTENTION: Installer un dépôt d'extensions tierces comporte des risques. Celui-ci peut contenir du code instable ou malveillant et peut être modifié à tout moment. N'installez que des dépôts provenant d'auteurs en qui vous avez confiance.",
"OptionEnableForAllTuners": "Activer pour tous les syntoniseurs",
"Notifications": "Notifications",
"HeaderLyricDownloads": "Téléchargements de paroles",
"Lyrics": "Paroles",
"MessagePluginInstallDisclaimer": "ATTENTION: Installer une extension tierce comporte des risques. Celle-ci peut contenir du code instable ou malveillant et peut être modifiée à tout moment. N'installez que des extensions provenant d'auteurs en qui vous avez confiance et soyez conscient des effets potentiels que cela peut avoir, incluant des requête vers des services externes, une augmentation de la durée des scans de la médiathèque, ou des tâches de fonds additionnelles.",
"OptionDateShowAdded": "Date d'ajout de la série",
"MessageDirectoryPickerBSDInstruction": "Sur BSD, vous devrez peut-être configurer le stockage de votre 'Jail FreeNAS' pour autoriser Jellyfin à accéder à vos médias.",
"MessageDirectoryPickerLinuxInstruction": "Pour Linux sur Arch Linux, CentOS, Debian, Fedora, openSUSE ou Ubuntu, vous devez autoriser au moins l'accès en lecture à vos répertoires de stockage à l'utilisateur du service.",
"OptionIsHD": "HD",
"Lyric": "Parole",
"LimitSupportedVideoResolution": "Limiter la résolution vidéo maximale supportée",
"LimitSupportedVideoResolutionHelp": "Utiliser la \"Résolution maximale du transcodage vidéo\" comme résolution vidéo maximale supportée.",
"MessageUnauthorizedUser": "Vous n'êtes pas autorisé à accéder au serveur pour le moment. Veuillez contacter votre administrateur de serveur pour plus d'informations.",
"PasswordResetComplete": "Le mot de passe a été réinitialisé.",
"PlaylistError.AddFailed": "Erreur d'ajout à la liste de lecture",
"PlaylistError.CreateFailed": "Erreur lors de la création de la liste de lecture",
"OriginalAirDate": "Date de diffusion originale",
"PathNotFound": "Le chemin d'accès n'a pas pu être trouvé. Vérifiez qu'il est valide et réessayez.",
"People": "Personnes",
"OptionMaxActiveSessions": "Défini le nombre maximal de sessions utilisateur simultanées.",
"PasswordMatchError": "Le mot de passe et sa confirmation doivent correspondre.",
"PasswordRequiredForAdmin": "Un mot de passe est requis pour les comptes administrateur.",
"PersonRole": "en tant que {0}",
"PlaybackErrorNoCompatibleStream": "Ce client n'est pas compatible avec le média et le serveur n'envoie pas de format compatible.",
"PreferEmbeddedEpisodeInfosOverFileNames": "Préférer les informations intégrées aux épisodes plutôt que les noms de fichiers",
"Overview": "Survol",
"OptionMaxActiveSessionsHelp": "Une valeur de 0 désactive la fonctionnalité.",
"OptionMissingEpisode": "Épisodes manquants",
"OptionOnInterval": "À intervalle",
"OptionParentalRating": "Classification parentale",
"OtherArtist": "Autre artiste",
"PackageInstallCompleted": "L'installation de {0} (version {1}) est terminée.",
"PictureInPicture": "Incrustation d'image",
"PlaybackError.ASS_RENDER_ERROR": "Une erreur est survenue avec le moteur de rendu des sous-titres ASS/SSA.",
"PlaybackData": "Données de lecture",
"PlaybackError.FATAL_HLS_ERROR": "Une erreur fatale est survenue dans le stream HLS.",
"PleaseRestartServerName": "Veuillez redémarrer le serveur Jellyfin sur {0}.",
"PleaseSelectTwoItems": "Veuillez sélectionner au moins deux éléments.",
"Poster": "Affiche",
"PosterCard": "Carton d'affiche",
"PleaseEnterNameOrId": "Veuillez entrer un nom ou un identifiant externe.",
"AllowEmbeddedSubtitlesHelp": "Désactiver les sous-titres intégrés aux fichiers. Nécessite un rafraichissement complet de la médiathèque.",
"AllowEmbeddedSubtitlesAllowNoneOption": "Autoriser aucun",
"AllowEmbeddedSubtitlesAllowImageOption": "Autoriser les images",
"PreviousChapter": "Chapitre précédent",
"PreviousTrack": "Revenir au précédent",
"Production": "Production",
"ProductionLocations": "Lieux de production",
"Profile": "Profil",
"Penciller": "Crayonneur",
"PlaceFavoriteChannelsAtBeginning": "Mettre les chaînes favorites au début",
"OptionLoginAttemptsBeforeLockout": "Défini le nombre de connexions échoués avant le vérouillage du compte.",
"OptionLoginAttemptsBeforeLockoutHelp": "Une valeur de 0 applique la politique par défaut de 3 essais pour les utilisateurs et 5 essais pour les administrateurs. Une valeur de -1 désactive le vérouillage.",
"OptionResumable": "Reprise possible",
"OptionPlayCount": "Nombre de lectures",
"OptionPremiereDate": "Date de l'avant-première",
"OptionRandom": "Aléatoire",
"OptionReleaseDate": "Date de sortie",
"OptionRequirePerfectSubtitleMatch": "Télécharger uniquement les sous-titres qui correspondent parfaitement aux fichiers vidéo",
"OptionSaveMetadataAsHiddenHelp": "Changer ceci s'appliquera aux nouvelles métadonnées sauvegardés à partir de maintenant. Les fichiers de métadonnés existants seront mis à jours la prochaine fois qu'ils seront sauvegardés par le serveur.",
"OptionSpecialEpisode": "Spéciaux",
"OptionSaveMetadataAsHidden": "Enregistrer les métadonnées et les images en tant que fichiers cachés",
"OptionTrackName": "Titre de la piste",
"PasswordResetProviderHelp": "Choisissez le fournisseur de réinitialisation de mot de passe à utiliser lorsqu'un utilisateur demande la réinitialisation de son mot de passe.",
"PasswordSaved": "Mot de passe sauvegardé.",
"Played": "Lu",
"PlayFromBeginning": "Lire depuis le début",
"PlaybackError.MEDIA_NOT_SUPPORTED": "La lecture a échoué car le média n'est pas supporté par ce client.",
"PleaseConfirmPluginInstallation": "Veuillez cliquer sur 'OK' pour confirmer que vous avez lu ce qui précède et que vous souhaitez poursuivre l'installation de l'extension.",
"PleaseAddAtLeastOneFolder": "Veuillez ajouter au moins un dossier à cette médiathèque en cliquant sur le bouton '+' de la section 'Dossiers'.",
"OptionMax": "Maximum",
"PackageInstallCancelled": "L'installation de {0} (version {1}) a été annulée.",
"PasswordResetConfirmation": "Êtes-vous sûr de vouloir réinitialiser le mot de passe?",
"OptionTvdbRating": "Évaluation TheTVDB",
"OptionUnairedEpisode": "Épisodes non diffusés",
"OptionWakeFromSleep": "Réveiller",
"PackageInstallFailed": "L'installation de {0} (version {1}) a échouée.",
"PlayCount": "Nombre de lectures",
"PlayNext": "Lire le suivant",
"Play": "Lire",
"PlayAllFromHere": "Tout lire à partir d'ici",
"PlayNextEpisodeAutomatically": "Lire l'épisode suivant automatiquement",
"PlaybackError.MEDIA_DECODE_ERROR": "La lecture a échouée en raison d'une erreur de décodage du média.",
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Les bonus possèdent souvent le même titre intégré que le fichier parent, cocher l'option pour utiliser ce titre quand même.",
"PleaseConfirmRepositoryInstallation": "Veuillez cliquer sur 'OK' pour confirmer que vous avez lu ce qui précède et que vous souhaitez poursuivre l'installation du dépôt d'extensions.",
"PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Utiliser les informations de l'épisode des métadonnées intégrées lorsque disponibles.",
"PreferEmbeddedExtrasTitlesOverFileNames": "Préférer les titres intégrés plutôt que les noms de fichiers pour les bonus",
"Photo": "Photo",
"PerfectMatch": "Correspondance parfaite",
"Person": "Personne",
"PlaybackRate": "Vitesse de lecture",
"PluginFromRepo": "{0} du dépôt {1}",
"QuickConnect": "Connexion rapide",
"QuickConnectActivationSuccessful": "Activé avec succès",
"QuickConnectAuthorizeCode": "Entrer le code {0} pour se connecter",
"PlaylistPublic": "Autoriser l'accès public",
"PlaylistPublicDescription": "Autoriser la lecture de cette liste de lecture par n'importe quel utilisateur connecté.",
"PreferEmbeddedTitlesOverFileNames": "Préférer les titres intégrés aux noms de fichiers",
"AllowEmbeddedSubtitles": "Désactiver différents types de sous-titres intégrés",
"PreferEmbeddedTitlesOverFileNamesHelp": "Déterminer le titre à afficher lorsqu'aucune métadonnée en ligne ou locale n'est disponible.",
"AllowEmbeddedSubtitlesAllowAllOption": "Autoriser tous",
"AllowEmbeddedSubtitlesAllowTextOption": "Autoriser le texte",
"Premieres": "Avant-premières",
"Preview": "Aperçu",
"Previous": "Précédent",
"Print": "Imprimer",
"Producer": "Producteur",
"Programs": "Programmes",
"OptionWeekdays": "Jours de semaine",
"OptionWeekends": "Fin de semaines",
"OptionWeekly": "Chaque semaine",
"Other": "Autre",
"PlaybackError.NETWORK_ERROR": "La lecture a échoué à cause d'un problème réseau.",
"PlaybackError.NotAllowed": "La lecture de ce média n'est pas autorisée.",
"PlaybackError.RateLimitExceeded": "Ce média ne peut pas être lu à cause des limites de débit.",
"PlaybackError.NO_MEDIA_ERROR": "Impossible de trouver une source multimédia valide à lire.",
"PlaybackError.PLAYER_ERROR": "La lecture a échoué en raison d'une erreur fatale du lecteur.",
"PlaybackError.SERVER_ERROR": "La lecture a échoué à cause d'une erreur serveur.",
"QuickConnectDeactivated": "La connexion rapide a été désactivée avant que la requête ne puisse être approuvée",
"RemoveFromPlaylist": "Retirer de la liste de lecture",
"XmlTvPathHelp": "Un chemin d'accès à un fichier XMLTV. Jellyfin lira ce fichier et en vérifiera périodiquement les mises à jour. Vous êtes responsable de la création et de la mise à jour du fichier.",
"UseEpisodeImagesInNextUpHelp": "Les sections \"À suivre\" et \"Reprendre le visionnage\" utiliseront les images des épisodes comme vignettes plutôt que la vignette principale de la série.",
"LabelScanBehaviorHelp": "Le comportement par défaut est non bloquant, ce qui ajoutera les médias à la médiathèque avant que la génération trickplay ne soit terminée. Le blocage garantira que les fichiers trickplay sont générés avant que le média ne soit ajouté à la médiathèque, mais rendra les scans beaucoup plus longs.",
"LabelExtractTrickplayDuringLibraryScan": "Extraire les images trickplay pendant le scan de la médiathèque",
"UseDoubleRateDeinterlacingHelp": "Ce réglage utilise la fréquence de trame lors du désentrelacement, souvent appelé désentrelacement \"bob\", qui double la fréquence d'images de la vidéo pour fournir un mouvement fluide comme en regardant une vidéo entrelacée sur une télévision.",
"UseEpisodeImagesInNextUp": "Utiliser l'image de l'épisode dans \"À suivre\" et \"Reprendre le visionnage\"",
"ValueOneSong": "1 chanson",
"RefreshMetadata": "Actualiser les métadonnées",
"RememberSubtitleSelectionsHelp": "Choisir la piste de sous-titres la plus proche de la dernière vidéo.",
"SaveRecordingImagesHelp": "Sauvegarder les images du guide avec l'enregistrement.",
"PreferFmp4HlsContainerHelp": "Préférer l'utilisation de fMP4 comme conteneur pour HLS rendant possible la lecture directe des stream HEVC et AV1 sur les appareils compatibles.",
"EnableGamepadHelp": "Détecter le signal d'entrée de toute manette connectée. (Nécessite le mode daffichage 'télé'.)",
"NonBlockingScan": "Non bloquant - génération de files d'attente, puis retour",
"LabelScanBehavior": "Comportement du scan",
"QuickConnectInvalidCode": "Code de connexion rapide invalide",
"RefreshDialogHelp": "Les métadonnées sont actualisées en fonction des paramètres et des services Internet activés dans le tableau de bord.",
"RepeatOne": "Répéter un",
"TabStreaming": "Streaming",
"Up": "Haut",
"SavePassword": "Sauvegarder le mot de passe",
"Trailers": "Bandes-annonces",
"TV": "Télé",
"Transcoding": "Transcodage",
"Uniform": "Uniforme",
"TypeOptionPluralMusicAlbum": "Albums de musique",
"UseDoubleRateDeinterlacing": "Doubler la fréquence d'image lors du désentrelacement",
"ValueEpisodeCount": "{0} épisodes",
"ThemeSong": "Chanson thème",
"RecordSeries": "Enregistrer la série",
"RememberMe": "Se souvenir de moi",
"Remixer": "Remixer",
"RemoveFromCollection": "Retirer de la collection",
"TabOther": "Autre",
"TabParentalControl": "Contrôle parental",
"TabPlugins": "Extensions",
"ValueOneMusicVideo": "1 vidéoclip",
"ValueOneSeries": "1 série",
"QuickConnectNotAvailable": "Demandez à votre administrateur système d'activer la connexion rapide",
"TabServer": "Serveur",
"TonemappingAlgorithmHelp": "Le mappage de ton peut être affiné. Si vous n'êtes pas familier avec ces options, gardez les valeurs par défaut. L'option recommandée est 'BT.2390'.",
"TonemappingRangeHelp": "Sélectionnez le gamme de coleur de sortie. \"Auto\" sélectionne la même game qu'en entrée.",
"Translator": "Traducteur",
"TypeOptionPluralBook": "Livres",
"TypeOptionPluralBoxSet": "Coffrets",
"TypeOptionPluralEpisode": "Épisodes",
"TypeOptionPluralMovie": "Films",
"UninstallPluginConfirmation": "Êtes-vous sûr de vouloir désinstaller {0}?",
"ValueAlbumCount": "{0} albums",
"ReleaseDate": "Date de sortie",
"ReleaseGroup": "Groupe de sortie",
"RememberAudioSelections": "Définir la piste audio en fonction de l'élément précédent",
"RememberAudioSelectionsHelp": "Choisir la piste audio la plus proche de la dernière vidéo.",
"RememberSubtitleSelections": "Définir la piste de sous-titre en fonction de l'élément précédent",
"ReplaceAllMetadata": "Remplacer toutes les métadonnées",
"ReplaceExistingImages": "Remplacer les images existantes",
"ResetPassword": "Réinitialiser le mot de passe",
"ResolutionMatchSource": "Résolution de la source",
"Thumb": "Miniature",
"TitleHardwareAcceleration": "Accélération matérielle",
"Upload": "Uploader",
"ValueMinutes": "{0} minutes",
"ValueMovieCount": "{0} films",
"ValueMusicVideoCount": "{0} vidéoclips",
"ValueOneAlbum": "1 album",
"LabelExtractTrickplayDuringLibraryScanHelp": "Générer les images trickplay lorsque les vidéos sont importées pendant le scan de la médiathèque. Autrement, elles seront extraites lors de la tâche programmée \"images trickplay\". Si la génération est définie sur non bloquante, cela naffectera pas le temps nécessaire au scan de la médiathèque.",
"QuickConnectAuthorizeSuccess": "Vous avez authentifié votre appareil avec succès!",
"QuickConnectDescription": "Pour utiliser la connexion rapide, appuyez sur le bouton 'Connexion rapide' de l'appareil à connecter et entrez le code affiché ci-dessous.",
"QuickConnectNotActive": "La connexion rapide n'est pas activée sur ce serveur",
"TypeOptionPluralAudio": "Audios",
"UserAgentHelp": "Fournir un en-tête \"'User-Agent\" personnalisé.",
"UserMenu": "Menu utilisateur",
"ValueDiscNumber": "Disque {0}",
"ValueOneEpisode": "1 épisode",
"TextSent": "Texte envoyé.",
"ResumeAt": "Reprendre depuis {0}",
"QuickConnectAuthorizeFail": "Code de connexion rapide inconnu",
"RepeatAll": "Répéter tous",
"RepeatEpisodes": "Répéter les épisodes",
"RepeatMode": "Mode de répétition",
"Rewind": "Rembobiner",
"Runtime": "Durée",
"Restart": "Redémarrer",
"SaveRecordingNFO": "Sauvegarder les données du guide dans le fichier NFO",
"SaveRecordingImages": "Sauvegarder les images du guide",
"SaveChanges": "Enregistrer les modifications",
"SaveLyricsIntoMediaFolders": "Enregistrer les paroles dans les dossiers média",
"SaveRecordingNFOHelp": "Sauvegarder les métadonnées du guide avec l'enregistrement.",
"SaveLyricsIntoMediaFoldersHelp": "Stocker les paroles avec les fichiers audio permet de les gérer plus facilement.",
"SaveSubtitlesIntoMediaFolders": "Sauvegarder les sous-titres dans les dossiers média",
"TabScheduledTasks": "Tâches planifiées",
"TheseSettingsAffectSubtitlesOnThisDevice": "Ces paramètres affectent les sous-titres sur cet appareil",
"TitleHostingSettings": "Paramètres d'hébergement",
"TitlePlayback": "Lecture",
"Track": "Piste",
"TypeOptionPluralMusicVideo": "Vidéoclips",
"LabelDirectStreamingInfo": "Informations de streaming direct",
"ValueSongCount": "{0} chansons",
"TypeOptionPluralSeason": "Saisons",
"TypeOptionPluralVideo": "Vidéos",
"TypeOptionPluralSeries": "Séries Télé",
"ValueOneMovie": "1 films",
"BlockingScan": "Bloquant - génération de files d'attente, bloquer les scans jusqu'à la fin",
"RecentlyWatched": "Vu récemment",
"RecommendationBecauseYouLike": "Parce que vous aimez {0}",
"RecommendationBecauseYouWatched": "Parce que vous avez regardé {0}",
"RecommendationStarring": "Mettant en vedette {0}",
"Recordings": "Enregistrements",
"SaveSubtitlesIntoMediaFoldersHelp": "Stocker les sous-titres avec les fichiers vidéos permet de les gérer plus facilement.",
"ScanForNewAndUpdatedFiles": "Scanner les nouveaux fichiers et les fichiers modifiés",
"TabMusic": "Musique",
"TabMyPlugins": "Mes extensions",
"TabNetworking": "Réseau",
"TabNfoSettings": "Paramètres NFO",
"TabNetworks": "Réseaux télé",
"TabUpcoming": "À venir",
"Tags": "Étiquettes",
"TagsValue": "Étiquettes: {0}",
"ThemeVideos": "Génériques",
"ThemeSongs": "Chansons thème",
"TypeOptionPluralMusicArtist": "Artistes musicaux",
"ThemeVideo": "Générique",
"ExtractTrickplayImagesHelp": "Les images Trickplay sont similaires aux images de chapitre, sauf qu'elles couvrent toute la longueur du contenu et sont utilisées pour afficher un aperçu lors du parcours des vidéos.",
"SortName": "Trier par nom",
"StopPlayback": "Arrêter la lecture",
"ShowIndicatorsFor": "Afficher les indicateurs pour",
"SimultaneousConnectionLimitHelp": "Le nombre maximal de streams simultanés autorisés. Entrez 0 pour ne pas limiter.",
"SearchResultsEmpty": "Désolé! Aucun résultat trouvé pour \"{0}\"",
"SkipEpisodesAlreadyInMyLibrary": "Ne pas enregistrer les épisodes qui sont déjà dans ma médiathèque",
"StereoDownmixAlgorithmHelp": "Algorithme utilisé pour downmixer les pistes multi-canaux en stéréo.",
"ShowLess": "Afficher moins",
"SortChannelsBy": "Trier les chaînes par",
"Studio": "Studio",
"SecondarySubtitles": "Sous-titres secondaires",
"ShowParentImages": "Afficher les images des séries",
"Shuffle": "Lecture aléatoire",
"SmartSubtitlesHelp": "Les sous-titres correspondant à la préférences de langue seront chargés lorsque l'audio est dans une langue étrangère.",
"ShowMore": "Afficher plus",
"OptionExtractTrickplayImage": "Activer l'extraction d'images trickplay",
"ScreenResolution": "Résolution d'écran",
"SubtitleAppearanceSettingsDisclaimer": "Les options suivantes ne s'appliquent aux sous-titres gaphiques mentionnés plus haut ni aux formats ASS/SSA qui incorporent leurs propres styles.",
"SubtitleGray": "Gris",
"SubtitleBlack": "Noir",
"SubtitleBlue": "Bleu",
"SubtitleCyan": "Cyan",
"SubtitleDownloadersHelp": "Actier et classer vos outils de téléchargements de sous-titres préférés en ordre de priorité.",
"SubtitleGreen": "Vert",
"SubtitleLightGray": "Gris pâle",
"SubtitleMagenta": "Magenta",
"SubtitleOffset": "Décalage des sous-titres",
"SubtitleRed": "Rouge",
"Rate": "Évaluer",
"ViewPlaybackInfo": "Voir les informations de lecture",
"AudioCodecNotSupported": "Le codec audio n'est pas supporté",
"Watched": "Vu",
"WeeklyAt": "{0}s à {1}",
"WriteAccessRequired": "Jellyfin a besoin d'accès en écriture à ce dossier. Vérifiez les permissions et réessayez.",
"Writers": "Scénaristes",
"XmlTvNewsCategoriesHelp": "Les émissions dans ces catégories seront affichés commes des émissions d'actualités. S'il y en a plusieurs, les séparer à l'aide du caractère \"|\".",
"HeaderSelectFallbackFontPath": "Choisir le dossier des polices de secours",
"LabelFallbackFontPathHelp": "Ces polices sont utilisées par certains clients pour afficher les sous-titres. Veuillez vous référer à la documentation pour plus d'informations.",
"HeaderSelectFallbackFontPathHelp": "Parcourir ou saisir le chemin du dossier de polices de secours à utiliser pour le rendu des sous-titres ASS/SSA.",
"AspectRatioCover": "Couvrir",
"Whitelist": "Whitelist",
"EnableFallbackFont": "Activer les polices de secours",
"EnableFallbackFontHelp": "Activer des polices alternatives personnalisées. Ceci peut éviter les problèmes de rendu incorrecte des sous-titres.",
"LabelRemuxingInfo": "Informations de remultiplexage",
"LabelAllowedAudioChannels": "Nombre maximal de canaux audio autorisés",
"Controls": "Contrôles",
"LabelSelectStereo": "Stéréo",
"YoutubeBadRequest": "Mauvaise requête.",
"LabelEnableGamepad": "Activer la manette de jeu",
"ContainerNotSupported": "Le conteneur n'est pas supporté",
"XmlTvSportsCategoriesHelp": "Les émissions dans ces catégories seront affichés commes émissions de sports. S'il y en a plusieurs, les séparer à l'aide du caractère \"|\".",
"Yesterday": "Hier",
"LabelSelectMono": "Mono",
"MessagePlaybackError": "Une erreur s'est produite lors de la lecture de ce fichier sur votre récepteur Google Cast.",
"EnableEnhancedNvdecDecoder": "Activer le décodeur NVDEC amélioré",
"ViewLyrics": "Voir les paroles",
"Writer": "Scénariste",
"LabelFallbackFontPath": "Chemin du dossier de secours des polices",
"PreferFmp4HlsContainer": "Préférer le conteneur fMP4-HLS",
"XmlTvKidsCategoriesHelp": "Les émissions dans ces catégories seront affichés comme des émissions pour enfants. S'il y en a plusieurs, les séparer à l'aide du caractère \"|\".",
"XmlTvMovieCategoriesHelp": "Les émissions dans ces catégories seront affichés commes films. S'il y en a plusieurs, les séparer à l'aide du caractère \"|\".",
"YoutubePlaybackError": "La vidéo demandée ne peut pas être jouée.",
"YoutubeNotFound": "Vidéo introuvable.",
"YoutubeDenied": "La vidéo demandée n'est pas autorisée à être lue dans des lecteurs intégrés.",
"MessageChromecastConnectionError": "Votre récepteur Google Cast ne parvient pas à contacter le serveur Jellyfin. Veuillez vérifier la connexion et réessayer.",
"Yadif": "Yet Another DeInterlacing Filter (YADIF)",
"LabelVideoInfo": "Informations vidéo",
"LabelTrackGain": "Gain de la piste",
"LabelOriginalMediaInfo": "Informations sur le média original",
"LabelSyncPlayInfo": "Informations SyncPlay",
"AllowHevcEncoding": "Autoriser l'encodage en format HEVC",
"LabelSelectAudioChannels": "Canaux",
"EnableVppTonemapping": "Activer le mappage de ton VPP",
"AspectRatioFill": "Remplir",
"Remuxing": "Remultiplexage",
"RemuxHelp1": "Le média est dans un conteneur incompatible (MKV, AVI, WMV, etc.), mais les streams vidéo et audio sont compatibles avec l'appareil. Le média sera rempaqueté à la volée avant d'être envoyé à l'appareil.",
"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"
} }

View file

@ -3,9 +3,9 @@
"AccessRestrictedTryAgainLater": "L'accès est actuellement restreint. Merci de réessayer plus tard.", "AccessRestrictedTryAgainLater": "L'accès est actuellement restreint. Merci de réessayer plus tard.",
"Actor": "Acteur", "Actor": "Acteur",
"Add": "Ajouter", "Add": "Ajouter",
"AddToCollection": "Ajouter à une collection", "AddToCollection": "Ajouter à la collection",
"AddToPlayQueue": "Ajouter à la file d'attente", "AddToPlayQueue": "Ajouter à la file d'attente",
"AddToPlaylist": "Ajouter à une liste de lecture", "AddToPlaylist": "Ajouter à la liste de lecture",
"AddedOnValue": "Ajouté le {0}", "AddedOnValue": "Ajouté le {0}",
"AdditionalNotificationServices": "Visiter le catalogue d'extensions pour installer des services de notifications supplémentaires.", "AdditionalNotificationServices": "Visiter le catalogue d'extensions pour installer des services de notifications supplémentaires.",
"AirDate": "Date de diffusion", "AirDate": "Date de diffusion",
@ -426,7 +426,7 @@
"LabelAllowedRemoteAddresses": "Filtre d'adresse IP distante", "LabelAllowedRemoteAddresses": "Filtre d'adresse IP distante",
"LabelAllowedRemoteAddressesMode": "Type de filtre des adresses IP distantes", "LabelAllowedRemoteAddressesMode": "Type de filtre des adresses IP distantes",
"LabelAppName": "Nom de l'application", "LabelAppName": "Nom de l'application",
"LabelAppNameExample": "Exemple : Sickbeard, Sonarr", "LabelAppNameExample": "Nom humainement lisible pour identifier les clés d'API. Ce paramètre n'affecte pas les fonctionnalités.",
"LabelArtists": "Artistes", "LabelArtists": "Artistes",
"LabelArtistsHelp": "Séparer les artistes par un point-virgule.", "LabelArtistsHelp": "Séparer les artistes par un point-virgule.",
"LabelAudioLanguagePreference": "Langue audio préférée", "LabelAudioLanguagePreference": "Langue audio préférée",
@ -940,7 +940,7 @@
"Overview": "Synopsis", "Overview": "Synopsis",
"PackageInstallCancelled": "L'installation de {0} (version {1}) a été annulée.", "PackageInstallCancelled": "L'installation de {0} (version {1}) a été annulée.",
"PackageInstallCompleted": "L'installation de {0} (version {1}) est terminée.", "PackageInstallCompleted": "L'installation de {0} (version {1}) est terminée.",
"PackageInstallFailed": "L'installation de {0} (version {1}) a échoué.", "PackageInstallFailed": "L'installation de {0} (version {1}) a échouée.",
"ParentalRating": "Classification parentale", "ParentalRating": "Classification parentale",
"PasswordMatchError": "Le mot de passe et sa confirmation doivent correspondre.", "PasswordMatchError": "Le mot de passe et sa confirmation doivent correspondre.",
"PasswordResetComplete": "Le mot de passe a été réinitialisé.", "PasswordResetComplete": "Le mot de passe a été réinitialisé.",
@ -1082,13 +1082,13 @@
"TabServer": "Serveur", "TabServer": "Serveur",
"TabUpcoming": "À venir", "TabUpcoming": "À venir",
"Tags": "Étiquettes", "Tags": "Étiquettes",
"TagsValue": "Mots clés : {0}", "TagsValue": "Étiquettes : {0}",
"TellUsAboutYourself": "Parlez-nous de vous", "TellUsAboutYourself": "Parlez-nous de vous",
"ThemeSongs": "Thèmes musicaux", "ThemeSongs": "Thèmes musicaux",
"ThemeVideos": "Génériques", "ThemeVideos": "Génériques",
"TheseSettingsAffectSubtitlesOnThisDevice": "Ces paramètres affectent les sous-titres de cet appareil", "TheseSettingsAffectSubtitlesOnThisDevice": "Ces paramètres affectent les sous-titres de cet appareil",
"ThisWizardWillGuideYou": "Cet assistant vous guidera dans le processus de configuration. Pour commencer, merci de sélectionner votre langue préférée.", "ThisWizardWillGuideYou": "Cet assistant vous guidera dans le processus de configuration. Pour commencer, merci de sélectionner votre langue préférée.",
"Thumb": "Miniature", "Thumb": "Vignettes",
"Thursday": "Jeudi", "Thursday": "Jeudi",
"TitleHardwareAcceleration": "Accélération matérielle", "TitleHardwareAcceleration": "Accélération matérielle",
"TitleHostingSettings": "Paramètres d'hébergement", "TitleHostingSettings": "Paramètres d'hébergement",
@ -1245,7 +1245,7 @@
"LabelDroppedFrames": "Images perdues", "LabelDroppedFrames": "Images perdues",
"LabelCorruptedFrames": "Images corrompues", "LabelCorruptedFrames": "Images corrompues",
"AskAdminToCreateLibrary": "Demander à un administrateur de créer une médiathèque.", "AskAdminToCreateLibrary": "Demander à un administrateur de créer une médiathèque.",
"AllowFfmpegThrottlingHelp": "Quand le transcodage ou le remultiplexage est suffisamment en avant de la position de lecture, le processus se mettra en pause afin déconomiser des ressources. Ceci est particulièrement utile lors dune lecture continue. À désactiver en cas de problèmes de lecture.", "AllowFfmpegThrottlingHelp": "Quand le transcodage ou le remultiplexage est suffisamment en avance sur la lecture, le processus se mettra en pause afin déconomiser des ressources. Ceci est particulièrement utile lors dune lecture continue. À désactiver en cas de problèmes de lecture.",
"AllowFfmpegThrottling": "Adapter la vitesse du transcodage", "AllowFfmpegThrottling": "Adapter la vitesse du transcodage",
"NoCreatedLibraries": "Il semble que vous n'ayez pas encore créé de bibliothèques. {0}Voulez-vous en créer une maintenant ?{1}", "NoCreatedLibraries": "Il semble que vous n'ayez pas encore créé de bibliothèques. {0}Voulez-vous en créer une maintenant ?{1}",
"PlaybackErrorNoCompatibleStream": "Ce client n'est pas compatible avec le média et le serveur n'envoie pas de format compatible.", "PlaybackErrorNoCompatibleStream": "Ce client n'est pas compatible avec le média et le serveur n'envoie pas de format compatible.",
@ -1272,7 +1272,7 @@
"ListPaging": "{0}-{1} de {2}", "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.", "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.",
"PathNotFound": "Le chemin d'accès n'a pas pu être trouvé. Merci de vérifier qu'il est valide et de réessayer.", "PathNotFound": "Le chemin d'accès n'a pas pu être trouvé. Merci de vérifier qu'il est valide et de réessayer.",
"Yadif": "YADIF", "Yadif": "Yet Another DeInterlacing Filter (YADIF)",
"LabelDeinterlaceMethod": "Méthode de désentrelacement", "LabelDeinterlaceMethod": "Méthode de désentrelacement",
"DeinterlaceMethodHelp": "Sélectionner la méthode de désentrelacement à utiliser lors du transcodage de contenu entrelacé. Lorsque l'accélération matérielle supportant le désentrelacement matériel est activée, le désentrelaceur matériel sera utilisé à la place de ce paramètre.", "DeinterlaceMethodHelp": "Sélectionner la méthode de désentrelacement à utiliser lors du transcodage de contenu entrelacé. Lorsque l'accélération matérielle supportant le désentrelacement matériel est activée, le désentrelaceur matériel sera utilisé à la place de ce paramètre.",
"LabelLibraryPageSize": "Taille des pages de la médiathèque", "LabelLibraryPageSize": "Taille des pages de la médiathèque",
@ -1377,7 +1377,7 @@
"Other": "Autre", "Other": "Autre",
"PosterCard": "Affiche sur carte", "PosterCard": "Affiche sur carte",
"UseDoubleRateDeinterlacing": "Multiplier par deux la fréquence d'images lors du désentrelacement", "UseDoubleRateDeinterlacing": "Multiplier par deux la fréquence d'images lors du désentrelacement",
"Bwdif": "BWDIF", "Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"UseDoubleRateDeinterlacingHelp": "Ce réglage utilise la fréquence de trame lors du désentrelacement, souvent appelé désentrelacement \"bob\", qui double la fréquence d'images de la vidéo pour fournir un mouvement fluide comme en regardant une vidéo entrelacée sur un téléviseur.", "UseDoubleRateDeinterlacingHelp": "Ce réglage utilise la fréquence de trame lors du désentrelacement, souvent appelé désentrelacement \"bob\", qui double la fréquence d'images de la vidéo pour fournir un mouvement fluide comme en regardant une vidéo entrelacée sur un téléviseur.",
"LabelTonemappingDesat": "Désaturation tonale", "LabelTonemappingDesat": "Désaturation tonale",
"TonemappingRangeHelp": "Définir la gamme de couleurs de sortie. Choisir 'Auto' pour utiliser la même gamme de couleurs qu'en entrée.", "TonemappingRangeHelp": "Définir la gamme de couleurs de sortie. Choisir 'Auto' pour utiliser la même gamme de couleurs qu'en entrée.",
@ -1615,7 +1615,7 @@
"SelectAll": "Tout sélectionner", "SelectAll": "Tout sélectionner",
"ButtonExitApp": "Quitter l'application", "ButtonExitApp": "Quitter l'application",
"Clip": "Clip", "Clip": "Clip",
"ThemeVideo": "Thème Vidéo", "ThemeVideo": "Thème vidéo",
"ThemeSong": "Thème musical", "ThemeSong": "Thème musical",
"Sample": "Échantillon", "Sample": "Échantillon",
"Scene": "Scène", "Scene": "Scène",
@ -1676,7 +1676,7 @@
"RememberSubtitleSelections": "Définir la piste de sous-titre en fonction de l'élément précédent", "RememberSubtitleSelections": "Définir la piste de sous-titre en fonction de l'élément précédent",
"RememberAudioSelectionsHelp": "Choisir la piste audio la plus proche de la dernière vidéo.", "RememberAudioSelectionsHelp": "Choisir la piste audio la plus proche de la dernière vidéo.",
"RememberAudioSelections": "Définir la piste audio en fonction de l'élément précédent", "RememberAudioSelections": "Définir la piste audio en fonction de l'élément précédent",
"IgnoreDtsHelp": "Désactiver cette option peut résoudre certains problèmes, par exemple une piste audio manquante sur les chaines TV avec flux audio et vidéo séparés.", "IgnoreDtsHelp": "Désactiver cette option peut résoudre certains problèmes, par exemple une piste audio manquante sur les chaînes avec flux audio et vidéo séparés.",
"OptionDateShowAdded": "Date d'ajout de la série", "OptionDateShowAdded": "Date d'ajout de la série",
"OptionDateEpisodeAdded": "Date d'ajout de l'épisode", "OptionDateEpisodeAdded": "Date d'ajout de l'épisode",
"IgnoreDts": "Ignorer le DTS (horodatage de décodage)", "IgnoreDts": "Ignorer le DTS (horodatage de décodage)",
@ -1719,7 +1719,7 @@
"Short": "Court-métrage", "Short": "Court-métrage",
"HeaderPerformance": "Performance", "HeaderPerformance": "Performance",
"LabelParallelImageEncodingLimit": "Limite de parallélisation de l'encodage d'image", "LabelParallelImageEncodingLimit": "Limite de parallélisation de l'encodage d'image",
"LabelParallelImageEncodingLimitHelp": "Nombre maximal dencodages dimage autorisés à sexécuter en parallèle. Si vous définissez cette valeur sur 0, vous choisirez une limite en fonction des spécifications de votre système.", "LabelParallelImageEncodingLimitHelp": "Nombre maximal dencodages dimage autorisés à sexécuter en parallèle. Si vous définissez cette valeur sur 0, la limite sera choisie en fonction du nombre de cœurs de votre système.",
"LabelEnableAudioVbr": "Activer lencodage audio VBR", "LabelEnableAudioVbr": "Activer lencodage audio VBR",
"LabelEnableAudioVbrHelp": "Le débit binaire variable offre une qualité supérieure à la moyenne mais peut, dans de rares cas, causer des problèmes de mise en mémoire tampon et de compatibilité.", "LabelEnableAudioVbrHelp": "Le débit binaire variable offre une qualité supérieure à la moyenne mais peut, dans de rares cas, causer des problèmes de mise en mémoire tampon et de compatibilité.",
"LabelTonemappingMode": "Mode de mappage tonal", "LabelTonemappingMode": "Mode de mappage tonal",
@ -1807,8 +1807,8 @@
"LabelAllowContentWithTags": "Autoriser les objets comportants des étiquettes", "LabelAllowContentWithTags": "Autoriser les objets comportants des étiquettes",
"ConfirmDeleteSeries": "La suppression de cette série effacera l'ENTIÈRETÉ des {0} épisodes à la fois de votre système de ficher et de votre médiathèque. Êtes-vous sur de vouloir continuer ?", "ConfirmDeleteSeries": "La suppression de cette série effacera l'ENTIÈRETÉ des {0} épisodes à la fois de votre système de ficher et de votre médiathèque. Êtes-vous sur de vouloir continuer ?",
"DeleteEntireSeries": "Supprimer {0} Épisodes", "DeleteEntireSeries": "Supprimer {0} Épisodes",
"DeleteSeries": "Supprimer Séries", "DeleteSeries": "Supprimer la série",
"DeleteEpisode": "Suppri", "DeleteEpisode": "Supprimer l'épisode",
"HeaderDeleteSeries": "Supprimer Séries", "HeaderDeleteSeries": "Supprimer Séries",
"EnableSmoothScroll": "Activer le défilement fluide", "EnableSmoothScroll": "Activer le défilement fluide",
"Lyric": "Parole", "Lyric": "Parole",
@ -1835,7 +1835,7 @@
"LabelProcessPriorityHelp": "Un réglage inférieur ou supérieur déterminera la manière dont le processeur donne la priorité au processus de génération de trickplay ffmpeg par rapport aux autres processus. Si vous remarquez un ralentissement lors de la génération d'images trickplay mais que vous ne souhaitez pas arrêter complètement leur génération, essayez de réduire ce ralentissement en modifiant le nombre de threads.", "LabelProcessPriorityHelp": "Un réglage inférieur ou supérieur déterminera la manière dont le processeur donne la priorité au processus de génération de trickplay ffmpeg par rapport aux autres processus. Si vous remarquez un ralentissement lors de la génération d'images trickplay mais que vous ne souhaitez pas arrêter complètement leur génération, essayez de réduire ce ralentissement en modifiant le nombre de threads.",
"LabelImageIntervalHelp": "Intervalle de temps (ms) entre chaque nouvelle image trickplay.", "LabelImageIntervalHelp": "Intervalle de temps (ms) entre chaque nouvelle image trickplay.",
"LabelWidthResolutions": "Largeur des résolutions", "LabelWidthResolutions": "Largeur des résolutions",
"LabelTrickplayAccel": "Activer l'accélération matérielle", "LabelTrickplayAccel": "Activer le décodage matériel",
"LabelTrickplayAccelHelp": "Assurez-vous d'activer « Autoriser l'encodage MJPEG » dans Transcodage si votre matériel le prend en charge.", "LabelTrickplayAccelHelp": "Assurez-vous d'activer « Autoriser l'encodage MJPEG » dans Transcodage si votre matériel le prend en charge.",
"NonBlockingScan": "Non bloquant - génération de files d'attente, puis retour", "NonBlockingScan": "Non bloquant - génération de files d'attente, puis retour",
"BlockingScan": "Bloquant - génération de files d'attente, analyse des blocs jusqu'à la fin", "BlockingScan": "Bloquant - génération de files d'attente, analyse des blocs jusqu'à la fin",
@ -1858,14 +1858,38 @@
"PlaybackError.MEDIA_NOT_SUPPORTED": "La lecture a échoué car le média n'est pas pris en charge par ce client.", "PlaybackError.MEDIA_NOT_SUPPORTED": "La lecture a échoué car le média n'est pas pris en charge par ce client.",
"PlaybackError.NETWORK_ERROR": "La lecture a échoué à cause d'une erreur réseau.", "PlaybackError.NETWORK_ERROR": "La lecture a échoué à cause d'une erreur réseau.",
"PlaybackError.NO_MEDIA_ERROR": "Impossible de trouver une source multimédia valide à lire.", "PlaybackError.NO_MEDIA_ERROR": "Impossible de trouver une source multimédia valide à lire.",
"PlaybackError.PLAYER_ERROR": "La lecture a échoué en raison d'une erreur fatale du joueur.", "PlaybackError.PLAYER_ERROR": "La lecture a échoué en raison d'une erreur fatale du lecteur.",
"LabelTrickplayAccelEncoding": "Activer l'encodage MJPEG accéléré par le matériel", "LabelTrickplayAccelEncoding": "Activer l'encodage MJPEG accéléré par le matériel",
"LabelTrickplayAccelEncodingHelp": "Actuellement disponible uniquement sur QSV et VAAPI, cette option n'a aucun effet sur les autres méthodes d'accélération matérielle.", "LabelTrickplayAccelEncodingHelp": "Actuellement disponible uniquement sur QSV et VAAPI, cette option n'a aucun effet sur les autres méthodes d'accélération matérielle.",
"ErrorDeletingLyrics": "Une erreur est survenu lors de la suppression des paroles du serveur. S'il vous plaît verifier que Jellyfin peut modifier les fichier dans le dossier multimedia et réessayez.", "ErrorDeletingLyrics": "Une erreur est survenu lors de la suppression des paroles du serveur. S'il vous plaît verifier que Jellyfin peut modifier les fichier dans le dossier multimedia et réessayez.",
"HeaderDeleteLyrics": "Supprimez ces paroles", "HeaderDeleteLyrics": "Suppression des paroles",
"ConfirmDeleteLyrics": "En supprimant ces paroles vous les supprimez a la fois de votre systeme de fichier et de votre bibliothèque. Êtes vous sure de vouloir continuez ?", "ConfirmDeleteLyrics": "En supprimant ces paroles vous les supprimez a la fois de votre systeme de fichier et de votre bibliothèque. Êtes vous sure de vouloir continuez ?",
"DeleteLyrics": "Supprimez ces paroles", "DeleteLyrics": "Supprimer ces paroles",
"HeaderNoLyrics": "Aucune paroles n'ont êtes trouves", "HeaderNoLyrics": "Aucune parole trouvée",
"Lyrics": "Paroles", "Lyrics": "Paroles",
"ViewLyrics": "Voir les paroles" "ViewLyrics": "Voir les paroles",
"SavePassword": "Sauvegarder le mot de passe",
"EnableDts": "Activer DTS (DCA)",
"EnableDtsHelp": "N'activez cette option que si votre appareil prend en charge le DTS ou s'il est connecté à un lecteur audio compatible, sinon la lecture risque d'échouer.",
"EnableTrueHdHelp": "N'activez cette option que si votre appareil prend en charge la norme TrueHD ou s'il est connecté à un lecteur audio compatible, sinon la lecture risque d'échouer.",
"EnableTrueHd": "Activer le TrueHD",
"PlaylistError.CreateFailed": "Erreur lors de la création d'une liste de lecture",
"SaveLyricsIntoMediaFolders": "Enregistrer les paroles dans les dossiers média",
"PlaylistError.AddFailed": "Erreur d'ajout à la liste de lecture",
"SaveLyricsIntoMediaFoldersHelp": "Le stockage des paroles à côté des fichiers audio permet de les gérer plus facilement.",
"HeaderVideoAdvanced": "Vidéo avancée",
"PlaylistPublic": "Autoriser l'accès public",
"PlaylistPublicDescription": "Autoriser la lecture de cette liste de lecture par n'importe quel utilisateur connecté.",
"HeaderLyricDownloads": "Téléchargements des paroles",
"Illustrator": "Illustrateur",
"Author": "Auteur",
"LibraryScanFanoutConcurrencyHelp": "Nombre maximal de tâches en parallèle pour les scans de bibliothèque. Si vous définissez cette valeur à 0, la limite sera choisie en fonction du nombre de cœurs de votre système. ATTENTION: Définir une valeur trop élevée peut causer des problèmes avec les systèmes de fichiers en réseau; si vous rencontrez des problèmes, diminuez cette valeur.",
"Colorist": "Coloriste",
"Creator": "Créateur",
"LibraryScanFanoutConcurrency": "Limite de tâches de scan de bibliothèque en parallèle",
"Translator": "Traducteur",
"Editor": "Éditeur",
"Inker": "Encreur",
"Letterer": "Lettreur",
"Penciller": "Crayonneur"
} }

View file

@ -1281,5 +1281,6 @@
"LabelMaxAudiobookResume": "דקות נותרות בספר המוקלט להמשך", "LabelMaxAudiobookResume": "דקות נותרות בספר המוקלט להמשך",
"LabelMaxAudiobookResumeHelp": "כותרים נחשבים כנוגנו במלואם כאשר משך הזמן הנותר קטן יותר מערך זה.", "LabelMaxAudiobookResumeHelp": "כותרים נחשבים כנוגנו במלואם כאשר משך הזמן הנותר קטן יותר מערך זה.",
"LabelMetadataReaders": "קוראי מטא-דאטה", "LabelMetadataReaders": "קוראי מטא-דאטה",
"LabelMetadataSavers": "שומרי מטא-דאטה" "LabelMetadataSavers": "שומרי מטא-דאטה",
"PlaybackError.RateLimitExceeded": "מדיה"
} }

Some files were not shown because too many files have changed in this diff Show more