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
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
steps:
- uses: eps1lon/actions-label-merge-conflict@e62d7a53ff8be8b97684bffb6cfbbf3fc1115e2e # v3.0.0
- uses: eps1lon/actions-label-merge-conflict@6d74047dcef155976a15e4a124dde2c7fe0c5522 # v3.0.1
with:
dirtyLabel: 'merge conflict'
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:
- name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Setup node environment
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
@ -57,9 +57,11 @@ jobs:
steps:
- name: Save PR context
env:
PR_BRANCH: ${{ github.ref_name }}
PR_NUMBER: ${{ github.event.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }}
run: |
echo $PR_BRANCH > PR_branch
echo $PR_NUMBER > PR_number
echo $PR_SHA > PR_sha
@ -68,5 +70,6 @@ jobs:
with:
name: PR_context
path: |
PR_branch
PR_number
PR_sha

View file

@ -19,16 +19,16 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Initialize CodeQL
uses: github/codeql-action/init@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2
uses: github/codeql-action/init@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5
with:
languages: javascript
queries: +security-extended
- name: Autobuild
uses: github/codeql-action/autobuild@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2
uses: github/codeql-action/autobuild@b7cec7526559c32f1616476ff32d17ba4c59b2d6 # v3.25.5
- 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 }}
reactions: '+1'
- name: Checkout the latest code
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0

View file

@ -17,7 +17,7 @@ jobs:
steps:
- name: Check out Git repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
ref: ${{ github.event.pull_request.head.sha }}
@ -33,6 +33,6 @@ jobs:
- name: Run eslint
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:
github-token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -8,13 +8,40 @@ on:
- completed
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:
permissions:
contents: read
deployments: write
name: Deploy to Cloudflare Pages
if: ${{ always() }}
runs-on: ubuntu-latest
needs:
- pr-context
outputs:
url: ${{ steps.cf.outputs.url }}
@ -33,32 +60,10 @@ jobs:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: jellyfin-web
branch: ${{ github.event.workflow_run.head_branch }}
branch: ${{ needs.pr-context.outputs.branch || github.ref_name }}
directory: dist
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:
name: Compose comment
if: ${{ always() }}
@ -68,7 +73,7 @@ jobs:
- pr-context
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 }}
preview_url: ${{ needs.publish.outputs.url }}
build_workflow_run_id: ${{ github.event.workflow_run.id }}

View file

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

View file

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

View file

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

View file

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

View file

@ -16,7 +16,15 @@ $mui-bp-xl: 1536px;
}
// Fix the padding of dashboard pages
.content-primary.content-primary {
padding-top: 3.25rem !important;
.content-primary {
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;
}
// 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
#myPreferencesMenuPage {
.lnkQuickConnectPreferences,

View file

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

View file

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

View file

@ -49,16 +49,6 @@ export const LEGACY_USER_ROUTES: LegacyRoute[] = [
controller: 'user/subtitles/index',
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',
pageProps: {

View file

@ -7,8 +7,10 @@ import { toAsyncPageRoute } from 'components/router/AsyncRoute';
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
import { toRedirectRoute } from 'components/router/Redirect';
import AppLayout from '../AppLayout';
import { ASYNC_USER_ROUTES } from './asyncRoutes';
import { LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './legacyRoutes';
import VideoPage from './video';
export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [
{
@ -20,7 +22,13 @@ export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [
element: <ConnectionRequired isUserRequired />,
children: [
...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 globalize from 'scripts/globalize';
import theme from 'themes/theme';
import { DisplayPreferences } from './DisplayPreferences';
import { ItemDetailPreferences } from './ItemDetailPreferences';
import { LibraryPreferences } from './LibraryPreferences';
@ -80,11 +79,7 @@ export default function UserDisplayPreferences() {
<Button
type='submit'
sx={{
color: theme.palette.text.primary,
fontSize: theme.typography.htmlFontSize,
fontWeight: theme.typography.fontWeightBold
}}
size='large'
>
{globalize.translate('Save')}
</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 { appHost } from './apphost';
@ -33,8 +34,13 @@ class ServerConnections extends ConnectionManager {
super(...arguments);
this.localApiClient = null;
// Set the apiclient minimum version to match the SDK
this._minServerVersion = MINIMUM_VERSION;
Events.on(this, 'localusersignedout', (_e, logoutInfo) => {
setUserInfo(null, null);
// Ensure the updated credentials are persisted to storage
credentialProvider.credentials(credentialProvider.credentials());
if (window.NativeShell && typeof window.NativeShell.onLocalUserSignedOut === 'function') {
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);
export default new ServerConnections(
credentials,
credentialProvider,
appHost.appName(),
appHost.appVersion(),
appHost.deviceName(),

View file

@ -2,7 +2,7 @@
position: fixed;
left: 0;
right: 0;
z-index: 10;
z-index: 1201 !important; // mui drawer uses z-index 1200
bottom: 0;
transition: transform 180ms linear;
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);
});
}

View file

@ -4,6 +4,7 @@
* @module components/cardBuilder/cardBuilder
*/
import { PersonKind } from '@jellyfin/sdk/lib/generated-client/models/person-kind';
import escapeHtml from 'escape-html';
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) {
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) {
playedIndicator = document.createElement('div');
playedIndicator.classList.add('playedIndicator');
playedIndicator.classList.add('indicator');
playedIndicator.classList.add('playedIndicator', 'indicator');
indicatorsElem = ensureIndicators(card, indicatorsElem);
indicatorsElem.appendChild(playedIndicator);
}
@ -1302,7 +1320,7 @@ function updateUserData(card, userData) {
if (!countIndicator) {
countIndicator = document.createElement('div');
countIndicator.classList.add('countIndicator');
countIndicator.classList.add('countIndicator', 'indicator');
indicatorsElem = ensureIndicators(card, indicatorsElem);
indicatorsElem.appendChild(countIndicator);
}

View file

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

View file

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

View file

@ -78,7 +78,7 @@ export function getPlayedIndicatorHtml(item) {
if (enablePlayedIndicator(item)) {
const userData = item.UserData || {};
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)) {
@ -93,12 +93,16 @@ export function getChildCountIndicatorHtml(item, options) {
const minCount = options?.minCount ? options.minCount : 0;
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 '';
}
function formatCountIndicator(count) {
return count >= 100 ? '99+' : count.toString();
}
export function getTimerIndicator(item) {
let status;

View file

@ -60,6 +60,10 @@ const enablePlayedIndicator = (item: ItemDto) => {
return itemHelper.canMarkPlayed(item);
};
const formatCountIndicator = (count: number) => {
return count >= 100 ? '99+' : count.toString();
};
const useIndicator = (item: ItemDto) => {
const getMediaSourceIndicator = () => {
const mediaSourceCount = item.MediaSourceCount ?? 0;
@ -135,7 +139,7 @@ const useIndicator = (item: ItemDto) => {
if (childCount > 1) {
return (
<Box className='countIndicator indicator childCountIndicator'>
{datetime.toLocaleString(item.ChildCount)}
{formatCountIndicator(childCount)}
</Box>
);
}
@ -149,7 +153,7 @@ const useIndicator = (item: ItemDto) => {
if (userData.UnplayedItemCount) {
return (
<Box className='countIndicator indicator unplayedItemCount'>
{datetime.toLocaleString(userData.UnplayedItemCount)}
{formatCountIndicator(userData.UnplayedItemCount)}
</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({
name: globalize.translate('RemoveFromPlaylist'),
id: 'removefromplaylist',
icon: 'remove'
icon: 'playlist_remove'
});
}
@ -292,7 +292,7 @@ export function getCommands(options) {
commands.push({
name: globalize.translate('RemoveFromCollection'),
id: 'removefromcollection',
icon: 'remove'
icon: 'playlist_remove'
});
}
@ -696,6 +696,6 @@ export function show(options) {
}
export default {
getCommands: getCommands,
show: show
getCommands,
show
};

View file

@ -432,6 +432,12 @@ export function setContentType(parent, contentType) {
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');
return populateMetadataSettings(parent, contentType);
@ -536,6 +542,7 @@ export function getLibraryOptions(parent) {
SkipSubtitlesIfEmbeddedSubtitlesPresent: parent.querySelector('#chkSkipIfGraphicalSubsPresent').checked,
SkipSubtitlesIfAudioTrackMatches: parent.querySelector('#chkSkipIfAudioTrackPresent').checked,
SaveSubtitlesWithMedia: parent.querySelector('#chkSaveSubtitlesLocally').checked,
SaveLyricsWithMedia: parent.querySelector('#chkSaveLyricsLocally').checked,
RequirePerfectSubtitleMatch: parent.querySelector('#chkRequirePerfectMatch').checked,
AutomaticallyAddToCollection: parent.querySelector('#chkAutomaticallyAddToCollection').checked,
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('#chkSkipIfGraphicalSubsPresent').checked = options.SkipSubtitlesIfEmbeddedSubtitlesPresent;
parent.querySelector('#chkSaveSubtitlesLocally').checked = options.SaveSubtitlesWithMedia;
parent.querySelector('#chkSaveLyricsLocally').checked = options.SaveLyricsWithMedia;
parent.querySelector('#chkSkipIfAudioTrackPresent').checked = options.SkipSubtitlesIfAudioTrackMatches;
parent.querySelector('#chkRequirePerfectMatch').checked = options.RequirePerfectSubtitleMatch;
parent.querySelector('#chkAutomaticallyAddToCollection').checked = options.AutomaticallyAddToCollection;

View file

@ -193,3 +193,15 @@
<div class="fieldDescription checkboxFieldDescription">${SaveSubtitlesIntoMediaFoldersHelp}</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 === 'Studio'
|| item.Type === 'MusicGenre'
|| item.Type === 'TvChannel'
|| item.Type === 'Book') {
|| item.Type === 'TvChannel') {
hideElement('#peopleCollapsible', context);
} else {
showElement('#peopleCollapsible', context);

View file

@ -26,6 +26,18 @@
<option value="Engineer">${Engineer}</option>
<option value="Mixer">${Mixer}</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>
</div>

View file

@ -43,8 +43,7 @@ let currentRuntimeTicks = 0;
let isVisibilityAllowed = true;
let lyricPageActive = false;
let isAudio = false;
let isLyricPageActive = false;
function getNowPlayingBarHtml() {
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="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="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() {
if (lyricPageActive) {
if (isLyricPageActive) {
appRouter.back();
} else {
appRouter.show('lyrics');
@ -303,8 +302,8 @@ function getNowPlayingBar() {
nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar');
if (layoutManager.mobile) {
hideButton(nowPlayingBarElement.querySelector('.btnShuffleQueue'));
hideButton(nowPlayingBarElement.querySelector('.nowPlayingBarCenter'));
nowPlayingBarElement.querySelector('.btnShuffleQueue').classList.add('hide');
nowPlayingBarElement.querySelector('.nowPlayingBarCenter').classList.add('hide');
}
if (browser.safari && browser.slow) {
@ -319,14 +318,6 @@ function getNowPlayingBar() {
return nowPlayingBarElement;
}
function showButton(button) {
button.classList.remove('hide');
}
function hideButton(button) {
button.classList.add('hide');
}
function updatePlayPauseState(isPaused) {
if (playPauseButtons) {
playPauseButtons.forEach((button) => {
@ -378,7 +369,7 @@ function updatePlayerStateInternal(event, state, player) {
updateTimeDisplay(playState.PositionTicks, nowPlayingItem.RunTimeTicks, playbackManager.getBufferedRanges(player));
updateNowPlayingInfo(state);
updateLyricButton();
updateLyricButton(nowPlayingItem);
}
function updateRepeatModeDisplay(repeatMode) {
@ -453,11 +444,7 @@ function updatePlayerVolumeState(isMuted, volumeLevel) {
showVolumeSlider = false;
}
if (showMuteButton) {
showButton(muteButton);
} else {
hideButton(muteButton);
}
muteButton.classList.toggle('hide', !showMuteButton);
// See bindEvents for why this is necessary
if (volumeSlider) {
@ -469,20 +456,18 @@ function updatePlayerVolumeState(isMuted, volumeLevel) {
}
}
function updateLyricButton() {
if (!isEnabled) {
return;
}
function updateLyricButton(item) {
if (!isEnabled) return;
isAudio ? showButton(lyricButton) : hideButton(lyricButton);
const hasLyrics = !!item && item.Type === 'Audio' && item.HasLyrics;
lyricButton.classList.toggle('hide', !hasLyrics);
setLyricButtonActiveStatus();
}
function setLyricButtonActiveStatus() {
if (!isEnabled) {
return;
}
lyricButton.classList.toggle('buttonActive', lyricPageActive);
if (!isEnabled) return;
lyricButton.classList.toggle('buttonActive', isLyricPageActive);
}
function seriesImageUrl(item, options) {
@ -628,8 +613,6 @@ function onPlaybackStart(e, state) {
console.debug('nowplaying event: ' + e.type);
const player = this;
isAudio = state.NowPlayingItem.Type === 'Audio';
onStateChanged.call(player, e, state);
}
@ -733,7 +716,7 @@ function onStateChanged(event, state) {
}
getNowPlayingBar();
updateLyricButton();
updateLyricButton(state.NowPlayingItem);
updatePlayerStateInternal(event, state, player);
}
@ -790,7 +773,7 @@ function refreshFromPlayer(player, type) {
}
function bindToPlayer(player) {
lyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
isLyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
if (player === currentPlayer) {
return;
}
@ -823,7 +806,7 @@ Events.on(playbackManager, 'playerchange', function () {
bindToPlayer(playbackManager.getCurrentPlayer());
document.addEventListener('viewbeforeshow', function (e) {
lyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
isLyricPageActive = appRouter.currentRouteInfo.path.toLowerCase() === '/lyrics';
setLyricButtonActiveStatus();
if (!e.detail.options.enableMediaControl) {
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';
interface PlaybackInfo {
item: BaseItemDto;
context?: string;
}
async function mirrorIfEnabled(serverId: string, itemId: string) {
if (playbackManager.enableDisplayMirroring()) {
const playerInfo = playbackManager.getPlayerInfo();
function mirrorItem(info: PlaybackInfo, player?: unknown) {
const { item } = info;
if (playerInfo && !playerInfo.isLocalPlayer && playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
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,
Context: info.context
}, player);
ItemType: item.Type
}, playbackManager.getCurrentPlayer());
} catch (err) {
console.error('[DisplayMirrorManager] failed to mirror item', err);
}
function mirrorIfEnabled(info: PlaybackInfo) {
if (info && playbackManager.enableDisplayMirroring()) {
const playerInfo = playbackManager.getPlayerInfo();
if (playerInfo && !playerInfo.isLocalPlayer && playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
mirrorItem(info, playbackManager.getCurrentPlayer());
}
}
}
document.addEventListener('viewshow', e => {
const state = e.detail.state || {};
const { item } = state;
if (item?.ServerId) {
mirrorIfEnabled({ item });
const { serverId, id } = e.detail?.params || {};
if (serverId && id) {
void mirrorIfEnabled(serverId, id);
}
});

View file

@ -18,6 +18,7 @@ import { PluginType } from '../../types/plugin.ts';
import { includesAny } from '../../utils/container.ts';
import { getItems } from '../../utils/jellyfin-apiclient/getItems.ts';
import { getItemBackdropImageUrl } from '../../utils/jellyfin-apiclient/backdropImage';
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
import { MediaError } from 'types/mediaError';
import { getMediaError } from 'utils/mediaError';
@ -1789,42 +1790,85 @@ class PlaybackManager {
});
}
function translateItemsForPlayback(items, options) {
if (!items.length) return Promise.resolve([]);
async function translateItemsForPlayback(items, options) {
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
items.sort(function (a, b) {
return options.ids.indexOf(a.Id) - options.ids.indexOf(b.Id);
});
}
}
const firstItem = items[0];
let promise;
const serverId = firstItem.ServerId;
const queryOptions = options.queryOptions || {};
if (firstItem.Type === 'Program') {
promise = getItemsForPlayback(serverId, {
function getPlaybackPromise(firstItem, serverId, options, queryOptions, items) {
switch (firstItem.Type) {
case 'Program':
return getItemsForPlayback(serverId, {
Ids: firstItem.ChannelId
});
} else if (firstItem.Type === 'Playlist') {
promise = getItemsForPlayback(serverId, {
case 'Playlist':
return getItemsForPlayback(serverId, {
ParentId: firstItem.Id,
SortBy: options.shuffle ? 'Random' : null
});
} else if (firstItem.Type === 'MusicArtist') {
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
case 'MusicArtist':
return 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({
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);
}
return getNonItemTypePromise(firstItem, serverId, options, queryOptions);
}
function getNonItemTypePromise(firstItem, serverId, options, queryOptions) {
if (firstItem.MediaType === 'Photo') {
return getItemsForPlayback(serverId, mergePlaybackQueries({
ParentId: firstItem.ParentId,
Filters: 'IsNotFolder',
// Setting this to true may cause some incorrect sorting
@ -1847,66 +1891,8 @@ class PlaybackManager {
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') {
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
return getItemsForPlayback(serverId, mergePlaybackQueries({
ParentId: firstItem.Id,
Filters: 'IsNotFolder',
Recursive: true,
@ -1922,7 +1908,8 @@ class PlaybackManager {
} else if (firstItem.Type !== 'BoxSet') {
sortBy = 'SortName';
}
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
return getItemsForPlayback(serverId, mergePlaybackQueries({
ParentId: firstItem.Id,
Filters: 'IsNotFolder',
Recursive: true,
@ -1930,12 +1917,68 @@ class PlaybackManager {
SortBy: sortBy,
MediaTypes: 'Audio,Video'
}, queryOptions));
} else if (firstItem.Type === 'Episode' && items.length === 1 && getPlayer(firstItem, options).supportsProgress !== false) {
promise = new Promise(function (resolve, reject) {
}
return null;
}
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 {
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);
apiClient.getCurrentUser().then(function (user) {
if (!user.Configuration.EnableNextEpisodeAutoPlay || !firstItem.SeriesId) {
if (!firstItem.SeriesId) {
resolve(null);
return;
}
@ -1946,34 +1989,25 @@ class PlaybackManager {
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);
resolve(filterEpisodes(episodesResult, firstItem, options));
}, reject);
});
});
}
if (promise) {
return promise.then(function (result) {
return result ? result.Items : items;
});
} else {
return Promise.resolve(items);
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.getItemsForPlayback = getItemsForPlayback;
@ -2245,35 +2279,42 @@ class PlaybackManager {
playOptions.isFirstItem = true;
}
return runInterceptors(item, playOptions).then(function () {
if (playOptions.fullscreen) {
loading.show();
}
const apiClient = ServerConnections.getApiClient(item.ServerId);
// TODO: This should be the media type requested, not the original media type
const mediaType = item.MediaType;
const onBitrateDetectionFailure = function () {
return playAfterBitrateDetect(getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType), item, playOptions, onPlaybackStartedFn, prevSource);
};
return runInterceptors(item, playOptions)
.then(() => {
if (playOptions.fullscreen) {
loading.show();
}
if (!isServerItem(item) || itemHelper.isLocalItem(item)) {
return onBitrateDetectionFailure();
return Promise.reject('skip bitrate detection');
}
const apiClient = ServerConnections.getApiClient(item.ServerId);
apiClient.getEndpointInfo().then(function (endpointInfo) {
return apiClient.getEndpointInfo().then((endpointInfo) => {
if ((mediaType === 'Video' || mediaType === 'Audio') && appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType)) {
return apiClient.detectBitrate().then(function (bitrate) {
return apiClient.detectBitrate().then((bitrate) => {
appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType, bitrate);
return playAfterBitrateDetect(bitrate, item, playOptions, onPlaybackStartedFn, prevSource);
}, onBitrateDetectionFailure);
} else {
onBitrateDetectionFailure();
return bitrate;
});
}
}, onBitrateDetectionFailure);
}, onInterceptorRejection);
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() {
@ -2287,8 +2328,21 @@ class PlaybackManager {
Events.trigger(self, 'playbackcancelled');
}
function onInterceptorRejection() {
function onInterceptorRejection(e) {
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();
}
@ -2782,7 +2836,7 @@ class PlaybackManager {
} else {
if (item.AlbumId != null) {
return apiClient.getItem(apiClient.getCurrentUserId(), item.AlbumId).then(function(result) {
mediaSource.albumLUFS = result.LUFS;
mediaSource.albumNormalizationGain = result.NormalizationGain;
return mediaSource;
});
}
@ -3313,8 +3367,14 @@ class PlaybackManager {
if (errorOccurred) {
showPlaybackInfoErrorMessage(self, 'PlaybackError' + displayErrorCode);
} else if (nextItem) {
const apiClient = ServerConnections.getApiClient(nextItem.item.ServerId);
apiClient.getCurrentUser().then(function (user) {
if (user.Configuration.EnableNextEpisodeAutoPlay || nextMediaType !== MediaType.Video) {
self.nextTrack();
}
});
}
}
function onPlaybackChanging(activePlayer, newPlayer, newItem) {

View file

@ -173,6 +173,8 @@ function loadForm(context, user, userSettings, systemInfo, apiClient) {
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
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('#selectAudioNormalization').value = userSettings.selectAudioNormalization();
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('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers();
context.querySelector('.chkLimitSupportedVideoResolution').checked = appSettings.limitSupportedVideoResolution();
context.querySelector('#selectPreferredTranscodeVideoAudioCodec').value = appSettings.preferredTranscodeVideoAudioCodec();
setMaxBitrateIntoField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video');
setMaxBitrateIntoField(context.querySelector('.selectVideoInternetQuality'), false, 'Video');
@ -215,6 +218,10 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
appSettings.maxChromecastBitrate(context.querySelector('.selectChromecastVideoQuality').value);
appSettings.maxVideoWidth(context.querySelector('.selectMaxVideoWidth').value);
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('.selectVideoInternetQuality'), false, 'Video');

View file

@ -159,6 +159,41 @@
</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">
<span>${Save}</span>
</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 escapeHtml from 'escape-html';
import toast from 'components/toast/toast';
import dom from 'scripts/dom';
import globalize from 'scripts/globalize';
import { currentSettings as userSettings } from 'scripts/settings/userSettings';
@ -52,12 +53,14 @@ function onSubmit(this: HTMLElement, e: Event) {
addToPlaylist(panel, playlistId)
.catch(err => {
console.error('[PlaylistEditor] Failed to add to playlist %s', playlistId, err);
toast(globalize.translate('PlaylistError.AddFailed'));
})
.finally(loading.hide);
} else {
createPlaylist(panel)
.catch(err => {
console.error('[PlaylistEditor] Failed to create playlist', err);
toast(globalize.translate('PlaylistError.CreateFailed'));
})
.finally(loading.hide);
}
@ -73,13 +76,16 @@ function createPlaylist(dlg: DialogElement) {
const apiClient = ServerConnections.getApiClient(currentServerId);
const api = toApi(apiClient);
const itemIds = dlg.querySelector<HTMLInputElement>('.fldSelectedItemIds')?.value || '';
const itemIds = dlg.querySelector<HTMLInputElement>('.fldSelectedItemIds')?.value || undefined;
return getPlaylistsApi(api)
.createPlaylist({
name: dlg.querySelector<HTMLInputElement>('#txtNewPlaylistName')?.value,
ids: itemIds.split(','),
userId: apiClient.getCurrentUserId()
createPlaylistDto: {
Name: dlg.querySelector<HTMLInputElement>('#txtNewPlaylistName')?.value,
IsPublic: dlg.querySelector<HTMLInputElement>('#chkPlaylistPublic')?.checked,
Ids: itemIds?.split(','),
UserId: apiClient.getCurrentUserId()
}
})
.then(result => {
dlg.submitted = true;
@ -147,6 +153,32 @@ function populatePlaylists(editorOptions: PlaylistEditorOptions, panel: DialogEl
recursive: true
})
.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 = '';
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 += data.Items?.map(i => {
return `<option value="${i.Id}">${escapeHtml(i.Name)}</option>`;
html += playlists.map(({ item, permissions }) => {
if (!permissions?.CanEdit) return '';
return `<option value="${item.Id}">${escapeHtml(item.Name)}</option>`;
});
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 += '</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
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 { appHost } from '../apphost';
@ -9,8 +10,11 @@ import loading from '../loading/loading';
import viewManager from '../viewManager/viewManager';
import ServerConnections from '../ServerConnections';
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();
@ -183,10 +187,18 @@ class AppRouter {
showItem(item, serverId, options) {
// 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();
apiClient.getItem(apiClient.getCurrentUserId(), item).then((itemObject) => {
const api = toApi(apiClient);
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 {
if (arguments.length === 2) {
@ -194,7 +206,7 @@ class AppRouter {
}
const url = this.getRouteUrl(item, options);
this.show(url, { item });
this.show(url);
}
}

View file

@ -2,6 +2,7 @@
* Module shortcuts.
* @module components/shortcuts
*/
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
import { playbackManager } from './playback/playbackmanager';
import inputManager from '../scripts/inputManager';
@ -12,6 +13,7 @@ import recordingHelper from './recordingcreator/recordinghelper';
import ServerConnections from './ServerConnections';
import toast from './toast/toast';
import * as userSettings from '../scripts/settings/userSettings';
import { toApi } from 'utils/jellyfin-apiclient/compat';
function playAllFromHere(card, serverId, queue) {
const parent = card.parentNode;
@ -100,7 +102,7 @@ function notifyRefreshNeeded(childElement, itemsContainer) {
}
}
function showContextMenu(card, options) {
function showContextMenu(card, options = {}) {
getItem(card).then(item => {
const playlistId = card.getAttribute('data-playlistid');
const collectionId = card.getAttribute('data-collectionid');
@ -110,18 +112,48 @@ function showContextMenu(card, options) {
item.PlaylistItemId = elem ? elem.getAttribute('data-playlistitemid') : null;
}
import('./itemContextMenu').then((itemContextMenu) => {
ServerConnections.getApiClient(item.ServerId).getCurrentUser().then(user => {
itemContextMenu.show(Object.assign({
item: item,
const apiClient = ServerConnections.getApiClient(item.ServerId);
const api = toApi(apiClient);
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,
queue: true,
playAllFromHere: !item.IsFolder,
playAllFromHere: item.Type === 'Season' || !item.IsFolder,
queueAllFromHere: !item.IsFolder,
playlistId: playlistId,
collectionId: collectionId,
user: user
}, options || {}))
playlistId,
canEditPlaylist: !!playlistPerms.CanEdit,
collectionId,
user,
...options
});
})
.then(result => {
if (result.command === 'playallfromhere' || result.command === 'queueallfromhere') {
executeAction(card, options.positionTo, result.command);
@ -131,8 +163,6 @@ function showContextMenu(card, options) {
})
.catch(() => { /* no-op */ });
});
});
});
}
function getItemInfoFromCard(card) {
@ -406,8 +436,8 @@ export function getShortcutAttributesHtml(item, serverId) {
}
export default {
on: on,
off: off,
onClick: onClick,
getShortcutAttributesHtml: getShortcutAttributesHtml
on,
off,
onClick,
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 * as userSettings from '../scripts/settings/userSettings';
import Events from '../utils/events.ts';
import ServerConnections from './ServerConnections';
let currentOwnerId;
@ -50,44 +58,61 @@ function stopIfPlaying() {
}
function enabled(mediaType) {
if (mediaType === 'Video') {
if (mediaType === MediaType.Video) {
return userSettings.enableThemeVideos();
}
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
];
async function loadThemeMedia(serverId, itemId) {
const apiClient = ServerConnections.getApiClient(serverId);
const api = toApi(apiClient);
const userId = apiClient.getCurrentUserId();
try {
const item = await queryClient.fetchQuery(getItemQuery(
api,
userId,
itemId));
function loadThemeMedia(item) {
if (item.CollectionType) {
stopIfPlaying();
return;
}
if (excludeTypes.indexOf(item.Type) !== -1) {
if (excludeTypes.includes(item.Type)) {
stopIfPlaying();
return;
}
const apiClient = ServerConnections.getApiClient(item.ServerId);
apiClient.getThemeMedia(apiClient.getCurrentUserId(), item.Id, true).then(function (themeMediaResult) {
const result = userSettings.enableThemeVideos() && themeMediaResult.ThemeVideosResult.Items.length ? themeMediaResult.ThemeVideosResult : themeMediaResult.ThemeSongsResult;
const { data: themeMedia } = await getLibraryApi(api)
.getThemeMedia({ userId, itemId: item.Id, inheritFromParent: true });
const ownerId = result.OwnerId;
const result = userSettings.enableThemeVideos() && themeMedia.ThemeVideosResult?.Items?.length ? themeMedia.ThemeVideosResult : themeMedia.ThemeSongsResult;
if (ownerId !== currentOwnerId) {
playThemeMedia(result.Items, ownerId);
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) {
const state = e.detail.state || {};
const item = state.item;
if (item?.ServerId) {
loadThemeMedia(item);
document.addEventListener('viewshow', e => {
const { serverId, id } = e.detail?.params || {};
if (serverId && id) {
void loadThemeMedia(serverId, id);
return;
}
@ -100,7 +125,7 @@ document.addEventListener('viewshow', function (e) {
}
}, true);
Events.on(playbackManager, 'playbackstart', function (e, player) {
Events.on(playbackManager, 'playbackstart', (_e, player) => {
const item = playbackManager.currentItem(player);
// User played something manually
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 Tooltip from '@mui/material/Tooltip';
import React, { FC, ReactNode } from 'react';
import { useLocation } from 'react-router-dom';
import { appRouter } from 'components/router/appRouter';
import { useApi } from 'hooks/useApi';
@ -17,7 +16,8 @@ interface AppToolbarProps {
buttons?: ReactNode
isDrawerAvailable: boolean
isDrawerOpen: boolean
onDrawerButtonClick: (event: React.MouseEvent<HTMLElement>) => void
onDrawerButtonClick?: (event: React.MouseEvent<HTMLElement>) => void,
isUserMenuAvailable?: boolean
}
const onBackButtonClick = () => {
@ -32,17 +32,14 @@ const AppToolbar: FC<AppToolbarProps> = ({
children,
isDrawerAvailable,
isDrawerOpen,
onDrawerButtonClick
onDrawerButtonClick = () => { /* no-op */ },
isUserMenuAvailable = true
}) => {
const { user } = useApi();
const isUserLoggedIn = Boolean(user);
const currentLocation = useLocation();
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 (
<Toolbar
variant='dense'
@ -84,16 +81,14 @@ const AppToolbar: FC<AppToolbarProps> = ({
{children}
{isUserLoggedIn && isUserMenuAvailable && (
<>
<Box sx={{ display: 'flex', flexGrow: 1, justifyContent: 'flex-end' }}>
{buttons}
</Box>
{isUserLoggedIn && isUserMenuAvailable && (
<Box sx={{ flexGrow: 0 }}>
<UserMenuButton />
</Box>
</>
)}
</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 class="content-primary">
<form class="encodingSettingsForm">

View file

@ -2,7 +2,6 @@ import 'jquery';
import loading from '../../components/loading/loading';
import globalize from '../../scripts/globalize';
import dom from '../../scripts/dom';
import libraryMenu from '../../scripts/libraryMenu';
import Dashboard from '../../utils/dashboard';
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;
function getSystemInfo() {
return systemInfo ? Promise.resolve(systemInfo) : ApiClient.getPublicSystemInfo().then(
@ -292,7 +275,6 @@ $(document).on('pageinit', '#encodingSettingsPage', function () {
$('.encodingSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#encodingSettingsPage', function () {
loading.show();
libraryMenu.setTabs('playback', 0, getTabs);
const page = this;
ApiClient.getNamedConfiguration('encoding').then(function (config) {
ApiClient.getSystemInfo().then(function (fetchedSystemInfo) {

View file

@ -83,7 +83,11 @@
<div class="verticalSection">
<h2>${HeaderPerformance}</h2>
<div class="inputContainer">
<input is="emby-input" id="txtParallelImageEncodingLimit" label="${LabelParallelImageEncodingLimit}" type="number" pattern="[0-9]*" min="0" step="1" />
<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>
</div>

View file

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

View file

@ -2,7 +2,6 @@ import escapeHtml from 'escape-html';
import 'jquery';
import taskButton from '../../scripts/taskbutton';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import dom from '../../scripts/dom';
import imageHelper from '../../utils/image';
@ -358,22 +357,6 @@ function getVirtualFolderHtml(page, virtualFolder, index) {
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 = {
next: function () {
Dashboard.navigate('wizardsettings.html');
@ -383,8 +366,6 @@ pageClassOn('pageshow', 'mediaLibraryPage', function () {
reloadLibrary(this);
});
pageIdOn('pageshow', 'mediaLibraryPage', function () {
libraryMenu.setTabs('librarysetup', 0, getTabs);
const page = this;
taskButton({
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 class="content-primary">
<form>

View file

@ -1,26 +1,8 @@
import globalize from '../../scripts/globalize';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import '../../elements/emby-checkbox/emby-checkbox';
import '../../elements/emby-button/emby-button';
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) {
function loadData() {
ApiClient.getServerConfiguration().then(function(config) {
@ -57,7 +39,6 @@ export default function(view) {
});
view.addEventListener('viewshow', function() {
libraryMenu.setTabs('librarysetup', 1, getTabs);
loadData();
ApiClient.getSystemInfo().then(function(info) {
if (info.OperatingSystem === 'Windows') {

View file

@ -3,7 +3,6 @@ import { ImageResolution } from '@jellyfin/sdk/lib/generated-client/models/image
import 'jquery';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard';
@ -86,26 +85,9 @@ function onSubmit() {
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() {
$('.metadataImagesConfigurationForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#metadataImagesConfigurationPage', function() {
libraryMenu.setTabs('metadata', 2, getTabs);
loading.show();
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>

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>

View file

@ -1,7 +1,6 @@
import escapeHtml from 'escape-html';
import 'jquery';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard';
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';
$(document).on('pageinit', '#metadataNfoPage', function () {
$('.metadataNfoForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#metadataNfoPage', function () {
libraryMenu.setTabs('metadata', 3, getTabs);
loading.show();
const page = this;
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 class="content-primary">
<form class="playbackConfigurationForm">

View file

@ -1,7 +1,5 @@
import 'jquery';
import loading from '../../components/loading/loading';
import libraryMenu from '../../scripts/libraryMenu';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard';
function loadPage(page, config) {
@ -29,27 +27,10 @@ function onSubmit() {
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 () {
$('.playbackConfigurationForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#playbackConfigurationPage', function () {
loading.show();
libraryMenu.setTabs('playback', 1, getTabs);
const page = this;
ApiClient.getServerConfiguration().then(function (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 class="content-primary">
<div class="inputContainer">

View file

@ -1,7 +1,6 @@
import escapeHTML from 'escape-html';
import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import globalize from '../../../../scripts/globalize';
import '../../../../components/cardbuilder/card.scss';
import '../../../../elements/emby-button/emby-button';
@ -159,22 +158,8 @@ function getPluginHtml(plugin, options, installedPlugins) {
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) {
view.addEventListener('viewshow', function () {
libraryMenu.setTabs('plugins', 1, getTabs);
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 class="content-primary">
<div class="inputContainer">

View file

@ -1,5 +1,4 @@
import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import dom from '../../../../scripts/dom';
import globalize from '../../../../scripts/globalize';
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) {
if (dom.parentWithClass(e.target, 'noConfigPluginCard')) {
showNoConfigurationMessage();
@ -257,7 +243,6 @@ function onFilterType(page, searchBar) {
}
pageIdOn('pageshow', 'pluginsPage', function () {
libraryMenu.setTabs('plugins', 0, getTabs);
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 class="content-primary">
<div class="sectionTitleContainer flex align-items-center">

View file

@ -1,5 +1,4 @@
import loading from '../../../../components/loading/loading';
import libraryMenu from '../../../../scripts/libraryMenu';
import globalize from '../../../../scripts/globalize';
import dialogHelper from '../../../../components/dialogHelper/dialogHelper';
import confirm from '../../../../components/confirm/confirm';
@ -103,22 +102,8 @@ function getRepositoryElement(repository) {
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) {
view.addEventListener('viewshow', function () {
libraryMenu.setTabs('plugins', 2, getTabs);
reloadList(this);
const save = this;

View file

@ -20,7 +20,7 @@
</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;">
<div class="ui-bar-a">
<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 class="content-primary">
<form class="streamingSettingsForm">

View file

@ -1,7 +1,5 @@
import 'jquery';
import libraryMenu from '../../scripts/libraryMenu';
import loading from '../../components/loading/loading';
import globalize from '../../scripts/globalize';
import Dashboard from '../../utils/dashboard';
function loadPage(page, config) {
@ -20,27 +18,10 @@ function onSubmit() {
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 () {
$('.streamingSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
}).on('pageshow', '#streamingSettingsPage', function () {
loading.show();
libraryMenu.setTabs('playback', 2, getTabs);
const page = this;
ApiClient.getServerConfiguration().then(function (config) {
loadPage(page, config);

View file

@ -175,14 +175,14 @@
<div id="additionalPartsCollapsible" class="verticalSection detailVerticalSection hide">
<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>
</div>
<div class="verticalSection detailVerticalSection moreFromSeasonSection hide">
<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>
</div>
@ -194,21 +194,21 @@
<div class="verticalSection detailVerticalSection moreFromArtistSection hide">
<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>
</div>
<div id="castCollapsible" class="verticalSection detailVerticalSection hide">
<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>
</div>
<div id="guestCastCollapsible" class="verticalSection detailVerticalSection hide">
<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>
</div>
@ -220,28 +220,28 @@
<div id="specialsCollapsible" class="verticalSection detailVerticalSection hide">
<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>
</div>
<div id="musicVideosCollapsible" class="verticalSection detailVerticalSection hide">
<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>
</div>
<div id="scenesCollapsible" class="verticalSection detailVerticalSection verticalSection-extrabottompadding hide">
<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>
</div>
<div id="similarCollapsible" class="verticalSection detailVerticalSection verticalSection-extrabottompadding hide">
<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>
</div>

View file

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

View file

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

View file

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

View file

@ -26,3 +26,13 @@
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.triedAddingMarkers) {
addMarkers(range);
}
updateMarkers(range, value);
}
});
@ -206,21 +203,6 @@ function setMarker(range, valueMarker, marker, valueProgress) {
}
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) {
let markerTypeSpecificClasses = '';
@ -236,12 +218,25 @@ function addMarkers(range) {
return `<span class="material-icons sliderMarker ${markerTypeSpecificClasses}" aria-hidden="true"></span>`;
}
if (range.getMarkerInfo) {
range.markerInfo = range.getMarkerInfo();
range.markerContainerElement.innerHTML = '';
range.markerInfo.forEach(info => {
range.markerContainerElement.insertAdjacentHTML('beforeend', getMarkerHtml(info));
});
range.markerElements = range.markerContainerElement.querySelectorAll('.sliderMarker');
range.triedAddingMarkers = true;
}
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 () {

View file

@ -28,36 +28,6 @@ import { LibraryViewSettings, ParentId } from 'types/library';
import { LibraryTab } from 'types/libraryTab';
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 (
currentApi: JellyfinApiContext,
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 type { History, Router } from '@remix-run/router';
const normalizePath = (pathname: string) => pathname.replace(/^!/, '');
interface UseLegacyRouterSyncProps {
router: Router;
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
* hash for us.
*/
if (update.location.pathname.startsWith('!')) {
history.replace(normalizePath(update.location.pathname), update.location.state);
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 (update.location.pathname.startsWith('!')) {
history.replace(
{ ...update.location, pathname: update.location.pathname.replace(/^!/, '') },
update.location.state);
} else if (!isSynced) {
await router.navigate(update.location, { replace: true });
}

View file

@ -12,11 +12,6 @@
<meta name="application-name" content="Jellyfin">
<meta name="robots" content="noindex, nofollow, noarchive">
<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">
@ -116,6 +111,7 @@
z-index: 1;
width: 0.8em;
padding-left: env(safe-area-inset-left);
caret-color: transparent;
}
[dir="ltr"] .mainDrawerHandle {

View file

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

View file

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

View file

@ -1250,14 +1250,14 @@ export class HtmlVideoPlayer {
*/
renderSsaAss(videoElement, track, item) {
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 apiClient = ServerConnections.getApiClient(item);
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
if (supportedFonts.includes(i.MimeType)) {
// embedded font url
avaliableFonts.push(apiClient.getUrl(i.DeliveryUrl));
availableFonts.push(apiClient.getUrl(i.DeliveryUrl));
}
});
const fallbackFontList = apiClient.getUrl('/FallbackFont/Fonts', {
@ -1268,7 +1268,7 @@ export class HtmlVideoPlayer {
const options = {
video: videoElement,
subUrl: getTextTrackUrl(track, item),
fonts: avaliableFonts,
fonts: availableFonts,
workerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker.js`,
legacyWorkerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker-legacy.js`,
onError() {
@ -1307,10 +1307,10 @@ export class HtmlVideoPlayer {
if (config.EnableFallbackFont) {
apiClient.getJSON(fallbackFontList).then((fontFiles = []) => {
fontFiles.forEach(font => {
const fontUrl = apiClient.getUrl(`/FallbackFont/Fonts/${font.Name}`, {
const fontUrl = apiClient.getUrl(`/FallbackFont/Fonts/${encodeURIComponent(font.Name)}`, {
api_key: apiClient.accessToken()
});
avaliableFonts.push(fontUrl);
availableFonts.push(fontUrl);
});
this.#currentAssRenderer = new SubtitlesOctopus(options);
});

View file

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

View file

@ -94,6 +94,25 @@ function supportsAc3(videoTestElement) {
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) {
if (browser.tizen || browser.web0s) {
return true;
@ -121,6 +140,15 @@ function supportsAc3InHls(videoTestElement) {
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) {
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 canPlayMp3VideoAudioInHls = supportsMp3InHls(videoTestElement);
const canPlayAc3VideoAudio = supportsAc3(videoTestElement);
const canPlayEac3VideoAudio = supportsEac3(videoTestElement);
const canPlayAc3VideoAudioInHls = supportsAc3InHls(videoTestElement);
@ -474,12 +503,15 @@ export default function (options) {
if (supportsMp3VideoAudio) {
videoAudioCodecs.push('mp3');
}
// PS4 fails to load HLS with mp3 audio
if (!browser.ps4) {
// Safari supports mp3 with HLS, but only in mpegts container, and the supportsMp3VideoAudio will return false.
if (browser.safari || (supportsMp3VideoAudio && !browser.ps4)) {
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');
}
@ -515,14 +547,9 @@ export default function (options) {
hlsInFmp4VideoAudioCodecs.push('mp2');
}
let supportsDts = options.supportsDts;
let supportsDts = appSettings.enableDts() || options.supportsDts;
if (supportsDts == null) {
supportsDts = browser.tizen || browser.web0sVersion || videoTestElement.canPlayType('video/mp4; codecs="dts-"').replace(/no/, '') || videoTestElement.canPlayType('video/mp4; codecs="dts+"').replace(/no/, '');
// 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;
}
supportsDts = canPlayDts(videoTestElement);
}
if (supportsDts) {
@ -535,7 +562,7 @@ export default function (options) {
videoAudioCodecs.push('pcm_s24le');
}
if (options.supportsTrueHd) {
if (appSettings.enableTrueHd() || options.supportsTrueHd) {
videoAudioCodecs.push('truehd');
}

View file

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

View file

@ -30,6 +30,7 @@ import '../elements/emby-button/paper-icon-button-light';
import 'material-design-icons-iconfont';
import '../styles/scrollstyles.scss';
import '../styles/flexstyles.scss';
import { EventType } from 'types/eventType';
function renderHeader() {
let html = '';
@ -703,6 +704,8 @@ const skinHeader = document.querySelector('.skinHeader');
let requiresUserRefresh = true;
function setTabs (type, selectedIndex, builder) {
Events.trigger(document, EventType.SET_TABS, type ? [ type, selectedIndex, builder()] : []);
import('../components/maintabsmanager').then((mainTabsManager) => {
if (type) {
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 () {
const query = {
Fields: 'PrimaryImageAspectRatio',
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 listView.getListViewHtml({
items: items,
items,
showIndex: false,
showRemoveFromPlaylist: true,
playFromHere: true,
action: 'playallfromhere',
smallIcon: true,
dragHandle: true,
playlistId: itemId
dragHandle: isEditable,
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');
elem.classList.add('vertical-list');
elem.classList.remove('vertical-wrap');
elem.enableDragReordering(true);
elem.fetchData = getFetchPlaylistItemsFn(item.Id);
elem.getItemsHtml = getItemsHtmlFn(item.Id);
elem.enableDragReordering(isEditable);
elem.fetchData = getFetchPlaylistItemsFn(apiClient, 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) {
if (!page.playlistInit) {
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 = {

View file

@ -132,6 +132,44 @@ class AppSettings {
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) {
const currentValue = this.get(name, userId);
localStorage.setItem(this.#getKey(name, userId), value);

View file

@ -197,7 +197,7 @@
"HeaderSelectTranscodingPathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم لملفات التشفير البيني. يجب أن يكون هذا المجلد قابل للكتابة فيه.",
"HeaderSendMessage": "أرسل رسالة",
"HeaderServerSettings": "إعدادات الخادم",
"HeaderSetupLibrary": "ضبط مكتبة المحتوى الخاصة بك",
"HeaderSetupLibrary": "ضبط مكاتب المحتوى الخاصة بك",
"HeaderSortBy": "ترتيب حسب",
"HeaderSortOrder": "تسلسل الترتيب",
"HeaderSpecialEpisodeInfo": "معلومات الحلقة الخاصة",
@ -255,7 +255,7 @@
"LabelCollection": "المجموعة",
"LabelCommunityRating": "تقييم الجمهور",
"LabelContentType": "نوع المحتوى",
"LabelCountry": "البلد",
"LabelCountry": "البلد/المنطقة",
"LabelCurrentPassword": "كلمة السر الحالية",
"LabelCustomCertificatePath": "مسار شهادة SSL المخصص",
"LabelCustomCertificatePathHelp": "مسار ملف PKCS # 12 يحتوي على شهادة ومفتاح خاص لتمكين دعم TLS على مجال مخصص.",
@ -527,7 +527,7 @@
"MessagePasswordResetForUsers": "تم إعادة تعيين كلمات المرور للمستخدمين التاليين. يمكنهم الآن تسجيل الدخول باستخدام رموز الـPIN التي تم استخدامها لإعادة الضبط.",
"MessagePleaseEnsureInternetMetadata": "الرجاء التأكد من أن إمكانية إنزال واصفات البيانات من الإنترنت ممكنة.",
"MessagePluginConfigurationRequiresLocalAccess": "لضبط هذا البرنامج المساعد ، يرجى تسجيل الدخول إلى الخادم المحلي الخاص بك مباشرة.",
"MessagePluginInstallDisclaimer": "تحذير: تنصيب الملحقات التي بناها أعضاء مجتمع Jellyfin هي طريقة رائعة لتحسين متعة استخدام Jellyfin عن طريق أضافة مزايا وخدمات الجديدة. ولكن تنصيب ملحقات من مصادر ثالثة تحمل بعظ الخطورة.\nقبل تثبيت الملحقات، نرجو أخذ العلم بالآثار التي قد تلحقها بخادم Jellyfin الخاص بك، مثل أوقات أطولة لتمشيط مكتبتك، والعمليات الخلفية الإضافية وتقليل استقرار نظامك.",
"MessagePluginInstallDisclaimer": "تحذير: تنصيب مكونات إضافية من مصادر طرف ثالث تحمل بعض المخاطر. قد تتضمن بعض البرمجيات الضارة، وقد تتغير في أي وقت. فقط قم بتنصيب المكونات الإضافية من مصدر تثق به، يرجى أخذ العلم بالآثار المحتملة التي قد تلحقها، مثل أوقات أطول لتمشيط مكتبتك، والعمليات الخلفية الإضافية.",
"MessageReenableUser": "أنظر أدناه لإعادة التفعيل",
"MessageTheFollowingLocationWillBeRemovedFromLibrary": "مكان الوسائط التالي سيزال من مكتبة Jellyfin الخاصة بك",
"MessageUnableToConnectToServer": "لم نستطع الاتصال إلى الخادم المختار في الوقت الحالي. الرجاء التأكد من أنه يعمل ثم المحاولة مرة أخرى.",
@ -1085,7 +1085,7 @@
"Record": "سجل",
"RecentlyWatched": "شاهدت مؤخرا",
"Rate": "معدل",
"QuickConnectAuthorizeSuccess": "تمت الموافقة على الطلب",
"QuickConnectAuthorizeSuccess": "تمت المصادقة على الجهاز بنجاح!",
"QuickConnectAuthorizeCode": "أدخل الرمز {0} لتسجيل الدخول",
"QuickConnectActivationSuccessful": "تم التفعيل بنجاح",
"Quality": "الجودة",
@ -1553,7 +1553,7 @@
"LabelColorPrimaries": "الألوان الأساسية",
"HeaderSyncPlayPlaybackSettings": "التشغيل",
"HeaderNewRepository": "مستودع جديد",
"DirectPlayHelp": "الملف المصدر متوافق تمامًا مع هذا العميل ، وتستقبل الجلسة الملف بدون تعديلات.",
"DirectPlayHelp": "الملف المصدر متوافق تمامًا مع هذا العميل والجلسة تستقبل الملف بدون تعديلات.",
"LabelDashboardTheme": "قالب لوحة تحكم الخادم",
"LabelTonemappingParamHelp": "ضبط خوارزمية تعيين النغمة. القيم الموصى بها والافتراضية هي NaN. اتركه فارغًا بشكل عام.",
"LabelTonemappingParam": "معلمة تعيين النغمة",
@ -1564,7 +1564,7 @@
"LabelAutomaticallyAddToCollection": "إضافة إلى المجموعة تلقائيا",
"Console": "وحدة التحكم",
"Casual": "غير رسمي",
"AllowTonemappingHelp": "يمكن أن يؤدي تعيين النغمة إلى تحويل النطاق الديناميكي لمقاطع الفيديو من HDR إلى SDR مع الحفاظ على تفاصيل الصورة والألوان. هذه بينات مهمة جدًا لتمثيل المشهد الأصلي بشكل وفي للمقطع ألأصلي. حاليًا يعمل هذا ألاعداد فقط مع مقاطع فيديو HDR10 أو HLG. يتطلب هذا ألأعداد وقت تشغيل OpenCL أو CUDA.",
"AllowTonemappingHelp": "يمكن أن يؤدي تعيين النغمة إلى تحويل النطاق الديناميكي لمقاطع الفيديو من HDR إلى SDR مع الحفاظ على تفاصيل الصورة والألوان. هذه بينات مهمة جدًا لتمثيل المشهد الأصلي بشكل وفي للمقطع ألأصلي. حاليًا يعمل هذا ألاعداد فقط مع مقاطع فيديو HDR10 أو HLG. يتطلب هذا ألأعداد وقت تشغيل GPGPU.",
"RefFramesNotSupported": "الإطارات المرجعية غير مدعومة",
"InterlacedVideoNotSupported": "الفيديو المتشابك غير مدعوم",
"AnamorphicVideoNotSupported": "لا يتم دعم الفيديو ذي الصورة المشوهة",
@ -1663,9 +1663,9 @@
"MediaInfoVideoRangeType": "نوع نطاق الفيديو",
"LabelVideoRangeType": "نوع نطاق الفيديو",
"VideoRangeTypeNotSupported": "نوع نطاق الفيديو غير مدعوم",
"LabelVppTonemappingContrastHelp": "تطبيق كسب التباين في تعيين نغمة VPP. القيم الموصى بها والافتراضية هي 1.2 و 1.",
"LabelVppTonemappingContrastHelp": "تطبيق كسب التباين في تعيين نغمة VPP. القيم الموصى بها والافتراضية هي 1.",
"LabelVppTonemappingContrast": "كسب تباين تعيين نغمة VPP",
"LabelVppTonemappingBrightnessHelp": "تطبيق كسب السطوع في تعيين نغمة VPP. كل من القيم الموصى بها والافتراضية هي 0.",
"LabelVppTonemappingBrightnessHelp": "تطبيق كسب السطوع في تعيين نغمة VPP. القيم الموصى بها والافتراضية هي 0.",
"LabelVppTonemappingBrightness": "كسب سطوع رسم الخرائط VPP نغمة",
"ScreenResolution": "تعيين مسار الترجمة على أساس البند السابق",
"RememberSubtitleSelectionsHelp": "تعيين مسار الترجمة على أساس البند السابق.",
@ -1715,14 +1715,60 @@
"LogLevel.None": "لا شيئ",
"MenuOpen": "أفتح القائمة",
"AllowSegmentDeletion": "ألغاء القسم",
"AllowSegmentDeletionHelp": "ألغي الأقسام القديمة بعد أن يتم أرسالها للعميل. هذا يساهم بمنع أن يتم تخزين الملف ألذي تم أعادة ترميزه. ستعمل هذه الميزة فقط عندما يتم كبح الترميز. قم بأيقاف هذا الأعداد في حال واجهت مشاكل بتشغيل ألصوت أو ألفديو.",
"AllowSegmentDeletionHelp": "ألغي الأقسام القديمة بعد أن يتم تحميلها من قبل العميل. هذا يساهم بمنع أن يتم تخزين الملف ألذي تم أعادة ترميزه بالكامل. قم بأيقاف هذا الأعداد في حال واجهت مشاكل بتشغيل الصوت أو الفيديو.",
"LabelThrottleDelaySeconds": "أكبح بعد",
"LabelThrottleDelaySecondsHelp": "ألزمن بالثواني الذي سيتم بعده كبح أعادة الترميز. يجب أن يكون الزمن طويلاً بكفاية ليحافظ العميل على مخزون صحي. يجب أن يكون كفح أعادة الترميز مفعلاً ليعمل هذا ألاعاداد.",
"LabelSegmentKeepSeconds": "الممدة للأحتفاظ على الشرائح",
"LabelEnableAudioVbrHelp": "معدل البِت المتغير ينتج على جودة أفضل مقارنة بمعدل البت المتوسط، ولكن في بعض الحالات النادرة قد يسبب مشاكل في التخزين المؤقت والتوافق.",
"LabelSegmentKeepSecondsHelp": "الزمن بالثواني الذي يجب الاحتفاظ به للشرائح قبل أن يتم الكتابة فوقها. يجب أن يكون أكبر من \"بعد الخنق\". يعمل هذا ألأعداد فقط إذا كان حذف الشرائح مفعلًا.",
"LabelSegmentKeepSecondsHelp": "الزمن بالثواني الذي يجب الاحتفاظ به للشرائح بعد أن يتم تحميلها من قبل العميل. يعمل هذا ألأعداد فقط إذا كان حذف الشرائح مفعلًا.",
"AiTranslated": "مترجمة من قبل ذكاء اسطناعي",
"SelectAudioNormalizationHelp": "كسب الالبوم-تعديل الصوت لكل مسار لكي يعملون بنفس مستوى- كسب الالبوم- تعديل مستوى الصوت لكل المسارات في البوم واحد مع ابقاء على النطاق الديناميكي للألبوم.",
"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}",
"DailyAt": "Diariament a {0}",
"ClearQueue": "Esborra la cua",
"Bwdif": "BWDIF",
"Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"ButtonPlayer": "Reproductor",
"ButtonCast": "Transmetre a dispositiu",
"ApiKeysCaption": "Llista de les claus API activades actualment",
@ -864,7 +864,7 @@
"HeaderSelectFallbackFontPath": "Seleccioneu la ruta de la carpeta de fonts",
"Yesterday": "Ahir",
"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.",
"XmlDocumentAttributeListHelp": "Aquests atributs s'apliquen a l'element arrel de cada resposta XML.",
"Writers": "Escriptors",
@ -1335,7 +1335,7 @@
"LabelAudioChannels": "Canals d'àudio",
"LabelAudioBitrate": "Taxa 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ó",
"LabelAllowHWTranscoding": "Permetre la transcodificació de maquinari",
"LabelAllowedRemoteAddressesMode": "La manera de filtre d'adreces IP remota",
@ -1863,5 +1863,25 @@
"DeleteLyrics": "Esborrar lletres",
"HeaderNoLyrics": "No s'ha trobat cap lletra",
"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",
"HeaderSeriesOptions": "Nastavení seriálu",
"HeaderServerSettings": "Nastavení serveru",
"HeaderSetupLibrary": "Nastavení Vašich knihoven médií",
"HeaderSetupLibrary": "Nastavit knihovny médií",
"HeaderSortBy": "Třídit dle",
"HeaderSortOrder": "Pořadí třídění",
"HeaderSpecialEpisodeInfo": "Infromace o speciální epizodě",
@ -362,7 +362,7 @@
"LabelAlbumArtists": "Alba umělce",
"LabelAllowHWTranscoding": "Povolit hardwarové překódování",
"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",
"LabelArtistsHelp": "Více interpretů se odděluje pomocí středníku.",
"LabelAudioLanguagePreference": "Preferovaný jazyk zvuku",
@ -1267,7 +1267,7 @@
"PathNotFound": "Cesta nebyla nalezena. Zkontrolujte, zda je platná a zkuste to znovu.",
"WeeklyAt": "V {0} v {1}",
"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í.",
"LabelLibraryPageSize": "Velikost stránky knihovny",
"LabelDeinterlaceMethod": "Metoda odstranění prokládání",
@ -1371,7 +1371,7 @@
"LabelIconMaxResHelp": "Maximální rozlišení ikon daných vlastností 'upnp:icon'.",
"LabelAlbumArtMaxResHelp": "Maximální rozlišení obrázku v souboru dané vlastností 'upnp:albumArtURI'.",
"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.",
"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é.",
@ -1719,7 +1719,7 @@
"Short": "Krátký film",
"HeaderPerformance": "Výkon",
"LabelParallelImageEncodingLimit": "Počet paralelních kódování obrázků",
"LabelParallelImageEncodingLimitHelp": "Maximální počet kódování obrázků, které mohou běžet zároveň. Nastavením na 0 bude limit nastaven dle parametrů systému.",
"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",
"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",
@ -1867,5 +1867,32 @@
"HeaderDeleteLyrics": "Odstranit texty písní",
"HeaderNoLyrics": "Žádné texty písní nebyly nalezeny",
"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",
"LabelUserLoginAttemptsBeforeLockout": "Mislykkede loginforsøg før bruger lukkes ude",
"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",
"AlbumArtist": "Album Kunstner",
"Album": "Album",
@ -1300,7 +1300,7 @@
"YoutubeNotFound": "Video ikke fundet.",
"ButtonCast": "Cast til enhed",
"ClearQueue": "Ryd kø",
"Bwdif": "BWDIF",
"Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"ButtonUseQuickConnect": "Brug Quick Connect",
"ButtonPlayer": "Afspiller",
"Authorize": "Tillad",
@ -1842,5 +1842,19 @@
"MediaInfoElPresentFlag": "DV el forudindstillelses flag",
"MediaInfoBlPresentFlag": "DV bl forudindstillelses flag",
"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",
"LabelAllowedRemoteAddressesMode": "Filtermodus für externe IP-Adressen",
"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",
"LabelArtistsHelp": "Trenne mehrere Künstler durch ein Semikolon.",
"LabelAudioLanguagePreference": "Bevorzugte Audiosprache",
@ -1366,7 +1366,7 @@
"Poster": "Poster",
"Photo": "Foto",
"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.",
"UseDoubleRateDeinterlacing": "Verdoppelung der Bildfrequenz beim Deinterlacing",
"LabelIconMaxResHelp": "Maximale Auflösung der Icons, die über die Eigenschaft 'upnp:icon' bereitgestellt wird.",
@ -1789,7 +1789,7 @@
"ButtonEditUser": "Editiere Benutzer",
"DlnaMovedMessage": "Die DLNA-Funktion wurde in ein Plugin verschoben.",
"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)",
"LabelUseReplayGainTags": "ReplayGain-Tags Benutzen",
"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.",
"HeaderDeleteLyrics": "Songtexte löschen",
"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

@ -1249,7 +1249,7 @@
"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.",
"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",
"OnApplicationStartup": "On application startup",
"EveryXHours": "Every {0} hours",
@ -1860,5 +1860,13 @@
"PlaybackError.SERVER_ERROR": "Playback failed due to a server error.",
"PlaybackError.NotAllowed": "Playback of this media is not allowed.",
"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.",
"AllowSubtitleManagement": "Allow this user to edit subtitles",
"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",
"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",
@ -57,6 +57,7 @@
"AsManyAsPossible": "As many as possible",
"AspectRatio": "Aspect Ratio",
"Audio": "Audio",
"Author": "Author",
"Authorize": "Authorize",
"AuthProviderHelp": "Select an authentication provider to be used to authenticate this user's password.",
"Auto": "Auto",
@ -131,7 +132,7 @@
"ButtonUninstall": "Uninstall",
"ButtonUseQuickConnect": "Use Quick Connect",
"ButtonWebsite": "Website",
"Bwdif": "BWDIF",
"Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"CancelRecording": "Cancel recording",
"CancelSeries": "Cancel series",
"Casual": "Casual",
@ -151,6 +152,7 @@
"ClearQueue": "Clear queue",
"ClientSettings": "Client Settings",
"Collections": "Collections",
"Colorist": "Colorist",
"ColorPrimaries": "Color primaries",
"ColorSpace": "Color space",
"ColorTransfer": "Color transfer",
@ -174,6 +176,8 @@
"CopyFailed": "Could not copy",
"CopyStreamURL": "Copy Stream URL",
"CopyStreamURLSuccess": "URL copied successfully.",
"CoverArtist": "Cover artist",
"Creator": "Creator",
"CriticRating": "Critics rating",
"Cursive": "Cursive",
"DailyAt": "Daily at {0}",
@ -232,6 +236,7 @@
"DrmChannelsNotImported": "Channels with DRM will not be imported.",
"DropShadow": "Drop Shadow",
"Edit": "Edit",
"Editor": "Editor",
"EditImages": "Edit images",
"EditMetadata": "Edit metadata",
"EditSubtitles": "Edit subtitles",
@ -246,6 +251,8 @@
"EnableDetailsBanner": "Details Banner",
"EnableDetailsBannerHelp": "Display a banner image at the top of the item details page.",
"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",
"EnableExternalVideoPlayersHelp": "An external player menu will be shown when starting video playback.",
"EnableFasterAnimations": "Faster animations",
@ -267,6 +274,8 @@
"EnableThemeSongsHelp": "Play the theme songs in background while browsing the library.",
"EnableThemeVideosHelp": "Play theme videos in the background while browsing the library.",
"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.",
"Ended": "Ended",
"EndsAtValue": "Ends at {0}",
@ -417,6 +426,7 @@
"HeaderLibrarySettings": "Library Settings",
"HeaderLiveTvTunerSetup": "Live TV Tuner Setup",
"HeaderLoginFailure": "Login Failure",
"HeaderLyricDownloads": "Lyric Downloads",
"HeaderMedia": "Media",
"HeaderMediaFolders": "Media Folders",
"HeaderMetadataSettings": "Metadata Settings",
@ -501,6 +511,7 @@
"HeaderUploadSubtitle": "Upload Subtitle",
"HeaderUser": "User",
"HeaderUsers": "Users",
"HeaderVideoAdvanced": "Video Advanced",
"HeaderVideoQuality": "Video Quality",
"HeaderVideos": "Videos",
"HeaderVideoType": "Video Type",
@ -516,9 +527,11 @@
"Identify": "Identify",
"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.",
"Illustrator": "Illustrator",
"Image": "Image",
"Images": "Images",
"ImportFavoriteChannelsHelp": "Only channels that are marked as favorite on the tuner device will be imported.",
"Inker": "Inker",
"InstallingPackage": "Installing {0} (version {1})",
"InstantMix": "Instant mix",
"ItemCount": "{0} items",
@ -544,7 +557,7 @@
"LabelAllowedRemoteAddressesMode": "Remote IP address filter mode",
"LabelAllowHWTranscoding": "Allow hardware transcoding",
"LabelAppName": "App name",
"LabelAppNameExample": "Example: Sickbeard, Sonarr",
"LabelAppNameExample": "A human readable name for identifying API keys. This setting will not affect functionality.",
"LabelArtists": "Artists",
"LabelArtistsHelp": "Separate multiple artists with a semicolon.",
"LabelAudioBitDepth": "Audio bit depth",
@ -553,6 +566,7 @@
"LabelAudioCodec": "Audio codec",
"LabelAudioLanguagePreference": "Preferred audio language",
"LabelSelectAudioNormalization": "Audio Normalization",
"LabelSelectPreferredTranscodeVideoAudioCodec": "Preferred transcode audio codec in video playback",
"LabelAudioSampleRate": "Audio sample rate",
"LabelAuthProvider": "Authentication Provider",
"LabelAutomaticallyAddToCollection": "Automatically add to collection",
@ -759,7 +773,7 @@
"LabelOriginalTitle": "Original title",
"LabelOverview": "Overview",
"LabelParallelImageEncodingLimit": "Parallel image encoding limit",
"LabelParallelImageEncodingLimitHelp": "Maximum amount of image encodings that are allowed to run in parallel. Setting this to 0 will choose a limit based on your system specs.",
"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",
"LabelParentNumber": "Parent number",
"LabelPassword": "Password",
@ -944,7 +958,10 @@
"LatestFromLibrary": "Recently Added in {0}",
"LearnHowYouCanContribute": "Learn how you can contribute.",
"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.",
"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",
"LimitSupportedVideoResolutionHelp": "Use 'Maximum Allowed Video Transcoding Resolution' as maximum supported video resolution.",
"List": "List",
@ -1230,6 +1247,7 @@
"PasswordResetProviderHelp": "Pick a password reset provider to be used when this user requests a password reset.",
"PasswordSaved": "Password saved.",
"PathNotFound": "The path could not be found. Please ensure the path is valid and try again.",
"Penciller": "Penciler",
"People": "People",
"PerfectMatch": "Perfect match",
"Person": "Person",
@ -1257,6 +1275,10 @@
"PlayCount": "Play count",
"Played": "Played",
"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",
"PlayNext": "Play next",
"PlayNextEpisodeAutomatically": "Play next episode automatically",
@ -1347,6 +1369,9 @@
"Saturday": "Saturday",
"Save": "Save",
"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",
"SaveRecordingNFOHelp": "Save metadata from EPG listings provider along side media.",
"SaveRecordingImages": "Save recording EPG images",
@ -1366,6 +1391,7 @@
"Season": "Season",
"SecondarySubtitles": "Secondary Subtitles",
"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",
"SendMessage": "Send message",
"Series": "Series",
@ -1472,6 +1498,7 @@
"TrackCount": "{0} tracks",
"Trailers": "Trailers",
"Transcoding": "Transcoding",
"Translator": "Translator",
"Tuesday": "Tuesday",
"TV": "TV",
"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 '|'.",
"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 '|'.",
"Yadif": "YADIF",
"Yadif": "Yet Another DeInterlacing Filter (YADIF)",
"Yes": "Yes",
"Yesterday": "Yesterday",
"HeaderSelectFallbackFontPath": "Select Fallback Font Folder Path",

View file

@ -171,7 +171,7 @@
"GuestStar": "Estrella invitada",
"Guide": "Guía",
"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.",
"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.",
@ -311,7 +311,7 @@
"HeaderSendMessage": "Enviar mensaje",
"HeaderSeriesOptions": "Opciones de series",
"HeaderServerSettings": "Ajustes del servidor",
"HeaderSetupLibrary": "Configure sus bibliotecas de medios",
"HeaderSetupLibrary": "Configure su biblioteca multimedia",
"HeaderSortBy": "Ordenar por",
"HeaderSortOrder": "Orden",
"HeaderSpecialEpisodeInfo": "Información del episodio especial",
@ -374,7 +374,7 @@
"LabelAllowedRemoteAddresses": "Filtro de dirección IP remota",
"LabelAllowedRemoteAddressesMode": "Modo de filtro de dirección IP remota",
"LabelAppName": "Nombre de la aplicación",
"LabelAppNameExample": "Ejemplo: Sickbeard, Sonarr",
"LabelAppNameExample": "Un nombre leible por humanos para identificar llaves de API. Esta opcion no afectara funcionalidad. Ejemplo: Sickbeard, Sonarr",
"LabelArtists": "Artistas",
"LabelArtistsHelp": "Separar múltiples artistas utilizando punto y coma.",
"LabelAudioLanguagePreference": "Idioma de audio preferido",
@ -1275,7 +1275,7 @@
"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.",
"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",
"Filter": "Filtro",
"New": "Nuevo",
@ -1363,7 +1363,7 @@
"Restart": "Reiniciar",
"ResetPassword": "Reiniciar Contraseña",
"Profile": "Perfil",
"Bwdif": "BWDIF",
"Bwdif": "Filtro de Desentrelazado de Bob Weaver",
"UseDoubleRateDeinterlacing": "Duplicar el número de cuadros por segundo al desentrelazar",
"Photo": "Fotografía",
"MusicVideos": "Vídeos musicales",
@ -1708,7 +1708,7 @@
"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.",
"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",
"SubtitleMagenta": "Magenta",
"SubtitleWhite": "Blanco",
@ -1801,7 +1801,7 @@
"LabelServerVersion": "Versión del servidor",
"AllowMjpegEncoding": "Permitir codificación en formato MJPEG (utilizado durante la generación de trickplay)",
"Trickplay": "Trickplay",
"LabelTrickplayAccel": "Habilitar aceleración por hardware",
"LabelTrickplayAccel": "Habilitar descodificación por hardware",
"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?",
"DeleteEntireSeries": "Eliminar {0} Episodios",
@ -1827,8 +1827,69 @@
"PlaybackError.SERVER_ERROR": "La reproducción falló por un error del servidor.",
"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.",
"EnableLibrary": "Activar la librería",
"EnableLibraryHelp": "Desactivando 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?",
"DeleteLyrics": "Borrar letras"
"EnableLibrary": "Activar la biblioteca",
"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 biblioteca de medios. ¿Estás seguro de que deseas continuar?",
"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.",
"ConfigureDateAdded": "Seadista, kuidas lisamise kuupäev kuvatakse juhtpaneeli meediakogu seadetes",
"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",
"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.",
@ -1656,7 +1656,7 @@
"Select": "Vali",
"EnableIntelLowPowerH264HwEncoder": "Luba Intel Low-Power H.264 riistvara kodeerija",
"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.",
"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.",
@ -1666,8 +1666,12 @@
"PreferSystemNativeHwDecoder": "Eelistage OS-i DXVA või VA-API riistvaradekoodereid",
"AllowCollectionManagement": "Luba sellel kasutajal kogusid hallata",
"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",
"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": "معمول سازی صوت",
"AllowCollectionManagement": "به این کاربر اجازه مدیریت مجموعه را بده",
"AllowSegmentDeletion": "تکه ها را پاک کن",
"AllowSegmentDeletionHelp": "پاک کردن تکه های قدیمی را بعد از فرستادن به کلاینت. این کار از ذخیره کل فایل transcode شده بر روی هارد جلوگیری می‌ کند. این تنها زمانی کار می کند که throttling فعال باشد. درصورت مشکل در هنگام پخش این ویژگی را غیرفعال کنید.",
"AllowSegmentDeletionHelp": "پاک کردن تکه های قدیمی بعد از دریافت کلاینت. این کار از ذخیره کل فایل transcode شده بر روی هارد جلوگیری می‌ کند. این تنها زمانی کار می کند که throttling فعال باشد. درصورت مشکل در هنگام پخش این ویژگی را غیرفعال کنید.",
"LogLevel.Error": "خطا",
"LogLevel.Critical": "بحرانی",
"LogLevel.None": "هیچکدام",
@ -1625,5 +1625,8 @@
"LogLevel.Information": "اطلاعات",
"LogoScreensaver": "محافظ صفحه لوگو",
"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.",
"NonBlockingScan": "Ei-blokkaava - asettaa generoinnin jonoon ja palaa",
"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",
"ButtonQuickStartGuide": "Guide de démarrage rapide",
"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",
"Disconnect": "Se déconnecter",
"Download": "Télécharger",
@ -70,7 +70,7 @@
"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>.",
"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",
"AddToPlayQueue": "Ajouter à la file d'attente",
"AddedOnValue": "Ajouté le {0}",
@ -88,7 +88,7 @@
"AllowMediaConversion": "Autoriser la 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",
"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",
"AllowRemoteAccessHelp": "Si l'option est désactivée, toutes les connexions distantes seront bloquées.",
"Artists": "Artistes",
@ -128,7 +128,7 @@
"BoxRear": "Dos de boîtier",
"Browse": "Parcourir",
"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.",
"ButtonAddMediaLibrary": "Ajouter une médiathèque",
"ButtonAddScheduledTaskTrigger": "Ajouter un déclencheur",
@ -150,15 +150,15 @@
"AspectRatio": "Format de l'image",
"AskAdminToCreateLibrary": "Demander un administrateur de créer une médiathèque.",
"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.",
"AllowFfmpegThrottling": "Limiter la vitesse de transcodage",
"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 le transcodage",
"AlbumArtist": "Artiste de l'album",
"Album": "Album",
"AuthProviderHelp": "Sélectionner un fournisseur d'authentification pour authentifier le mot de passe de cet utilisateur.",
"ButtonSyncPlay": "SyncPlay",
"Default": "Par défaut",
"DeathDateValue": "Mort: {0}",
"DatePlayed": "Date écoutée",
"DatePlayed": "Date d'écoute",
"DateAdded": "Date d'ajout",
"CriticRating": "Évaluation des critiques",
"CopyStreamURLSuccess": "URL copié avec succès.",
@ -167,7 +167,7 @@
"Connect": "Connexion",
"ConfirmEndPlayerSession": "Voulez-vous éteindre Jellyfin sur {0}?",
"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?",
"ClientSettings": "Paramètres du client",
"ChannelNumber": "Numéro de canal",
@ -186,7 +186,7 @@
"ButtonShutdown": "Éteindre",
"ButtonSend": "Envoyer",
"ButtonSelectDirectory": "Sélectionner le répertoire",
"ButtonScanAllLibraries": "Analyser toutes les médiathèques",
"ButtonScanAllLibraries": "Scanner toutes les médiathèques",
"ButtonRevoke": "Révoquer",
"ButtonResume": "Reprendre la lecture",
"ButtonResetEasyPassword": "Remettre à nouveau le code NIP Facile",
@ -215,7 +215,7 @@
"LabelVideo": "Vidéo",
"DashboardArchitecture": "Architecture : {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)",
"CommunityRating": "Évaluation de la communauté",
"ColorTransfer": "Transfert de couleur",
@ -234,9 +234,9 @@
"Episodes": "Épisodes",
"Episode": "Épisode",
"Ended": "Terminé",
"EnableThemeSongsHelp": "Jouer les bandes sonores 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.",
"EnableStreamLooping": "Lecture en boucle des diffusions en direct",
"EnableThemeSongsHelp": "Jouer les chansons thème en arrière-plan pendant la navigation de la médiathèque.",
"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 streams en direct",
"EnableQuickConnect": "Activer la connexion rapide sur ce serveur",
"EnablePhotosHelp": "Les images seront détectées et affichées avec les autres fichiers multimédia.",
"EnableExternalVideoPlayers": "Lecteurs vidéo externes",
@ -257,9 +257,9 @@
"HeaderApiKeys": "Clés 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.",
"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}",
"Bwdif": "BWDIF",
"Bwdif": "Bob Weaver DeInterlacing Filter (BWDIF)",
"ButtonPlayer": "Joueur",
"ButtonCast": "Diffuser sur l'appareil",
"ApiKeysCaption": "Liste des clés API actuellement activées",
@ -284,7 +284,7 @@
"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 ».",
"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",
"Guide": "Guide",
"GroupVersions": "Regrouper les versions",
@ -312,15 +312,15 @@
"EveryNDays": "Tous les {0} jours",
"EveryHour": "A chaque heure",
"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.",
"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.",
"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.",
"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é.",
"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.",
"EnableNextVideoInfoOverlay": "Voir les informations de la vidéo suivante pendant la lecture",
"EnableHardwareEncoding": "Activer l'encodage matériel",
@ -362,9 +362,9 @@
"DisplayInMyMedia": "Afficher sur lécran daccueil",
"Display": "Affichage",
"Disc": "Disque",
"DirectStreaming": "Diffusion en continu directe",
"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.",
"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é.",
"DirectStreaming": "Streaming direct",
"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 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",
"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",
@ -388,7 +388,7 @@
"LabelDay": "Jour de la semaine",
"LabelDateAdded": "Date d'ajout",
"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é",
"LabelCurrentStatus": "État actuel",
"LabelCurrentPassword": "Mot de passe actuel",
@ -399,13 +399,13 @@
"LabelCollection": "Collection",
"LabelChromecastVersion": "Version de Google Cast",
"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",
"LabelCancelled": "Annulé",
"LabelBirthYear": "Année de naissance",
"LabelBirthDate": "Date de naissance",
"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",
"LabelAlbum": "Album",
"LabelAirTime": "Heure de diffusion",
@ -501,7 +501,7 @@
"HeaderIdentificationHeader": "En-tête d'identification",
"HeaderIdentificationCriteriaHelp": "Saisissez au moins un critère d'identification.",
"HeaderIdentification": "Identification",
"HeaderGuideProviders": "Fournisseurs de données de guides TV",
"HeaderGuideProviders": "Fournisseurs de données des guides télé",
"HeaderForKids": "Pour les enfants",
"HeaderFetchImages": "Récupérer les images",
"HeaderFetcherSettings": "Paramètres du récupérateur",
@ -542,17 +542,17 @@
"DoNotRecord": "Ne pas enregistrer",
"HeaderDebugging": "Débogage et traçage",
"HeaderContainerProfile": "Profile conteneur",
"EnableTonemapping": "Activer la correspondance de tonalité",
"EnableTonemapping": "Activer le mappage tonal",
"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.",
"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.",
"HeaderXmlSettings": "Options XML",
"HeaderXmlDocumentAttributes": "Attributs de document XML",
"HeaderXmlDocumentAttribute": "Attribut de document XML",
"HeaderUploadSubtitle": "téléverser des sous-titres",
"HeaderUploadImage": "téléverser une image",
"HeaderUploadSubtitle": "Uploader des sous-titres",
"HeaderUploadImage": "Uploader une image",
"HeaderUpcomingOnTV": "Bientôt à la télé",
"HeaderUninstallPlugin": "Désinstaller l'extension",
"HeaderTunerDevices": "Syntoniseur",
@ -576,7 +576,7 @@
"HeaderOnNow": "Jouant maintenant",
"HeaderNewApiKey": "Nouvelle clef d'API",
"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\".",
"LabelMetadataSavers": "Métadonnées",
"HeaderInstantMix": "Mix instantané",
@ -591,7 +591,7 @@
"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.",
"HeaderCastAndCrew": "Distribution et équipe",
"HeaderBranding": "Slogan",
"HeaderBranding": "Branding",
"LabelAudioSampleRate": "Taux déchantillonnage audio",
"LabelAudioLanguagePreference": "Langue audio préférée",
"LabelAudioCodec": "Codec audio",
@ -639,7 +639,7 @@
"ShowYear": "Montrer l'année",
"ShowTitle": "Montrer le titre",
"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",
"LabelDidlMode": "Mode DIDL",
"LabelDeinterlaceMethod": "Méthode pour désentrelacer",
@ -701,7 +701,7 @@
"Small": "Petit",
"SkipEpisodesAlreadyInMyLibraryHelp": "Les épisodes seront comparés en utilisant les saisons et les numéros d'épisodes, si disponibles.",
"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.",
"Subtitle": "Sous-titres",
"Studios": "Studios",
@ -709,12 +709,12 @@
"Sports": "Sports",
"LabelEmbedAlbumArtDidl": "Intégrer la couverture d'album dans le DIDL",
"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",
"MusicVideos": "Vidéos musicales",
"MusicVideos": "Vidéoclips",
"OptionBluray": "BD",
"LabelEnableDlnaPlayTo": "Activer la fonction DLNA \"Play To\"",
"Engineer": "Ingénieur du son",
"Engineer": "Ingénieur de son",
"Conductor": "Conducteur",
"Casual": "Casuel",
"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é.",
"Console": "Console",
"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.",
"MessageNoServersAvailable": "Aucun serveur n'a été trouvé par la découverte de serveur automatique.",
"MessageNoRepositories": "Pas de dépôts.",
"MessageNoPluginsInstalled": "Vous n'avez pas de plugins installés.",
"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.",
"MessageNoGenresAvailable": "Utiliser des fournisseurs de métadonnées pour récupérer les genres depuis internet.",
"MessageNoAvailablePlugins": "Aucun plugin disponible.",
@ -742,11 +742,11 @@
"MessageInvalidUser": "Nom d'utilisateur ou mot de passe invalide. 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.",
"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.",
"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.",
"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.",
"MessageDeleteTaskTrigger": "Es-tu sûr de vouloir supprimer ce déclencheur de tâche ?",
"MessageCreateAccountAt": "Créez un compte sur {0}",
@ -757,7 +757,7 @@
"MessageConfirmRemoveMediaLocation": "Es-tu sûr de vouloir supprimer cet emplacement ?",
"MessageConfirmRecordingCancellation": "Canceller l'enregistrement ?",
"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 ?",
"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 ?",
@ -783,7 +783,7 @@
"MediaInfoChannels": "Chaînes",
"MediaInfoBitDepth": "Profondeur de couleur",
"MediaInfoAspectRatio": "Rapport de forme",
"LiveTV": "TV en direct",
"LiveTV": "Télé en direct",
"MediaInfoBitrate": "Débit binaire",
"MediaInfoForced": "Forcé",
"MediaInfoTitle": "Titre",
@ -793,10 +793,10 @@
"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.",
"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",
"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",
"OptionEmbedSubtitles": "Intégrer dans l'conteneur",
"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.",
"OptionAllowRemoteSharedDevices": "Autoriser le contrôle à distance des appareils partagés",
"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",
"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.",
"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.",
"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",
"OptionAdminUsers": "Administrateurs",
"Option3D": "3D",
@ -860,7 +860,7 @@
"Name": "Nom",
"MySubtitles": "Mes sous-titres",
"Mute": "Muet",
"MusicVideo": "Vidéo musical",
"MusicVideo": "Vidéoclip",
"MusicLibraryHelp": "Consultez le {0}guide de dénomination de musique{1}.",
"MusicArtist": "Artiste 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.",
"Arranger": "Arrangeur",
"AddToFavorites": "Ajouter aux favoris",
"EnableRewatchingNextUp": "Activer le re-visionnage dans \"La Prochaine\"",
"EnableRewatchingNextUp": "Activer le re-visionnage dans \"À suivre\"",
"Digital": "Numérique",
"Cursive": "Cursive",
"CopyFailed": "Me pouvait pas copier",
"Copy": "Copie",
"CopyFailed": "Impossible de copier",
"Copy": "Copier",
"Copied": "Copié",
"ButtonSpace": "Espace",
"ButtonExitApp": "Ferme l'application",
"ButtonClose": "Fermer",
"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",
"LabelEnableIP6Help": "Activez la fonctionnalité IPv6.",
"LabelEnableIP6": "Activez IPv6",
@ -930,7 +930,7 @@
"LabelEnableDlnaServerHelp": "Autoriser les appareils UPnP de votre réseau à parcourir et à lire du contenu.",
"LabelEnableDlnaServer": "Activer le serveur DLNA",
"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.",
"LabelAutomaticallyAddToCollection": "Ajouter automatiquement à la collection",
"ItemDetails": "Détails de L'article",
@ -938,7 +938,7 @@
"HeaderSyncPlayTimeSyncSettings": "Synchronisation de L'heure",
"HeaderSyncPlayPlaybackSettings": "Relecture",
"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.",
"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.",
@ -951,8 +951,8 @@
"LabelEpisodeNumber": "Numéro d'épisode",
"LabelEvent": "Évènement",
"LabelEveryXMinutes": "Tous les",
"LabelExtractChaptersDuringLibraryScan": "Extraire les images des chapitres pendant l'actualisation 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.",
"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 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é",
"LabelForgotPasswordUsernameHelp": "Saisissez votre nom d'utilisateur, si vous vous en souvenez.",
"LabelImportOnlyFavoriteChannels": "Restreindre aux chaînes ajoutées aux favoris",
@ -977,12 +977,12 @@
"LabelFormat": "Format",
"LabelFolder": "Dossier",
"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.",
"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.",
"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.",
"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.",
@ -1000,7 +1000,7 @@
"LabelMaxBackdropsPerItem": "Nombre maximal d'images d'arrière-plan par élément",
"LabelLineup": "Programmation",
"LabelMatchType": "Type recherché",
"LabelMaxDaysForNextUp": "Nombre de jours maximal dans « À suivre »",
"LabelMaxDaysForNextUp": "Nombre de jours maximal dans \"À suivre\"",
"LabelIsForced": "Forcé",
"LabelInNetworkSignInWithEasyPassword": "Activer l'authentification locale avec un code Easy PIN",
"LabelInternetQuality": "Qualité d'Internet",
@ -1052,7 +1052,7 @@
"LabelProfileCodecs": "Codecs",
"LabelProtocolInfoHelp": "La valeur utilisée pour répondre aux requêtes « GetProtocolInfo » de lappareil.",
"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.",
"LabelSyncPlayLeaveGroupDescription": "Désactiver SyncPlay",
"LabelSyncPlayNewGroup": "Nouveau groupe",
@ -1132,7 +1132,7 @@
"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).",
"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",
"LabelProtocolInfo": "Infos protocole",
"LabelRecordingPath": "Chemin par défaut pour lenregistrement",
@ -1183,7 +1183,7 @@
"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.",
"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",
"LabelPlayDefaultAudioTrack": "Lire la piste audio par défaut peu importe la langue",
"LabelPostProcessor": "Application de post-traitement",
@ -1248,7 +1248,7 @@
"Large": "Grand",
"LabelWeb": "Réseau",
"LabelUnstable": "Instable",
"LabelUserAgent": "Agent d'utilisateur",
"LabelUserAgent": "Agent utilisateur",
"LabelTranscodingThreadCount": "Nombre de threads de transcodage",
"LiveBroadcasts": "Diffusions en direct",
"LabelVaapiDevice": "Appareil VA-API",
@ -1262,19 +1262,19 @@
"LabelZipCode": "Code postal",
"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.",
"LabelTunerIpAddress": "Adresse IP du tuner",
"LabelTunerIpAddress": "Adresse IP du syntoniseur",
"LabelUserLoginAttemptsBeforeLockout": "Échec des tentatives de connexion avant que l'utilisateur ne soit verrouillé",
"LogLevel.Debug": "Déboguer",
"LogLevel.Information": "Information",
"LogLevel.Error": "Erreur",
"LogLevel.Critical": "Critique",
"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",
"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",
"LabelTunerType": "Type de tuner",
"LabelTunerType": "Type de syntoniseur",
"LabelUserLibrary": "Librairie d'utilisateur",
"LabelVaapiDeviceHelp": "Il s'agit du nœud de rendu utilisé pour l'accélération matérielle.",
"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.",
"LabelTonemappingRange": "Plage 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.",
"LearnHowYouCanContribute": "Découvrez comment vous pouvez contribuer.",
"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.",
"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.",
@ -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 »)",
"DeleteName": "Supprimer {0}",
"MediaInfoLevel": "Niveau",
"HeaderAllRecordings": "Tous les Enregistrements",
"HeaderAllRecordings": "Tous les enregistrements",
"ChannelResolutionSD": "SD",
"ChannelResolutionHD": "HD",
"ChannelResolutionFullHD": "Full HD",
@ -1337,11 +1337,377 @@
"MenuClose": "Fermer le menu",
"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.",
"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",
"EnableLibrary": "Activer la médiathèque",
"EnableLibraryHelp": "La désactivation de la médiathèque la masquera depuis toutes les vues utilisateur.",
"AirPlay": "AirPlay",
"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.",
"Actor": "Acteur",
"Add": "Ajouter",
"AddToCollection": "Ajouter à une collection",
"AddToCollection": "Ajouter à la collection",
"AddToPlayQueue": "Ajouter à la file d'attente",
"AddToPlaylist": "Ajouter à une liste de lecture",
"AddToPlaylist": "Ajouter à la liste de lecture",
"AddedOnValue": "Ajouté le {0}",
"AdditionalNotificationServices": "Visiter le catalogue d'extensions pour installer des services de notifications supplémentaires.",
"AirDate": "Date de diffusion",
@ -426,7 +426,7 @@
"LabelAllowedRemoteAddresses": "Filtre d'adresse IP distante",
"LabelAllowedRemoteAddressesMode": "Type de filtre des adresses IP distantes",
"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",
"LabelArtistsHelp": "Séparer les artistes par un point-virgule.",
"LabelAudioLanguagePreference": "Langue audio préférée",
@ -940,7 +940,7 @@
"Overview": "Synopsis",
"PackageInstallCancelled": "L'installation de {0} (version {1}) a été annulé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",
"PasswordMatchError": "Le mot de passe et sa confirmation doivent correspondre.",
"PasswordResetComplete": "Le mot de passe a été réinitialisé.",
@ -1082,13 +1082,13 @@
"TabServer": "Serveur",
"TabUpcoming": "À venir",
"Tags": "Étiquettes",
"TagsValue": "Mots clés : {0}",
"TagsValue": "Étiquettes : {0}",
"TellUsAboutYourself": "Parlez-nous de vous",
"ThemeSongs": "Thèmes musicaux",
"ThemeVideos": "Génériques",
"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.",
"Thumb": "Miniature",
"Thumb": "Vignettes",
"Thursday": "Jeudi",
"TitleHardwareAcceleration": "Accélération matérielle",
"TitleHostingSettings": "Paramètres d'hébergement",
@ -1245,7 +1245,7 @@
"LabelDroppedFrames": "Images perdues",
"LabelCorruptedFrames": "Images corrompues",
"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",
"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.",
@ -1272,7 +1272,7 @@
"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.",
"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",
"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",
@ -1377,7 +1377,7 @@
"Other": "Autre",
"PosterCard": "Affiche sur carte",
"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.",
"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.",
@ -1615,7 +1615,7 @@
"SelectAll": "Tout sélectionner",
"ButtonExitApp": "Quitter l'application",
"Clip": "Clip",
"ThemeVideo": "Thème Vidéo",
"ThemeVideo": "Thème vidéo",
"ThemeSong": "Thème musical",
"Sample": "Échantillon",
"Scene": "Scène",
@ -1676,7 +1676,7 @@
"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.",
"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",
"OptionDateEpisodeAdded": "Date d'ajout de l'épisode",
"IgnoreDts": "Ignorer le DTS (horodatage de décodage)",
@ -1719,7 +1719,7 @@
"Short": "Court-métrage",
"HeaderPerformance": "Performance",
"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",
"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",
@ -1807,8 +1807,8 @@
"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 ?",
"DeleteEntireSeries": "Supprimer {0} Épisodes",
"DeleteSeries": "Supprimer Séries",
"DeleteEpisode": "Suppri",
"DeleteSeries": "Supprimer la série",
"DeleteEpisode": "Supprimer l'épisode",
"HeaderDeleteSeries": "Supprimer Séries",
"EnableSmoothScroll": "Activer le défilement fluide",
"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.",
"LabelImageIntervalHelp": "Intervalle de temps (ms) entre chaque nouvelle image trickplay.",
"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.",
"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",
@ -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.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.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",
"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.",
"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 ?",
"DeleteLyrics": "Supprimez ces paroles",
"HeaderNoLyrics": "Aucune paroles n'ont êtes trouves",
"DeleteLyrics": "Supprimer ces paroles",
"HeaderNoLyrics": "Aucune parole trouvée",
"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": "דקות נותרות בספר המוקלט להמשך",
"LabelMaxAudiobookResumeHelp": "כותרים נחשבים כנוגנו במלואם כאשר משך הזמן הנותר קטן יותר מערך זה.",
"LabelMetadataReaders": "קוראי מטא-דאטה",
"LabelMetadataSavers": "שומרי מטא-דאטה"
"LabelMetadataSavers": "שומרי מטא-דאטה",
"PlaybackError.RateLimitExceeded": "מדיה"
}

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