mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'master' into fix-some-of-the-code-smells
This commit is contained in:
commit
13786b082d
48 changed files with 851 additions and 448 deletions
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
|
@ -21,11 +21,11 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
|
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # v2.3.3
|
uses: github/codeql-action/init@0225834cc549ee0ca93cb085b92954821a145866 # v2.3.5
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
queries: +security-extended
|
queries: +security-extended
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # v2.3.3
|
uses: github/codeql-action/autobuild@0225834cc549ee0ca93cb085b92954821a145866 # v2.3.5
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # v2.3.3
|
uses: github/codeql-action/analyze@0225834cc549ee0ca93cb085b92954821a145866 # v2.3.5
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
- [Vankerkom](https://github.com/vankerkom)
|
- [Vankerkom](https://github.com/vankerkom)
|
||||||
- [edvwib](https://github.com/edvwib)
|
- [edvwib](https://github.com/edvwib)
|
||||||
- [Rob Farraher](https://github.com/farraherbg)
|
- [Rob Farraher](https://github.com/farraherbg)
|
||||||
|
- [TelepathicWalrus](https://github.com/TelepathicWalrus)
|
||||||
- [Pier-Luc Ducharme](https://github.com/pl-ducharme)
|
- [Pier-Luc Ducharme](https://github.com/pl-ducharme)
|
||||||
- [Anantharaju S](https://github.com/Anantharajus)
|
- [Anantharaju S](https://github.com/Anantharajus)
|
||||||
- [Merlin Sievers](https://github.com/dann-merlin)
|
- [Merlin Sievers](https://github.com/dann-merlin)
|
||||||
|
|
29
package-lock.json
generated
29
package-lock.json
generated
|
@ -2825,12 +2825,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@jellyfin/sdk": {
|
"node_modules/@jellyfin/sdk": {
|
||||||
"version": "0.0.0-unstable.202304122102",
|
"version": "0.0.0-unstable.202305300501",
|
||||||
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202304122102.tgz",
|
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202305300501.tgz",
|
||||||
"integrity": "sha512-KToOmK3GmbjovtFPgb3dYx8cV6bopo46fhTkHDnKLqsmwqBz5/QKk7Z8NbR+5YaojNAP4LUYnenZmMK9HQ2YeA==",
|
"integrity": "sha512-xAiVZQFtnRkikiYcYSue75+socgwVY+NwY3PaRDTbjH90Guo4ptcLXmlgAFcUad+J3jpwpdAR9+fKmSomUFKRA==",
|
||||||
"dependencies": {
|
|
||||||
"compare-versions": "5.0.3"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"axios": "^1.3.4"
|
"axios": "^1.3.4"
|
||||||
}
|
}
|
||||||
|
@ -5653,11 +5650,6 @@
|
||||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/compare-versions": {
|
|
||||||
"version": "5.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.3.tgz",
|
|
||||||
"integrity": "sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A=="
|
|
||||||
},
|
|
||||||
"node_modules/component-emitter": {
|
"node_modules/component-emitter": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
||||||
|
@ -22165,12 +22157,10 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@jellyfin/sdk": {
|
"@jellyfin/sdk": {
|
||||||
"version": "0.0.0-unstable.202304122102",
|
"version": "0.0.0-unstable.202305300501",
|
||||||
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202304122102.tgz",
|
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.0.0-unstable.202305300501.tgz",
|
||||||
"integrity": "sha512-KToOmK3GmbjovtFPgb3dYx8cV6bopo46fhTkHDnKLqsmwqBz5/QKk7Z8NbR+5YaojNAP4LUYnenZmMK9HQ2YeA==",
|
"integrity": "sha512-xAiVZQFtnRkikiYcYSue75+socgwVY+NwY3PaRDTbjH90Guo4ptcLXmlgAFcUad+J3jpwpdAR9+fKmSomUFKRA==",
|
||||||
"requires": {
|
"requires": {}
|
||||||
"compare-versions": "5.0.3"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"@jridgewell/gen-mapping": {
|
"@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
|
@ -24280,11 +24270,6 @@
|
||||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"compare-versions": {
|
|
||||||
"version": "5.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.3.tgz",
|
|
||||||
"integrity": "sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A=="
|
|
||||||
},
|
|
||||||
"component-emitter": {
|
"component-emitter": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
||||||
|
|
112
src/apps/experimental/components/AppToolbar/RemotePlayButton.tsx
Normal file
112
src/apps/experimental/components/AppToolbar/RemotePlayButton.tsx
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import CastConnected from '@mui/icons-material/CastConnected';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Cast from '@mui/icons-material/Cast';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
|
|
||||||
|
import { playbackManager } from 'components/playback/playbackmanager';
|
||||||
|
import globalize from 'scripts/globalize';
|
||||||
|
import Events from 'utils/events';
|
||||||
|
|
||||||
|
import RemotePlayMenu, { ID } from './menus/RemotePlayMenu';
|
||||||
|
import RemotePlayActiveMenu, { ID as ACTIVE_ID } from './menus/RemotePlayActiveMenu';
|
||||||
|
|
||||||
|
const RemotePlayButton = () => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const [ playerInfo, setPlayerInfo ] = useState(playbackManager.getPlayerInfo());
|
||||||
|
|
||||||
|
const updatePlayerInfo = useCallback(() => {
|
||||||
|
setPlayerInfo(playbackManager.getPlayerInfo());
|
||||||
|
}, [ setPlayerInfo ]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Events.on(playbackManager, 'playerchange', updatePlayerInfo);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
Events.off(playbackManager, 'playerchange', updatePlayerInfo);
|
||||||
|
};
|
||||||
|
}, [ updatePlayerInfo ]);
|
||||||
|
|
||||||
|
const [ remotePlayMenuAnchorEl, setRemotePlayMenuAnchorEl ] = useState<null | HTMLElement>(null);
|
||||||
|
const isRemotePlayMenuOpen = Boolean(remotePlayMenuAnchorEl);
|
||||||
|
|
||||||
|
const onRemotePlayButtonClick = useCallback((event) => {
|
||||||
|
setRemotePlayMenuAnchorEl(event.currentTarget);
|
||||||
|
}, [ setRemotePlayMenuAnchorEl ]);
|
||||||
|
|
||||||
|
const onRemotePlayMenuClose = useCallback(() => {
|
||||||
|
setRemotePlayMenuAnchorEl(null);
|
||||||
|
}, [ setRemotePlayMenuAnchorEl ]);
|
||||||
|
|
||||||
|
const [ remotePlayActiveMenuAnchorEl, setRemotePlayActiveMenuAnchorEl ] = useState<null | HTMLElement>(null);
|
||||||
|
const isRemotePlayActiveMenuOpen = Boolean(remotePlayActiveMenuAnchorEl);
|
||||||
|
|
||||||
|
const onRemotePlayActiveButtonClick = useCallback((event) => {
|
||||||
|
setRemotePlayActiveMenuAnchorEl(event.currentTarget);
|
||||||
|
}, [ setRemotePlayActiveMenuAnchorEl ]);
|
||||||
|
|
||||||
|
const onRemotePlayActiveMenuClose = useCallback(() => {
|
||||||
|
setRemotePlayActiveMenuAnchorEl(null);
|
||||||
|
}, [ setRemotePlayActiveMenuAnchorEl ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{(playerInfo && !playerInfo.isLocalPlayer) ? (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
alignSelf: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tooltip title={globalize.translate('ButtonCast')}>
|
||||||
|
<Button
|
||||||
|
variant='text'
|
||||||
|
size='large'
|
||||||
|
startIcon={<CastConnected />}
|
||||||
|
aria-label={globalize.translate('ButtonCast')}
|
||||||
|
aria-controls={ACTIVE_ID}
|
||||||
|
aria-haspopup='true'
|
||||||
|
onClick={onRemotePlayActiveButtonClick}
|
||||||
|
color='inherit'
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.primary.main
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{playerInfo.deviceName || playerInfo.name}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Tooltip title={globalize.translate('ButtonCast')}>
|
||||||
|
<IconButton
|
||||||
|
size='large'
|
||||||
|
aria-label={globalize.translate('ButtonCast')}
|
||||||
|
aria-controls={ID}
|
||||||
|
aria-haspopup='true'
|
||||||
|
onClick={onRemotePlayButtonClick}
|
||||||
|
color='inherit'
|
||||||
|
>
|
||||||
|
<Cast />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<RemotePlayMenu
|
||||||
|
open={isRemotePlayMenuOpen}
|
||||||
|
anchorEl={remotePlayMenuAnchorEl}
|
||||||
|
onMenuClose={onRemotePlayMenuClose}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RemotePlayActiveMenu
|
||||||
|
open={isRemotePlayActiveMenuOpen}
|
||||||
|
anchorEl={remotePlayActiveMenuAnchorEl}
|
||||||
|
onMenuClose={onRemotePlayActiveMenuClose}
|
||||||
|
playerInfo={playerInfo}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RemotePlayButton;
|
|
@ -15,6 +15,7 @@ import globalize from 'scripts/globalize';
|
||||||
import AppTabs from '../tabs/AppTabs';
|
import AppTabs from '../tabs/AppTabs';
|
||||||
import { isDrawerPath } from '../drawers/AppDrawer';
|
import { isDrawerPath } from '../drawers/AppDrawer';
|
||||||
import UserMenuButton from './UserMenuButton';
|
import UserMenuButton from './UserMenuButton';
|
||||||
|
import RemotePlayButton from './RemotePlayButton';
|
||||||
|
|
||||||
interface AppToolbarProps {
|
interface AppToolbarProps {
|
||||||
isDrawerOpen: boolean
|
isDrawerOpen: boolean
|
||||||
|
@ -89,6 +90,8 @@ const AppToolbar: FC<AppToolbarProps> = ({
|
||||||
{isUserLoggedIn && (
|
{isUserLoggedIn && (
|
||||||
<>
|
<>
|
||||||
<Box sx={{ display: 'flex', flexGrow: 1, justifyContent: 'flex-end' }}>
|
<Box sx={{ display: 'flex', flexGrow: 1, justifyContent: 'flex-end' }}>
|
||||||
|
<RemotePlayButton />
|
||||||
|
|
||||||
<Tooltip title={globalize.translate('Search')}>
|
<Tooltip title={globalize.translate('Search')}>
|
||||||
<IconButton
|
<IconButton
|
||||||
size='large'
|
size='large'
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
import Check from '@mui/icons-material/Check';
|
||||||
|
import Close from '@mui/icons-material/Close';
|
||||||
|
import SettingsRemote from '@mui/icons-material/SettingsRemote';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import ListSubheader from '@mui/material/ListSubheader';
|
||||||
|
import Menu, { MenuProps } from '@mui/material/Menu';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import dialog from 'components/dialog/dialog';
|
||||||
|
import { playbackManager } from 'components/playback/playbackmanager';
|
||||||
|
import React, { FC, useCallback, useState } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { enable, isEnabled, supported } from 'scripts/autocast';
|
||||||
|
import globalize from 'scripts/globalize';
|
||||||
|
|
||||||
|
interface RemotePlayActiveMenuProps extends MenuProps {
|
||||||
|
onMenuClose: () => void
|
||||||
|
playerInfo: {
|
||||||
|
name: string
|
||||||
|
isLocalPlayer: boolean
|
||||||
|
id?: string
|
||||||
|
deviceName?: string
|
||||||
|
playableMediaTypes?: string[]
|
||||||
|
supportedCommands?: string[]
|
||||||
|
} | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ID = 'app-remote-play-active-menu';
|
||||||
|
|
||||||
|
const RemotePlayActiveMenu: FC<RemotePlayActiveMenuProps> = ({
|
||||||
|
anchorEl,
|
||||||
|
open,
|
||||||
|
onMenuClose,
|
||||||
|
playerInfo
|
||||||
|
}) => {
|
||||||
|
const [ isDisplayMirrorEnabled, setIsDisplayMirrorEnabled ] = useState(playbackManager.enableDisplayMirroring());
|
||||||
|
const isDisplayMirrorSupported = playerInfo?.supportedCommands && playerInfo.supportedCommands.indexOf('DisplayContent') !== -1;
|
||||||
|
const toggleDisplayMirror = useCallback(() => {
|
||||||
|
playbackManager.enableDisplayMirroring(!isDisplayMirrorEnabled);
|
||||||
|
setIsDisplayMirrorEnabled(!isDisplayMirrorEnabled);
|
||||||
|
}, [ isDisplayMirrorEnabled, setIsDisplayMirrorEnabled ]);
|
||||||
|
|
||||||
|
const [ isAutoCastEnabled, setIsAutoCastEnabled ] = useState(isEnabled());
|
||||||
|
const isAutoCastSupported = supported();
|
||||||
|
const toggleAutoCast = useCallback(() => {
|
||||||
|
enable(!isAutoCastEnabled);
|
||||||
|
setIsAutoCastEnabled(!isAutoCastEnabled);
|
||||||
|
}, [ isAutoCastEnabled, setIsAutoCastEnabled ]);
|
||||||
|
|
||||||
|
const remotePlayerName = playerInfo?.deviceName || playerInfo?.name;
|
||||||
|
|
||||||
|
const disconnectRemotePlayer = useCallback(() => {
|
||||||
|
if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) {
|
||||||
|
dialog.show({
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
name: globalize.translate('Yes'),
|
||||||
|
id: 'yes'
|
||||||
|
}, {
|
||||||
|
name: globalize.translate('No'),
|
||||||
|
id: 'no'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
text: globalize.translate('ConfirmEndPlayerSession', remotePlayerName)
|
||||||
|
}).then(id => {
|
||||||
|
onMenuClose();
|
||||||
|
|
||||||
|
if (id === 'yes') {
|
||||||
|
playbackManager.getCurrentPlayer().endSession();
|
||||||
|
}
|
||||||
|
playbackManager.setDefaultPlayerActive();
|
||||||
|
}).catch(() => {
|
||||||
|
// Dialog closed
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
onMenuClose();
|
||||||
|
playbackManager.setDefaultPlayerActive();
|
||||||
|
}
|
||||||
|
}, [ onMenuClose, remotePlayerName ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: 'bottom',
|
||||||
|
horizontal: 'right'
|
||||||
|
}}
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: 'top',
|
||||||
|
horizontal: 'right'
|
||||||
|
}}
|
||||||
|
id={ID}
|
||||||
|
keepMounted
|
||||||
|
open={open}
|
||||||
|
onClose={onMenuClose}
|
||||||
|
MenuListProps={{
|
||||||
|
'aria-labelledby': 'remote-play-active-subheader',
|
||||||
|
subheader: (
|
||||||
|
<ListSubheader component='div' id='remote-play-active-subheader'>
|
||||||
|
{remotePlayerName}
|
||||||
|
</ListSubheader>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isDisplayMirrorSupported && (
|
||||||
|
<MenuItem onClick={toggleDisplayMirror}>
|
||||||
|
{isDisplayMirrorEnabled && (
|
||||||
|
<ListItemIcon>
|
||||||
|
<Check />
|
||||||
|
</ListItemIcon>
|
||||||
|
)}
|
||||||
|
<ListItemText inset={!isDisplayMirrorEnabled}>
|
||||||
|
{globalize.translate('EnableDisplayMirroring')}
|
||||||
|
</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isAutoCastSupported && (
|
||||||
|
<MenuItem onClick={toggleAutoCast}>
|
||||||
|
{isAutoCastEnabled && (
|
||||||
|
<ListItemIcon>
|
||||||
|
<Check />
|
||||||
|
</ListItemIcon>
|
||||||
|
)}
|
||||||
|
<ListItemText inset={!isAutoCastEnabled}>
|
||||||
|
{globalize.translate('EnableAutoCast')}
|
||||||
|
</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(isDisplayMirrorSupported || isAutoCastSupported) && <Divider />}
|
||||||
|
|
||||||
|
<MenuItem
|
||||||
|
component={Link}
|
||||||
|
to='/queue'
|
||||||
|
onClick={onMenuClose}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<SettingsRemote />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
{globalize.translate('HeaderRemoteControl')}
|
||||||
|
</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
<Divider />
|
||||||
|
<MenuItem onClick={disconnectRemotePlayer}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Close />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
{globalize.translate('Disconnect')}
|
||||||
|
</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RemotePlayActiveMenu;
|
|
@ -0,0 +1,100 @@
|
||||||
|
import Warning from '@mui/icons-material/Warning';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import Menu, { type MenuProps } from '@mui/material/Menu';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import React, { FC, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import globalize from 'scripts/globalize';
|
||||||
|
import { playbackManager } from 'components/playback/playbackmanager';
|
||||||
|
import { pluginManager } from 'components/pluginManager';
|
||||||
|
import type { PlayTarget } from 'types/playTarget';
|
||||||
|
|
||||||
|
import PlayTargetIcon from '../../PlayTargetIcon';
|
||||||
|
|
||||||
|
interface RemotePlayMenuProps extends MenuProps {
|
||||||
|
onMenuClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ID = 'app-remote-play-menu';
|
||||||
|
|
||||||
|
const RemotePlayMenu: FC<RemotePlayMenuProps> = ({
|
||||||
|
anchorEl,
|
||||||
|
open,
|
||||||
|
onMenuClose
|
||||||
|
}) => {
|
||||||
|
// TODO: Add other checks for support (Android app, secure context, etc)
|
||||||
|
const isChromecastPluginLoaded = !!pluginManager.plugins.find(plugin => plugin.id === 'chromecast');
|
||||||
|
|
||||||
|
const [ playbackTargets, setPlaybackTargets ] = useState<PlayTarget[]>([]);
|
||||||
|
|
||||||
|
const onPlayTargetClick = (target: PlayTarget) => {
|
||||||
|
playbackManager.trySetActivePlayer(target.playerName, target);
|
||||||
|
onMenuClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchPlaybackTargets = async () => {
|
||||||
|
setPlaybackTargets(
|
||||||
|
await playbackManager.getTargets()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (open) {
|
||||||
|
fetchPlaybackTargets()
|
||||||
|
.catch(err => {
|
||||||
|
console.error('[AppRemotePlayMenu] unable to get playback targets', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [ open, setPlaybackTargets ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: 'bottom',
|
||||||
|
horizontal: 'right'
|
||||||
|
}}
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: 'top',
|
||||||
|
horizontal: 'right'
|
||||||
|
}}
|
||||||
|
id={ID}
|
||||||
|
keepMounted
|
||||||
|
open={open}
|
||||||
|
onClose={onMenuClose}
|
||||||
|
>
|
||||||
|
{!isChromecastPluginLoaded && ([
|
||||||
|
<MenuItem key='cast-unsupported-item' disabled>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Warning />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
{globalize.translate('GoogleCastUnsupported')}
|
||||||
|
</ListItemText>
|
||||||
|
</MenuItem>,
|
||||||
|
<Divider key='cast-unsupported-divider' />
|
||||||
|
])}
|
||||||
|
|
||||||
|
{playbackTargets.map(target => (
|
||||||
|
<MenuItem
|
||||||
|
key={target.id}
|
||||||
|
// Since we are looping over targets there is no good way to avoid creating a new function here
|
||||||
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
|
onClick={() => onPlayTargetClick(target)}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<PlayTargetIcon target={target} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText
|
||||||
|
primary={ target.appName ? `${target.name} - ${target.appName}` : target.name }
|
||||||
|
secondary={ target.user?.Name }
|
||||||
|
/>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RemotePlayMenu;
|
38
src/apps/experimental/components/PlayTargetIcon.tsx
Normal file
38
src/apps/experimental/components/PlayTargetIcon.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Cast from '@mui/icons-material/Cast';
|
||||||
|
import Computer from '@mui/icons-material/Computer';
|
||||||
|
import Devices from '@mui/icons-material/Devices';
|
||||||
|
import Smartphone from '@mui/icons-material/Smartphone';
|
||||||
|
import Tablet from '@mui/icons-material/Tablet';
|
||||||
|
import Tv from '@mui/icons-material/Tv';
|
||||||
|
|
||||||
|
import browser from 'scripts/browser';
|
||||||
|
import type { PlayTarget } from 'types/playTarget';
|
||||||
|
|
||||||
|
const PlayTargetIcon = ({ target }: { target: PlayTarget }) => {
|
||||||
|
if (!target.deviceType && target.isLocalPlayer) {
|
||||||
|
if (browser.tv) {
|
||||||
|
return <Tv />;
|
||||||
|
} else if (browser.mobile) {
|
||||||
|
return <Smartphone />;
|
||||||
|
}
|
||||||
|
return <Computer />;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (target.deviceType) {
|
||||||
|
case 'smartphone':
|
||||||
|
return <Smartphone />;
|
||||||
|
case 'tablet':
|
||||||
|
return <Tablet />;
|
||||||
|
case 'desktop':
|
||||||
|
return <Computer />;
|
||||||
|
case 'cast':
|
||||||
|
return <Cast />;
|
||||||
|
case 'tv':
|
||||||
|
return <Tv />;
|
||||||
|
default:
|
||||||
|
return <Devices />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlayTargetIcon;
|
|
@ -1,4 +1,11 @@
|
||||||
import { Lan, VpnKey, Article, EditNotifications, Extension, Schedule, ExpandLess, ExpandMore } from '@mui/icons-material';
|
import Article from '@mui/icons-material/Article';
|
||||||
|
import EditNotifications from '@mui/icons-material/EditNotifications';
|
||||||
|
import ExpandLess from '@mui/icons-material/ExpandLess';
|
||||||
|
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||||
|
import Extension from '@mui/icons-material/Extension';
|
||||||
|
import Lan from '@mui/icons-material/Lan';
|
||||||
|
import Schedule from '@mui/icons-material/Schedule';
|
||||||
|
import VpnKey from '@mui/icons-material/VpnKey';
|
||||||
import Collapse from '@mui/material/Collapse';
|
import Collapse from '@mui/material/Collapse';
|
||||||
import List from '@mui/material/List';
|
import List from '@mui/material/List';
|
||||||
import ListItem from '@mui/material/ListItem';
|
import ListItem from '@mui/material/ListItem';
|
||||||
|
@ -63,7 +70,7 @@ const AdvancedDrawerSection = () => {
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<EditNotifications />
|
<EditNotifications />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={globalize.translate('TabNotifications')} />
|
<ListItemText primary={globalize.translate('Notifications')} />
|
||||||
</ListItemLink>
|
</ListItemLink>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem disablePadding>
|
<ListItem disablePadding>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
|
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
|
||||||
|
|
||||||
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
||||||
|
{ path: 'notificationsettings.html', page: 'dashboard/notifications' },
|
||||||
{ path: 'usernew.html', page: 'user/usernew' },
|
{ path: 'usernew.html', page: 'user/usernew' },
|
||||||
{ path: 'userprofiles.html', page: 'user/userprofiles' },
|
{ path: 'userprofiles.html', page: 'user/userprofiles' },
|
||||||
{ path: 'useredit.html', page: 'user/useredit' },
|
{ path: 'useredit.html', page: 'user/useredit' },
|
||||||
|
|
|
@ -103,18 +103,6 @@ export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
|
||||||
controller: 'dashboard/metadatanfo',
|
controller: 'dashboard/metadatanfo',
|
||||||
view: 'dashboard/metadatanfo.html'
|
view: 'dashboard/metadatanfo.html'
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
path: 'notificationsetting.html',
|
|
||||||
pageProps: {
|
|
||||||
controller: 'dashboard/notifications/notification/index',
|
|
||||||
view: 'dashboard/notifications/notification/index.html'
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
path: 'notificationsettings.html',
|
|
||||||
pageProps: {
|
|
||||||
controller: 'dashboard/notifications/notifications/index',
|
|
||||||
view: 'dashboard/notifications/notifications/index.html'
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
path: 'playbackconfiguration.html',
|
path: 'playbackconfiguration.html',
|
||||||
pageProps: {
|
pageProps: {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
|
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
|
||||||
|
|
||||||
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
||||||
|
{ path: 'notificationsettings.html', page: 'dashboard/notifications' },
|
||||||
{ path: 'usernew.html', page: 'user/usernew' },
|
{ path: 'usernew.html', page: 'user/usernew' },
|
||||||
{ path: 'userprofiles.html', page: 'user/userprofiles' },
|
{ path: 'userprofiles.html', page: 'user/userprofiles' },
|
||||||
{ path: 'useredit.html', page: 'user/useredit' },
|
{ path: 'useredit.html', page: 'user/useredit' },
|
||||||
|
|
36
src/apps/stable/routes/dashboard/notifications.tsx
Normal file
36
src/apps/stable/routes/dashboard/notifications.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Page from 'components/Page';
|
||||||
|
import globalize from 'scripts/globalize';
|
||||||
|
|
||||||
|
const PluginLink = () => (
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `<a
|
||||||
|
is='emby-linkbutton'
|
||||||
|
class='button-link'
|
||||||
|
href='#/addplugin.html?name=Webhook&guid=71552a5a5c5c4350a2aeebe451a30173'
|
||||||
|
>
|
||||||
|
${globalize.translate('GetThePlugin')}
|
||||||
|
</a>`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Notifications = () => (
|
||||||
|
<Page
|
||||||
|
id='notificationSettingPage'
|
||||||
|
title={globalize.translate('Notifications')}
|
||||||
|
className='mainAnimatedPage type-interior'
|
||||||
|
>
|
||||||
|
<div className='content-primary'>
|
||||||
|
<h2>{globalize.translate('Notifications')}</h2>
|
||||||
|
<p>
|
||||||
|
{globalize.translate('NotificationsMovedMessage')}
|
||||||
|
</p>
|
||||||
|
<PluginLink />
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Notifications;
|
|
@ -103,18 +103,6 @@ export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
|
||||||
controller: 'dashboard/metadatanfo',
|
controller: 'dashboard/metadatanfo',
|
||||||
view: 'dashboard/metadatanfo.html'
|
view: 'dashboard/metadatanfo.html'
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
path: 'notificationsetting.html',
|
|
||||||
pageProps: {
|
|
||||||
controller: 'dashboard/notifications/notification/index',
|
|
||||||
view: 'dashboard/notifications/notification/index.html'
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
path: 'notificationsettings.html',
|
|
||||||
pageProps: {
|
|
||||||
controller: 'dashboard/notifications/notifications/index',
|
|
||||||
view: 'dashboard/notifications/notifications/index.html'
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
path: 'playbackconfiguration.html',
|
path: 'playbackconfiguration.html',
|
||||||
pageProps: {
|
pageProps: {
|
||||||
|
|
|
@ -109,7 +109,7 @@ export function getCommands(options) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemHelper.supportsAddingToCollection(item) && options.EnableCollectionManagement) {
|
if (itemHelper.supportsAddingToCollection(item) && (user.Policy.IsAdministrator || user.Policy.EnableCollectionManagement)) {
|
||||||
commands.push({
|
commands.push({
|
||||||
name: globalize.translate('AddToCollection'),
|
name: globalize.translate('AddToCollection'),
|
||||||
id: 'addtocollection',
|
id: 'addtocollection',
|
||||||
|
|
|
@ -416,6 +416,8 @@ export function setContentType(parent, contentType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent.querySelector('.chkEnableLUFSScan').classList.toggle('hide', contentType !== 'music');
|
||||||
|
|
||||||
if (contentType === 'tvshows') {
|
if (contentType === 'tvshows') {
|
||||||
parent.querySelector('.chkEnableEmbeddedEpisodeInfosContainer').classList.remove('hide');
|
parent.querySelector('.chkEnableEmbeddedEpisodeInfosContainer').classList.remove('hide');
|
||||||
} else {
|
} else {
|
||||||
|
@ -512,6 +514,7 @@ export function getLibraryOptions(parent) {
|
||||||
EnableArchiveMediaFiles: false,
|
EnableArchiveMediaFiles: false,
|
||||||
EnablePhotos: parent.querySelector('.chkEnablePhotos').checked,
|
EnablePhotos: parent.querySelector('.chkEnablePhotos').checked,
|
||||||
EnableRealtimeMonitor: parent.querySelector('.chkEnableRealtimeMonitor').checked,
|
EnableRealtimeMonitor: parent.querySelector('.chkEnableRealtimeMonitor').checked,
|
||||||
|
EnableLUFSScan: parent.querySelector('.chkEnableLUFSScan').checked,
|
||||||
ExtractChapterImagesDuringLibraryScan: parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked,
|
ExtractChapterImagesDuringLibraryScan: parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked,
|
||||||
EnableChapterImageExtraction: parent.querySelector('.chkExtractChapterImages').checked,
|
EnableChapterImageExtraction: parent.querySelector('.chkExtractChapterImages').checked,
|
||||||
EnableInternetProviders: true,
|
EnableInternetProviders: true,
|
||||||
|
@ -573,6 +576,7 @@ export function setLibraryOptions(parent, options) {
|
||||||
parent.querySelector('#txtSeasonZeroName').value = options.SeasonZeroDisplayName || 'Specials';
|
parent.querySelector('#txtSeasonZeroName').value = options.SeasonZeroDisplayName || 'Specials';
|
||||||
parent.querySelector('.chkEnablePhotos').checked = options.EnablePhotos;
|
parent.querySelector('.chkEnablePhotos').checked = options.EnablePhotos;
|
||||||
parent.querySelector('.chkEnableRealtimeMonitor').checked = options.EnableRealtimeMonitor;
|
parent.querySelector('.chkEnableRealtimeMonitor').checked = options.EnableRealtimeMonitor;
|
||||||
|
parent.querySelector('.chkEnableLUFSScan').checked = options.EnableLUFSScan;
|
||||||
parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked = options.ExtractChapterImagesDuringLibraryScan;
|
parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked = options.ExtractChapterImagesDuringLibraryScan;
|
||||||
parent.querySelector('.chkExtractChapterImages').checked = options.EnableChapterImageExtraction;
|
parent.querySelector('.chkExtractChapterImages').checked = options.EnableChapterImageExtraction;
|
||||||
parent.querySelector('#chkSaveLocal').checked = options.SaveLocalMetadata;
|
parent.querySelector('#chkSaveLocal').checked = options.SaveLocalMetadata;
|
||||||
|
|
|
@ -55,6 +55,14 @@
|
||||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableRealtimeMonitorHelp}</div>
|
<div class="fieldDescription checkboxFieldDescription">${LabelEnableRealtimeMonitorHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="checkboxContainer checkboxContainer-withDescription advanced">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" is="emby-checkbox" class="chkEnableLUFSScan" checked />
|
||||||
|
<span>${LabelEnableLUFSScan}</span>
|
||||||
|
</label>
|
||||||
|
<div class="fieldDescription checkboxFieldDescription">${LabelEnableLUFSScanHelp}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription chkAutomaticallyAddToCollectionContainer hide advanced">
|
<div class="checkboxContainer checkboxContainer-withDescription chkAutomaticallyAddToCollectionContainer hide advanced">
|
||||||
<label>
|
<label>
|
||||||
<input is="emby-checkbox" type="checkbox" id="chkAutomaticallyAddToCollection" />
|
<input is="emby-checkbox" type="checkbox" id="chkAutomaticallyAddToCollection" />
|
||||||
|
|
38
src/components/playback/displayMirrorManager.ts
Normal file
38
src/components/playback/displayMirrorManager.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
||||||
|
|
||||||
|
import { playbackManager } from './playbackmanager';
|
||||||
|
|
||||||
|
interface PlaybackInfo {
|
||||||
|
item: BaseItemDto;
|
||||||
|
context?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mirrorItem(info: PlaybackInfo, player?: unknown) {
|
||||||
|
const { item } = info;
|
||||||
|
|
||||||
|
playbackManager.displayContent({
|
||||||
|
ItemName: item.Name,
|
||||||
|
ItemId: item.Id,
|
||||||
|
ItemType: item.Type,
|
||||||
|
Context: info.context
|
||||||
|
}, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mirrorIfEnabled(info: PlaybackInfo) {
|
||||||
|
if (info && playbackManager.enableDisplayMirroring()) {
|
||||||
|
const playerInfo = playbackManager.getPlayerInfo();
|
||||||
|
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,4 +1,3 @@
|
||||||
import appSettings from '../../scripts/settings/appSettings';
|
|
||||||
import Events from '../../utils/events.ts';
|
import Events from '../../utils/events.ts';
|
||||||
import browser from '../../scripts/browser';
|
import browser from '../../scripts/browser';
|
||||||
import loading from '../loading/loading';
|
import loading from '../loading/loading';
|
||||||
|
@ -13,32 +12,6 @@ import '../../elements/emby-button/emby-button';
|
||||||
import dialog from '../dialog/dialog';
|
import dialog from '../dialog/dialog';
|
||||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||||
|
|
||||||
function mirrorItem(info, player) {
|
|
||||||
const item = info.item;
|
|
||||||
|
|
||||||
playbackManager.displayContent({
|
|
||||||
|
|
||||||
ItemName: item.Name,
|
|
||||||
ItemId: item.Id,
|
|
||||||
ItemType: item.Type,
|
|
||||||
Context: info.context
|
|
||||||
}, player);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mirrorIfEnabled(info) {
|
|
||||||
if (info && playbackManager.enableDisplayMirroring()) {
|
|
||||||
const getPlayerInfo = playbackManager.getPlayerInfo();
|
|
||||||
|
|
||||||
if (getPlayerInfo && !getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
|
|
||||||
mirrorItem(info, playbackManager.getCurrentPlayer());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function emptyCallback() {
|
|
||||||
// avoid console logs about uncaught promises
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTargetSecondaryText(target) {
|
function getTargetSecondaryText(target) {
|
||||||
if (target.user) {
|
if (target.user) {
|
||||||
return target.user.Name;
|
return target.user.Name;
|
||||||
|
@ -140,10 +113,14 @@ export function show(button) {
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
playbackManager.trySetActivePlayer(target.playerName, target);
|
playbackManager.trySetActivePlayer(target.playerName, target);
|
||||||
|
}).catch(() => {
|
||||||
mirrorIfEnabled();
|
// action sheet closed
|
||||||
}, emptyCallback);
|
});
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('[playerSelectionMenu] failed to import action sheet', err);
|
||||||
});
|
});
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('[playerSelectionMenu] failed to get playback targets', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +157,8 @@ function disconnectFromPlayer(currentDeviceName) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
// dialog closed
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
playbackManager.setDefaultPlayerActive();
|
playbackManager.setDefaultPlayerActive();
|
||||||
|
@ -272,11 +251,13 @@ function showActivePlayerMenuInternal(playerInfo) {
|
||||||
|
|
||||||
dialogHelper.open(dlg).then(function () {
|
dialogHelper.open(dlg).then(function () {
|
||||||
if (destination === 'nowplaying') {
|
if (destination === 'nowplaying') {
|
||||||
appRouter.showNowPlaying();
|
return appRouter.showNowPlaying();
|
||||||
} else if (destination === 'disconnectFromPlayer') {
|
} else if (destination === 'disconnectFromPlayer') {
|
||||||
disconnectFromPlayer(currentDeviceName);
|
disconnectFromPlayer(currentDeviceName);
|
||||||
}
|
}
|
||||||
}, emptyCallback);
|
}).catch(() => {
|
||||||
|
// dialog closed
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMirrorChange() {
|
function onMirrorChange() {
|
||||||
|
@ -287,23 +268,6 @@ function onAutoCastChange() {
|
||||||
enable(this.checked);
|
enable(this.checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('viewshow', function (e) {
|
|
||||||
const state = e.detail.state || {};
|
|
||||||
const item = state.item;
|
|
||||||
|
|
||||||
if (item && item.ServerId) {
|
|
||||||
mirrorIfEnabled({
|
|
||||||
item: item
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Events.on(appSettings, 'change', function (e, name) {
|
|
||||||
if (name === 'displaymirror') {
|
|
||||||
mirrorIfEnabled();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Events.on(playbackManager, 'pairing', function () {
|
Events.on(playbackManager, 'pairing', function () {
|
||||||
loading.show();
|
loading.show();
|
||||||
});
|
});
|
||||||
|
|
|
@ -173,6 +173,7 @@ function loadForm(context, user, userSettings, apiClient) {
|
||||||
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
|
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
|
||||||
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
|
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
|
||||||
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
|
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
|
||||||
|
context.querySelector('.chkEnableAudioNormalization').checked = userSettings.enableAudioNormalization();
|
||||||
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
|
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
|
||||||
context.querySelector('.chkRememberAudioSelections').checked = user.Configuration.RememberAudioSelections || false;
|
context.querySelector('.chkRememberAudioSelections').checked = user.Configuration.RememberAudioSelections || false;
|
||||||
context.querySelector('.chkRememberSubtitleSelections').checked = user.Configuration.RememberSubtitleSelections || false;
|
context.querySelector('.chkRememberSubtitleSelections').checked = user.Configuration.RememberSubtitleSelections || false;
|
||||||
|
@ -217,7 +218,7 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
|
||||||
user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked;
|
user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked;
|
||||||
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
|
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
|
||||||
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
|
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
|
||||||
|
userSettingsInstance.enableAudioNormalization(context.querySelector('.chkEnableAudioNormalization').checked);
|
||||||
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
|
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
|
||||||
user.Configuration.RememberAudioSelections = context.querySelector('.chkRememberAudioSelections').checked;
|
user.Configuration.RememberAudioSelections = context.querySelector('.chkRememberAudioSelections').checked;
|
||||||
user.Configuration.RememberSubtitleSelections = context.querySelector('.chkRememberSubtitleSelections').checked;
|
user.Configuration.RememberSubtitleSelections = context.querySelector('.chkRememberSubtitleSelections').checked;
|
||||||
|
|
|
@ -72,6 +72,14 @@
|
||||||
${TabAdvanced}
|
${TabAdvanced}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" is="emby-checkbox" class="chkEnableAudioNormalization" />
|
||||||
|
<span>${EnableAudioNormalization}</span>
|
||||||
|
</label>
|
||||||
|
<div class="fieldDescription checkboxFieldDescription">${EnableAudioNormalizationHelp}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkPreferFmp4HlsContainer" />
|
<input type="checkbox" is="emby-checkbox" class="chkPreferFmp4HlsContainer" />
|
||||||
|
@ -102,7 +110,7 @@
|
||||||
</label>
|
</label>
|
||||||
<div class="fieldDescription checkboxFieldDescription">${RememberAudioSelectionsHelp}</div>
|
<div class="fieldDescription checkboxFieldDescription">${RememberAudioSelectionsHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkRememberSubtitleSelections" />
|
<input type="checkbox" is="emby-checkbox" class="chkRememberSubtitleSelections" />
|
||||||
|
|
|
@ -68,7 +68,6 @@ function loadPage(page) {
|
||||||
page.querySelector('#selectLanguage').value = config.PreferredMetadataLanguage || '';
|
page.querySelector('#selectLanguage').value = config.PreferredMetadataLanguage || '';
|
||||||
page.querySelector('#selectCountry').value = config.MetadataCountryCode || '';
|
page.querySelector('#selectCountry').value = config.MetadataCountryCode || '';
|
||||||
page.querySelector('#valDummyChapterDuration').value = config.DummyChapterDuration || '';
|
page.querySelector('#valDummyChapterDuration').value = config.DummyChapterDuration || '';
|
||||||
page.querySelector('#valDummyChapterCount').value = config.DummyChapterCount || '';
|
|
||||||
page.querySelector('#txtChapterImageResolution').value = config.ChapterImageResolution || '';
|
page.querySelector('#txtChapterImageResolution').value = config.ChapterImageResolution || '';
|
||||||
loading.hide();
|
loading.hide();
|
||||||
});
|
});
|
||||||
|
@ -81,7 +80,6 @@ function onSubmit() {
|
||||||
config.PreferredMetadataLanguage = form.querySelector('#selectLanguage').value;
|
config.PreferredMetadataLanguage = form.querySelector('#selectLanguage').value;
|
||||||
config.MetadataCountryCode = form.querySelector('#selectCountry').value;
|
config.MetadataCountryCode = form.querySelector('#selectCountry').value;
|
||||||
config.DummyChapterDuration = form.querySelector('#valDummyChapterDuration').value;
|
config.DummyChapterDuration = form.querySelector('#valDummyChapterDuration').value;
|
||||||
config.DummyChapterCount = form.querySelector('#valDummyChapterCount').value;
|
|
||||||
config.ChapterImageResolution = form.querySelector('#txtChapterImageResolution').value;
|
config.ChapterImageResolution = form.querySelector('#txtChapterImageResolution').value;
|
||||||
ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult);
|
ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult);
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,13 +22,9 @@
|
||||||
<div class="verticalSection">
|
<div class="verticalSection">
|
||||||
<h2>${HeaderDummyChapter}</h2>
|
<h2>${HeaderDummyChapter}</h2>
|
||||||
<div class="inputContainer">
|
<div class="inputContainer">
|
||||||
<input is="emby-input" type="number" id="valDummyChapterDuration" label="${LabelDummyChapterDuration}" min="1"></input>
|
<input is="emby-input" type="number" id="valDummyChapterDuration" label="${LabelDummyChapterDuration}" min="0"></input>
|
||||||
<div class="fieldDescription">${LabelDummyChapterDurationHelp}</div>
|
<div class="fieldDescription">${LabelDummyChapterDurationHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="inputContainer">
|
|
||||||
<input is="emby-input" type="number" id="valDummyChapterCount" label="${LabelDummyChapterCount}" min="0"></input>
|
|
||||||
<div class="fieldDescription">${LabelDummyChapterCountHelp}</div>
|
|
||||||
</div>
|
|
||||||
<div class="selectContainer">
|
<div class="selectContainer">
|
||||||
<select is="emby-select" id="txtChapterImageResolution" label="${LabelChapterImageResolution}"></select>
|
<select is="emby-select" id="txtChapterImageResolution" label="${LabelChapterImageResolution}"></select>
|
||||||
<div class="fieldDescription">
|
<div class="fieldDescription">
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
<div id="notificationSettingPage" data-role="page" class="page type-interior notificationConfigurationPage withTabs">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="content-primary">
|
|
||||||
|
|
||||||
<form class="notificationSettingForm">
|
|
||||||
|
|
||||||
<div class="verticalSection">
|
|
||||||
<div class="sectionTitleContainer flex align-items-center">
|
|
||||||
<h2 class="notificationType sectionTitle"></h2>
|
|
||||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/notifications">${Help}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label class="checkboxContainer">
|
|
||||||
<input type="checkbox" is="emby-checkbox" id="chkEnabled" />
|
|
||||||
<span>${LabelNotificationEnabled}</span>
|
|
||||||
</label>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div class="monitorUsers" style="display: none;">
|
|
||||||
<div class="paperListLabel">${LabelMonitorUsers}</div>
|
|
||||||
<div class="monitorUsersList">
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="selectContainer">
|
|
||||||
<select is="emby-select" id="selectUsers" label="${LabelSendNotificationToUsers}">
|
|
||||||
<option value="All">${OptionAllUsers}</option>
|
|
||||||
<option value="Admins">${OptionAdminUsers}</option>
|
|
||||||
<option value="Custom">${OptionCustomUsers}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="selectCustomUsers" style="display: none;">
|
|
||||||
<br />
|
|
||||||
<label>${LabelSelectUsers}</label>
|
|
||||||
<div class="sendToUsersList">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label>${LabelUseNotificationServices}</label>
|
|
||||||
<div class="servicesList">
|
|
||||||
</div>
|
|
||||||
<div class="fieldDescription">${AdditionalNotificationServices}</div>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<br />
|
|
||||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
|
||||||
<span>${Save}</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button is="emby-button" type="button" class="raised button-cancel block btnCancel" onclick="history.back();">
|
|
||||||
<span>${ButtonCancel}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,121 +0,0 @@
|
||||||
import escapeHtml from 'escape-html';
|
|
||||||
import 'jquery';
|
|
||||||
import '../../../../elements/emby-checkbox/emby-checkbox';
|
|
||||||
import Dashboard from '../../../../utils/dashboard';
|
|
||||||
import { getParameterByName } from '../../../../utils/url.ts';
|
|
||||||
|
|
||||||
function fillItems(elem, items, cssClass, idPrefix, currentList, isEnabledList) {
|
|
||||||
let html = '<div class="checkboxList paperList" style="padding: .5em 1em;">';
|
|
||||||
html += items.map(function (u) {
|
|
||||||
const isChecked = isEnabledList ? currentList.indexOf(u.Id) != -1 : currentList.indexOf(u.Id) == -1;
|
|
||||||
const checkedHtml = isChecked ? ' checked="checked"' : '';
|
|
||||||
return '<label><input is="emby-checkbox" class="' + cssClass + '" type="checkbox" data-itemid="' + u.Id + '"' + checkedHtml + '/><span>' + escapeHtml(u.Name) + '</span></label>';
|
|
||||||
}).join('');
|
|
||||||
html += '</div>';
|
|
||||||
elem.html(html).trigger('create');
|
|
||||||
}
|
|
||||||
|
|
||||||
function reload(page) {
|
|
||||||
const type = getParameterByName('type');
|
|
||||||
const promise1 = ApiClient.getUsers();
|
|
||||||
const promise2 = ApiClient.getNamedConfiguration(notificationsConfigurationKey);
|
|
||||||
const promise3 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types'));
|
|
||||||
const promise4 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Services'));
|
|
||||||
Promise.all([promise1, promise2, promise3, promise4]).then(function (responses) {
|
|
||||||
const users = responses[0];
|
|
||||||
const notificationOptions = responses[1];
|
|
||||||
const types = responses[2];
|
|
||||||
const services = responses[3];
|
|
||||||
let notificationConfig = notificationOptions.Options.filter(function (n) {
|
|
||||||
return n.Type == type;
|
|
||||||
})[0];
|
|
||||||
const typeInfo = types.filter(function (n) {
|
|
||||||
return n.Type == type;
|
|
||||||
})[0] || {};
|
|
||||||
|
|
||||||
if (typeInfo.IsBasedOnUserEvent) {
|
|
||||||
$('.monitorUsers', page).show();
|
|
||||||
} else {
|
|
||||||
$('.monitorUsers', page).hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.notificationType', page).html(escapeHtml(typeInfo.Name || '') || 'Unknown Notification');
|
|
||||||
|
|
||||||
if (!notificationConfig) {
|
|
||||||
notificationConfig = {
|
|
||||||
DisabledMonitorUsers: [],
|
|
||||||
SendToUsers: [],
|
|
||||||
DisabledServices: [],
|
|
||||||
SendToUserMode: 'Admins'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fillItems($('.monitorUsersList', page), users, 'chkMonitor', 'chkMonitor', notificationConfig.DisabledMonitorUsers);
|
|
||||||
fillItems($('.sendToUsersList', page), users, 'chkSendTo', 'chkSendTo', notificationConfig.SendToUsers, true);
|
|
||||||
fillItems($('.servicesList', page), services, 'chkService', 'chkService', notificationConfig.DisabledServices);
|
|
||||||
$('#chkEnabled', page).prop('checked', notificationConfig.Enabled || false);
|
|
||||||
$('#selectUsers', page).val(notificationConfig.SendToUserMode).trigger('change');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function save(page) {
|
|
||||||
const type = getParameterByName('type');
|
|
||||||
const promise1 = ApiClient.getNamedConfiguration(notificationsConfigurationKey);
|
|
||||||
// TODO: Check if this promise is really needed, as it's unused.
|
|
||||||
const promise2 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types'));
|
|
||||||
Promise.all([promise1, promise2]).then(function (responses) {
|
|
||||||
const notificationOptions = responses[0];
|
|
||||||
let notificationConfig = notificationOptions.Options.filter(function (n) {
|
|
||||||
return n.Type == type;
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
if (!notificationConfig) {
|
|
||||||
notificationConfig = {
|
|
||||||
Type: type
|
|
||||||
};
|
|
||||||
notificationOptions.Options.push(notificationConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationConfig.Enabled = $('#chkEnabled', page).is(':checked');
|
|
||||||
notificationConfig.SendToUserMode = $('#selectUsers', page).val();
|
|
||||||
notificationConfig.DisabledMonitorUsers = $('.chkMonitor', page).get().filter(function (c) {
|
|
||||||
return !c.checked;
|
|
||||||
}).map(function (c) {
|
|
||||||
return c.getAttribute('data-itemid');
|
|
||||||
});
|
|
||||||
notificationConfig.SendToUsers = $('.chkSendTo', page).get().filter(function (c) {
|
|
||||||
return c.checked;
|
|
||||||
}).map(function (c) {
|
|
||||||
return c.getAttribute('data-itemid');
|
|
||||||
});
|
|
||||||
notificationConfig.DisabledServices = $('.chkService', page).get().filter(function (c) {
|
|
||||||
return !c.checked;
|
|
||||||
}).map(function (c) {
|
|
||||||
return c.getAttribute('data-itemid');
|
|
||||||
});
|
|
||||||
ApiClient.updateNamedConfiguration(notificationsConfigurationKey, notificationOptions).then(function () {
|
|
||||||
Dashboard.processServerConfigurationUpdateResult();
|
|
||||||
Dashboard.navigate('notificationsettings.html');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSubmit() {
|
|
||||||
save($(this).parents('.page'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const notificationsConfigurationKey = 'notifications';
|
|
||||||
$(document).on('pageinit', '#notificationSettingPage', function () {
|
|
||||||
const page = this;
|
|
||||||
$('#selectUsers', page).on('change', function () {
|
|
||||||
if (this.value == 'Custom') {
|
|
||||||
$('.selectCustomUsers', page).show();
|
|
||||||
} else {
|
|
||||||
$('.selectCustomUsers', page).hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$('.notificationSettingForm').off('submit', onSubmit).on('submit', onSubmit);
|
|
||||||
}).on('pageshow', '#notificationSettingPage', function () {
|
|
||||||
reload(this);
|
|
||||||
});
|
|
|
@ -1,9 +0,0 @@
|
||||||
<div id="notificationSettingsPage" data-role="page" class="page type-interior notificationConfigurationPage">
|
|
||||||
<div>
|
|
||||||
<div class="content-primary">
|
|
||||||
<div class="readOnlyContent">
|
|
||||||
<div class="notificationList"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,61 +0,0 @@
|
||||||
import loading from '../../../../components/loading/loading';
|
|
||||||
import globalize from '../../../../scripts/globalize';
|
|
||||||
import '../../../../components/listview/listview.scss';
|
|
||||||
import '../../../../elements/emby-button/emby-button';
|
|
||||||
|
|
||||||
function reload(page) {
|
|
||||||
loading.show();
|
|
||||||
ApiClient.getJSON(ApiClient.getUrl('Notifications/Types')).then(function (list) {
|
|
||||||
let html = '';
|
|
||||||
let lastCategory = '';
|
|
||||||
let showHelp = true;
|
|
||||||
html += list.map(function (notification) {
|
|
||||||
let itemHtml = '';
|
|
||||||
if (notification.Category !== lastCategory) {
|
|
||||||
lastCategory = notification.Category;
|
|
||||||
if (lastCategory) {
|
|
||||||
itemHtml += '</div>';
|
|
||||||
itemHtml += '</div>';
|
|
||||||
}
|
|
||||||
itemHtml += '<div class="verticalSection verticalSection-extrabottompadding">';
|
|
||||||
itemHtml += '<div class="sectionTitleContainer" style="margin-bottom:1em;">';
|
|
||||||
itemHtml += '<h2 class="sectionTitle">';
|
|
||||||
itemHtml += notification.Category;
|
|
||||||
itemHtml += '</h2>';
|
|
||||||
if (showHelp) {
|
|
||||||
showHelp = false;
|
|
||||||
itemHtml += '<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/notifications">';
|
|
||||||
itemHtml += globalize.translate('Help');
|
|
||||||
itemHtml += '</a>';
|
|
||||||
}
|
|
||||||
itemHtml += '</div>';
|
|
||||||
itemHtml += '<div class="paperList">';
|
|
||||||
}
|
|
||||||
itemHtml += '<a class="listItem listItem-border" is="emby-linkbutton" data-ripple="false" href="notificationsetting.html?type=' + notification.Type + '">';
|
|
||||||
if (notification.Enabled) {
|
|
||||||
itemHtml += '<span class="listItemIcon material-icons notifications_active" aria-hidden="true"></span>';
|
|
||||||
} else {
|
|
||||||
itemHtml += '<span class="listItemIcon material-icons notifications_off" aria-hidden="true" style="background-color:#999;"></span>';
|
|
||||||
}
|
|
||||||
itemHtml += '<div class="listItemBody">';
|
|
||||||
itemHtml += '<div class="listItemBodyText">' + notification.Name + '</div>';
|
|
||||||
itemHtml += '</div>';
|
|
||||||
itemHtml += '<button type="button" is="paper-icon-button-light"><span class="material-icons mode_edit" aria-hidden="true"></span></button>';
|
|
||||||
itemHtml += '</a>';
|
|
||||||
return itemHtml;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
if (list.length) {
|
|
||||||
html += '</div>';
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
page.querySelector('.notificationList').innerHTML = html;
|
|
||||||
loading.hide();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (view) {
|
|
||||||
view.addEventListener('viewshow', function () {
|
|
||||||
reload(view);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
<div id="btnInstallDiv" class="hide">
|
<div id="btnInstallDiv" class="hide">
|
||||||
<button is="emby-button" type="submit" id="btnInstall" class="raised button-submit block">
|
<button is="emby-button" type="submit" id="btnInstall" class="raised button-submit block">
|
||||||
<span>${Install}</span>
|
<span>${HeaderInstall}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="fieldDescription">${ServerRestartNeededAfterPluginInstall}</div>
|
<div class="fieldDescription">${ServerRestartNeededAfterPluginInstall}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -86,7 +86,7 @@ export default function (view, params, tabContent, options) {
|
||||||
elem.addEventListener('click', onPreviousPageClick);
|
elem.addEventListener('click', onPreviousPageClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
tabContent.querySelector('.btnShuffle').classList.toggle('hide', result.TotalRecordCount < 1);
|
tabContent.querySelector('.btnShuffle')?.classList.toggle('hide', result.TotalRecordCount < 1);
|
||||||
|
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
loading.hide();
|
loading.hide();
|
||||||
|
@ -258,7 +258,7 @@ export default function (view, params, tabContent, options) {
|
||||||
itemsContainer.refreshItems();
|
itemsContainer.refreshItems();
|
||||||
});
|
});
|
||||||
|
|
||||||
tabElement.querySelector('.btnShuffle').addEventListener('click', shuffle);
|
tabElement.querySelector('.btnShuffle')?.addEventListener('click', shuffle);
|
||||||
};
|
};
|
||||||
|
|
||||||
let itemsContainer = tabContent.querySelector('.itemsContainer');
|
let itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||||
|
|
4
src/global.d.ts
vendored
4
src/global.d.ts
vendored
|
@ -6,4 +6,8 @@ export declare global {
|
||||||
Events: Events;
|
Events: Events;
|
||||||
NativeShell: any;
|
NativeShell: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DocumentEventMap {
|
||||||
|
'viewshow': CustomEvent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { appHost } from './components/apphost';
|
||||||
import { getPlugins } from './scripts/settings/webSettings';
|
import { getPlugins } from './scripts/settings/webSettings';
|
||||||
import { pluginManager } from './components/pluginManager';
|
import { pluginManager } from './components/pluginManager';
|
||||||
import packageManager from './components/packageManager';
|
import packageManager from './components/packageManager';
|
||||||
|
import './components/playback/displayMirrorManager.ts';
|
||||||
import { appRouter, history } from './components/router/appRouter';
|
import { appRouter, history } from './components/router/appRouter';
|
||||||
import './elements/emby-button/emby-button';
|
import './elements/emby-button/emby-button';
|
||||||
import './scripts/autoThemes';
|
import './scripts/autoThemes';
|
||||||
|
|
|
@ -101,6 +101,7 @@ class HtmlAudioPlayer {
|
||||||
self._currentTime = null;
|
self._currentTime = null;
|
||||||
|
|
||||||
const elem = createMediaElement();
|
const elem = createMediaElement();
|
||||||
|
|
||||||
return setCurrentSrc(elem, options);
|
return setCurrentSrc(elem, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,6 +111,17 @@ class HtmlAudioPlayer {
|
||||||
|
|
||||||
let val = options.url;
|
let val = options.url;
|
||||||
console.debug('playing url: ' + val);
|
console.debug('playing url: ' + val);
|
||||||
|
import('../../scripts/settings/userSettings').then((userSettings) => {
|
||||||
|
if (userSettings.enableAudioNormalization() && options.item.LUFS != null) {
|
||||||
|
const dbGain = -18 - options.item.LUFS;
|
||||||
|
self.gainNode.gain.value = Math.pow(10, (dbGain / 20));
|
||||||
|
} else {
|
||||||
|
self.gainNode.gain.value = 1;
|
||||||
|
}
|
||||||
|
console.debug('gain:' + self.gainNode.gain.value);
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error('Failed to add/change gainNode', err);
|
||||||
|
});
|
||||||
|
|
||||||
// Convert to seconds
|
// Convert to seconds
|
||||||
const seconds = (options.playerStartPositionTicks || 0) / 10000000;
|
const seconds = (options.playerStartPositionTicks || 0) / 10000000;
|
||||||
|
@ -245,9 +257,29 @@ class HtmlAudioPlayer {
|
||||||
|
|
||||||
self._mediaElement = elem;
|
self._mediaElement = elem;
|
||||||
|
|
||||||
|
addGainElement(elem);
|
||||||
|
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addGainElement(elem) {
|
||||||
|
try {
|
||||||
|
const AudioContext = window.AudioContext || window.webkitAudioContext; /* eslint-disable-line compat/compat */
|
||||||
|
|
||||||
|
const audioCtx = new AudioContext();
|
||||||
|
const source = audioCtx.createMediaElementSource(elem);
|
||||||
|
|
||||||
|
const gainNode = audioCtx.createGain();
|
||||||
|
|
||||||
|
source.connect(gainNode);
|
||||||
|
gainNode.connect(audioCtx.destination);
|
||||||
|
|
||||||
|
self.gainNode = gainNode;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Web Audio API is not supported in this browser', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onEnded() {
|
function onEnded() {
|
||||||
htmlMediaHelper.onEndedInternal(self, this, onError);
|
htmlMediaHelper.onEndedInternal(self, this, onError);
|
||||||
}
|
}
|
||||||
|
|
|
@ -516,10 +516,9 @@ function createToolsMenuList(pluginItems) {
|
||||||
icon: 'bug_report'
|
icon: 'bug_report'
|
||||||
});
|
});
|
||||||
links.push({
|
links.push({
|
||||||
name: globalize.translate('TabNotifications'),
|
name: globalize.translate('Notifications'),
|
||||||
icon: 'notifications',
|
icon: 'notifications',
|
||||||
href: '#/notificationsettings.html',
|
href: '#/notificationsettings.html'
|
||||||
pageIds: ['notificationSettingsPage', 'notificationSettingPage']
|
|
||||||
});
|
});
|
||||||
links.push({
|
links.push({
|
||||||
name: globalize.translate('TabPlugins'),
|
name: globalize.translate('TabPlugins'),
|
||||||
|
|
|
@ -156,6 +156,19 @@ export class UserSettings {
|
||||||
return toBoolean(this.get('enableCinemaMode', false), true);
|
return toBoolean(this.get('enableCinemaMode', false), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or set 'Enable Audio Normalization' state.
|
||||||
|
* @param {boolean|undefined} val - Flag to enable 'Enable Audio Normalization' or undefined.
|
||||||
|
* @return {boolean} 'Enable Audio Normalization' state.
|
||||||
|
*/
|
||||||
|
enableAudioNormalization(val) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
return this.set('enableAudioNormalization', val.toString(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toBoolean(this.get('enableAudioNormalization', false), true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or set 'Next Video Info Overlay' state.
|
* Get or set 'Next Video Info Overlay' state.
|
||||||
* @param {boolean|undefined} val - Flag to enable 'Next Video Info Overlay' or undefined.
|
* @param {boolean|undefined} val - Flag to enable 'Next Video Info Overlay' or undefined.
|
||||||
|
@ -592,6 +605,7 @@ export const serverConfig = currentSettings.serverConfig.bind(currentSettings);
|
||||||
export const allowedAudioChannels = currentSettings.allowedAudioChannels.bind(currentSettings);
|
export const allowedAudioChannels = currentSettings.allowedAudioChannels.bind(currentSettings);
|
||||||
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
|
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
|
||||||
export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings);
|
export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings);
|
||||||
|
export const enableAudioNormalization = currentSettings.enableAudioNormalization.bind(currentSettings);
|
||||||
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
||||||
export const enableVideoRemainingTime = currentSettings.enableVideoRemainingTime.bind(currentSettings);
|
export const enableVideoRemainingTime = currentSettings.enableVideoRemainingTime.bind(currentSettings);
|
||||||
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"AsManyAsPossible": "So veel moontlik",
|
"AsManyAsPossible": "So veel moontlik",
|
||||||
"Artists": "Kunstenare",
|
"Artists": "Kunstenare",
|
||||||
"Art": "Kuns",
|
"Art": "Kuns",
|
||||||
"AroundTime": "Ongeveer",
|
"AroundTime": "Ongeveer {0}",
|
||||||
"Anytime": "Enige tyd",
|
"Anytime": "Enige tyd",
|
||||||
"AnyLanguage": "Enige Taal",
|
"AnyLanguage": "Enige Taal",
|
||||||
"AlwaysPlaySubtitlesHelp": "Subtitels wat ooreenstem met taal voorkeur sal gelaai work ongeag oudio taal.",
|
"AlwaysPlaySubtitlesHelp": "Subtitels wat ooreenstem met taal voorkeur sal gelaai work ongeag oudio taal.",
|
||||||
|
@ -144,7 +144,7 @@
|
||||||
"ButtonWebsite": "Webtuiste",
|
"ButtonWebsite": "Webtuiste",
|
||||||
"ButtonUseQuickConnect": "Gebruik Vinnige Konneksie",
|
"ButtonUseQuickConnect": "Gebruik Vinnige Konneksie",
|
||||||
"ButtonUninstall": "Verwyder",
|
"ButtonUninstall": "Verwyder",
|
||||||
"ButtonTrailer": "Voorskou",
|
"ButtonTrailer": "Lokprent",
|
||||||
"ButtonTogglePlaylist": "Snitlys",
|
"ButtonTogglePlaylist": "Snitlys",
|
||||||
"ButtonSubmit": "Dien In",
|
"ButtonSubmit": "Dien In",
|
||||||
"ButtonSplit": "Verdeel",
|
"ButtonSplit": "Verdeel",
|
||||||
|
@ -229,10 +229,10 @@
|
||||||
"Ended": "Geëindig",
|
"Ended": "Geëindig",
|
||||||
"EnableDetailsBannerHelp": "Wys 'n banier-beeld bo aan die item besonderhede blad.",
|
"EnableDetailsBannerHelp": "Wys 'n banier-beeld bo aan die item besonderhede blad.",
|
||||||
"EnableDetailsBanner": "Besonderhede Banier",
|
"EnableDetailsBanner": "Besonderhede Banier",
|
||||||
"EnableThemeVideosHelp": "Speel tema-videos in die agtergrond terwyl deur die versameling geblaai word.",
|
"EnableThemeVideosHelp": "Speel temavideo's in die agtergrond terwyl jy deur die biblioteek blaai.",
|
||||||
"EnableThemeSongsHelp": "Speel tema-liedjies in die agtergrond terwyl daur die versameling geblaai word.",
|
"EnableThemeSongsHelp": "Speel die temaliedjies in die agtergrond terwyl jy deur die biblioteek blaai.",
|
||||||
"EnableStreamLoopingHelp": "Aktiveer hierdie as die lewendige stroom net 'n paar sekondes data bevat en gereeld versoek moet word. Om hierdie te aktiveer wanneer dit nie nodig is nie kan probleme veroorsaak.",
|
"EnableStreamLoopingHelp": "Aktiveer die opsie as die regstreekse strome slegs 'n paar sekondes se data bevat en voortdurend aangevra moet word. Om dit te aktiveer wanneer dit nie nodig is nie, kan probleme veroorsaak.",
|
||||||
"EnableStreamLooping": "Auto-lus lewendige strome",
|
"EnableStreamLooping": "Outo-lus regstreekse strome",
|
||||||
"EnableQuickConnect": "Aktiveer vinnige-inskakel op hierdie bediener",
|
"EnableQuickConnect": "Aktiveer vinnige-inskakel op hierdie bediener",
|
||||||
"EnablePhotosHelp": "Beelde sal opgetel en gewys word saam met ander media leêrs.",
|
"EnablePhotosHelp": "Beelde sal opgetel en gewys word saam met ander media leêrs.",
|
||||||
"EnablePhotos": "Wys fotos",
|
"EnablePhotos": "Wys fotos",
|
||||||
|
@ -508,7 +508,7 @@
|
||||||
"DashboardServerName": "Bediener: {0}",
|
"DashboardServerName": "Bediener: {0}",
|
||||||
"DashboardOperatingSystem": "Bedryfstelsel: {0}",
|
"DashboardOperatingSystem": "Bedryfstelsel: {0}",
|
||||||
"DashboardArchitecture": "Argitektuur: {0}",
|
"DashboardArchitecture": "Argitektuur: {0}",
|
||||||
"ColorPrimaries": "Kleur primêre",
|
"ColorPrimaries": "Primêre kleure",
|
||||||
"ClearQueue": "Maak waglys skoon",
|
"ClearQueue": "Maak waglys skoon",
|
||||||
"ButtonPlayer": "Speler",
|
"ButtonPlayer": "Speler",
|
||||||
"ButtonCast": "Plaas media",
|
"ButtonCast": "Plaas media",
|
||||||
|
@ -540,14 +540,14 @@
|
||||||
"HeaderAddUpdateSubtitle": "Voeg ondertitel by / werk dit op",
|
"HeaderAddUpdateSubtitle": "Voeg ondertitel by / werk dit op",
|
||||||
"Framerate": "Raamkoers",
|
"Framerate": "Raamkoers",
|
||||||
"Filter": "Filtreerder",
|
"Filter": "Filtreerder",
|
||||||
"EnableTonemapping": "Aktiveer toonkaarting",
|
"EnableTonemapping": "Aktiveer kleurtoon kartering",
|
||||||
"EnableFasterAnimationsHelp": "Gebruik vinniger animasies en oorgange.",
|
"EnableFasterAnimationsHelp": "Gebruik vinniger animasies en oorgange.",
|
||||||
"EnableFasterAnimations": "Vinniger animasies",
|
"EnableFasterAnimations": "Vinniger animasies",
|
||||||
"EnableBlurHashHelp": "Beelde wat nog gelaai word, sal met 'n unieke plekhouer vertoon word.",
|
"EnableBlurHashHelp": "Beelde wat nog gelaai word, sal met 'n unieke plekhouer vertoon word.",
|
||||||
"Depressed": "Depressief",
|
"Depressed": "Ingedrukte",
|
||||||
"Bwdif": "BWDIF",
|
"Bwdif": "BWDIF",
|
||||||
"ButtonSyncPlay": "Sinkroniseer Speler",
|
"ButtonSyncPlay": "Sinkroniseer Speler",
|
||||||
"AllowTonemappingHelp": "Toonkartering kan die dinamiese omvang van 'n video van HDR na SDR verander, terwyl beelddetails en kleure behoue bly, wat baie belangrike inligting is om die oorspronklike toneel voor te stel. Werk tans slegs wanneer video's gekodeer word met ingeboude HDR10- of HLG-metadata. As die afspeel nie glad is nie of misluk, oorweeg dit om die ooreenstemmende hardeware-dekodeerder uit te skakel.",
|
"AllowTonemappingHelp": "Die kartering van kleurtoon kan die dinamiese omvang van 'n video van HDR na SDR verander, terwyl die detail van die beeld en kleur behoue bly, wat baie belangrike inligting is om die oorspronklike toneel voor te stel. Werk tans slegs wanneer video's gekodeer word met ingeboude 10bit HDR10, HLG en DoVi-metadata. Die terugspeel benodig die ooreenkomstige OpenCL of CUDA looptyd.",
|
||||||
"LabelFolder": "Vouer",
|
"LabelFolder": "Vouer",
|
||||||
"LabelFinish": "Voltooi",
|
"LabelFinish": "Voltooi",
|
||||||
"LabelFileOrUrl": "Lêer of URL",
|
"LabelFileOrUrl": "Lêer of URL",
|
||||||
|
@ -812,5 +812,55 @@
|
||||||
"ButtonExitApp": "Verlaat Program",
|
"ButtonExitApp": "Verlaat Program",
|
||||||
"ButtonClose": "Maak toe",
|
"ButtonClose": "Maak toe",
|
||||||
"ButtonBackspace": "Terug",
|
"ButtonBackspace": "Terug",
|
||||||
"AddToFavorites": "Voeg by gunstelinge"
|
"AddToFavorites": "Voeg by gunstelinge",
|
||||||
|
"EnableAudioNormalizationHelp": "Oudionormalisering sal 'n konstante versterking byvoeg om die gemiddeld op die verlangde vlak te hou (-18dB).",
|
||||||
|
"EnableAudioNormalization": "Oudionormalisering",
|
||||||
|
"RefreshQueued": "Herlaai in waglys geplaas.",
|
||||||
|
"LabelTimeLimitHours": "Tydsbeperking (ure)",
|
||||||
|
"LabelMaxMuxingQueueSize": "Maksimum muxing waglys-grootte",
|
||||||
|
"LabelModelName": "Model naam",
|
||||||
|
"LabelPasswordRecoveryPinCode": "PIN kode",
|
||||||
|
"LabelSelectFolderGroups": "Groepeer die inhoud outomaties uit die volgende lêers na 'Flieks', 'Musiek' en 'TV'",
|
||||||
|
"LabelSyncPlaySettingsExtraTimeOffsetHelp": "Pas tydsverstelling verplasing aan (in ms) met geselekteerde toestel vir tydsinkronisering. Pas versigtig aan.",
|
||||||
|
"EnableCardLayout": "Wys kaartuitleg",
|
||||||
|
"LabelLoginDisclaimer": "Teken in vrywaring",
|
||||||
|
"LabelManufacturer": "Vervaardiger",
|
||||||
|
"LabelModelUrl": "Model-URL",
|
||||||
|
"None": "Geen",
|
||||||
|
"LabelMinAudiobookResumeHelp": "Titels word as ongespeel beskou as dit voor hierdie tyd gestop is.",
|
||||||
|
"DownloadAll": "Laai alles af",
|
||||||
|
"AllowCollectionManagement": "Laat hierdie gebruiker toe om versamelings te bestuur",
|
||||||
|
"EnableRewatchingNextUp": "Aktiveer kyk weer in Volgende",
|
||||||
|
"EnableRewatchingNextUpHelp": "Aktiveer die wys van reeds gekykte episodes in 'Volgende'-afdelings.",
|
||||||
|
"LabelMaxAudiobookResume": "Oudioboek oorblywende minute om te hervat",
|
||||||
|
"LabelModelNumber": "Model nommer",
|
||||||
|
"LabelAutomaticallyAddToCollectionHelp": "Wanneer ten minste 2 flieks dieselfde versamelingsnaam het, sal hulle outomaties by die versameling gevoeg word.",
|
||||||
|
"LabelSyncPlaySettingsMaxDelaySpeedToSyncHelp": "Maksimum terugspeelvertraging (in ms) waarna SkipToSync in plaas van SpeedToSync gebruik word.",
|
||||||
|
"LabelMaxDaysForNextUpHelp": "Stel die maksimum aantal dae wat 'n program in die 'Volgende'-lys moet bly sonder om dit te kyk.",
|
||||||
|
"LabelMaxDaysForNextUp": "Maksimum dae in 'Volgende'",
|
||||||
|
"LabelLogs": "Logs",
|
||||||
|
"LabelMaxChromecastBitrate": "Google Cast-stroomkwaliteit",
|
||||||
|
"LabelOpenclDeviceHelp": "Dit is die OpenCL-toestel wat vir kleurtoon kartering gebruik word. Die linkerkant van die kolletjie is die platformnommer, en die regterkant is die toestelnommer op die platform. Die verstekwaarde is 0.0. Die FFmpeg-toepassingslêer wat die OpenCL-hardewareversnellingsmetode bevat, word vereis.",
|
||||||
|
"LabelSlowResponseEnabled": "Teken 'n waarskuwingsboodskap aan as die bediener te traag was om te reageer",
|
||||||
|
"LabelSyncPlaySettingsSpeedToSyncDurationHelp": "Aantal millisekondes wat deur SpeedToSync gebruik word om afspeelposisie reg te stel.",
|
||||||
|
"MessageDownloadQueued": "Aflaai in waglys geplaas.",
|
||||||
|
"LabelLoginDisclaimerHelp": "'n Boodskap wat onderaan die aanmeldbladsy vertoon sal word.",
|
||||||
|
"LabelMatchType": "Pas tipe",
|
||||||
|
"LabelManufacturerUrl": "Vervaardiger-URL",
|
||||||
|
"LabelMaxAudiobookResumeHelp": "Titels word aanvaar as klaar gespeel as dit gestop word en die oorblywende tyd is minder as hierdie waarde.",
|
||||||
|
"LabelMaxBackdropsPerItem": "Maksimum aantal agtergronde per item",
|
||||||
|
"LabelMaxMuxingQueueSizeHelp": "Maksimum aantal pakkies wat opgehoop kan word terwyl daar gewag word vir alle strome om te inisialiseer. Probeer om dit te verhoog as jy steeds 'Too many packets buffered for output stream'-fout in FFmpeg-logboek sien. Die aanbevole waarde is 2048.",
|
||||||
|
"LabelMetadataSaversHelp": "Kies die lêerformate om te gebruik wanneer metadata gestoor word.",
|
||||||
|
"LabelModelDescription": "Model beskrywing",
|
||||||
|
"LabelMusicStreamingTranscodingBitrateHelp": "Spesifiseer die maksimum bissnelheid as musiek gestroom word.",
|
||||||
|
"LabelNumberOfGuideDaysHelp": "Die aflaai van meer dae se gidsdata bied die vermoë om verder vooruit te skeduleer en meer items te sien, maar dit sal ook langer neem om af te laai. Die outomatiese keuse kies op grond van die aantal kanale wat daar is.",
|
||||||
|
"LabelQuickConnectCode": "Vinnige verbinding kode",
|
||||||
|
"LabelRemoteClientBitrateLimit": "Internetstroom-bissnelheidlimiet (Mbps)",
|
||||||
|
"LabelSerialNumber": "Reeks nommer",
|
||||||
|
"LabelSlowResponseTime": "Tyd in ms waarna 'n reaksie as stadig beskou word",
|
||||||
|
"LabelSonyAggregationFlagsHelp": "Bepaal die inhoud van die 'aggregationFlags'-element in die 'urn:schemas-sonycom:av'-naamruimte.",
|
||||||
|
"LabelSyncPlayAccess": "SyncPlay toegang",
|
||||||
|
"LabelSyncPlaySettingsMinDelaySpeedToSyncHelp": "Minimum terugspeelvertraging (in ms) waarna SpeedToSync probeer om terugspeelposisie reg te stel.",
|
||||||
|
"LabelSyncPlaySettingsMinDelaySkipToSyncHelp": "Minimum terugspeelvertraging (in ms) waarna SkipToSync probeer om terugspeelposisie reg te stel.",
|
||||||
|
"LabelLockItemToPreventChanges": "Sluit hierdie item om toekomstige veranderinge te voorkom"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1710,5 +1710,14 @@
|
||||||
"LabelEnableAudioVbrHelp": "Пераменны бітрэйт забяспечвае лепшае суадносіны якасці да сярэдняга, але ў некаторых рэдкіх выпадках можа выклікаць праблемы з буферызацыяй і сумяшчальнасцю.",
|
"LabelEnableAudioVbrHelp": "Пераменны бітрэйт забяспечвае лепшае суадносіны якасці да сярэдняга, але ў некаторых рэдкіх выпадках можа выклікаць праблемы з буферызацыяй і сумяшчальнасцю.",
|
||||||
"TonemappingModeHelp": "Выберыце рэжым танальнага адлюстравання. Калі вы бачыце, што блікі блякнуць, паспрабуйце пераключыцца ў рэжым RGB.",
|
"TonemappingModeHelp": "Выберыце рэжым танальнага адлюстравання. Калі вы бачыце, што блікі блякнуць, паспрабуйце пераключыцца ў рэжым RGB.",
|
||||||
"LabelTonemappingMode": "Рэжым танальнага адлюстравання",
|
"LabelTonemappingMode": "Рэжым танальнага адлюстравання",
|
||||||
"Select": "Выбраць"
|
"Select": "Выбраць",
|
||||||
|
"EnableAudioNormalizationHelp": "Нармалізацыя гуку дадасць пастаяннае ўзмацненне, каб падтрымліваць сярэдняе значэнне на жаданым узроўні (-18 дБ).",
|
||||||
|
"EnableAudioNormalization": "Нармалізацыя гуку",
|
||||||
|
"LabelEnableLUFSScan": "Уключыць сканаванне LUFS",
|
||||||
|
"LabelEnableLUFSScanHelp": "Уключыце сканаванне LUFS на наяўнасць музыкі (гэта зойме больш часу і рэсурсаў).",
|
||||||
|
"Studio": "Студыя",
|
||||||
|
"MenuOpen": "Адкрыць меню",
|
||||||
|
"MenuClose": "Зачыніць меню",
|
||||||
|
"AllowCollectionManagement": "Дазволіць гэтаму карыстальніку кіраваць калекцыямі",
|
||||||
|
"UserMenu": "Меню карыстальніка"
|
||||||
}
|
}
|
||||||
|
|
|
@ -408,7 +408,7 @@
|
||||||
"LabelDownloadLanguages": "Stahované jazyky",
|
"LabelDownloadLanguages": "Stahované jazyky",
|
||||||
"LabelDropImageHere": "Sem přetáhněte obrázek nebo klikněte pro procházení.",
|
"LabelDropImageHere": "Sem přetáhněte obrázek nebo klikněte pro procházení.",
|
||||||
"LabelDropShadow": "Typ zvýraznění titulků",
|
"LabelDropShadow": "Typ zvýraznění titulků",
|
||||||
"LabelEasyPinCode": "Jednoduchý kód PIN",
|
"LabelEasyPinCode": "Jednoduchý kód PIN:",
|
||||||
"LabelEmbedAlbumArtDidl": "Vložit alba do DIDL",
|
"LabelEmbedAlbumArtDidl": "Vložit alba do DIDL",
|
||||||
"LabelEmbedAlbumArtDidlHelp": "Některá zařízení preferují tento způsob pro získání alba. Jiné mohou selhat pokud máte tuto volbu povolenu.",
|
"LabelEmbedAlbumArtDidlHelp": "Některá zařízení preferují tento způsob pro získání alba. Jiné mohou selhat pokud máte tuto volbu povolenu.",
|
||||||
"LabelEnableAutomaticPortMap": "Povolit automatické mapování portů",
|
"LabelEnableAutomaticPortMap": "Povolit automatické mapování portů",
|
||||||
|
@ -1092,7 +1092,7 @@
|
||||||
"LabelCustomCertificatePathHelp": "Umístění souboru PKCS #12, který obsahuje certifikát a soukromý klíč k povolení podpory TLS na vlastní doméně.",
|
"LabelCustomCertificatePathHelp": "Umístění souboru PKCS #12, který obsahuje certifikát a soukromý klíč k povolení podpory TLS na vlastní doméně.",
|
||||||
"LabelDateTimeLocale": "Místní nastavení data",
|
"LabelDateTimeLocale": "Místní nastavení data",
|
||||||
"LabelDefaultScreen": "Výchozí obrazovka",
|
"LabelDefaultScreen": "Výchozí obrazovka",
|
||||||
"LabelDynamicExternalId": "{0} ID",
|
"LabelDynamicExternalId": "{0} ID:",
|
||||||
"LabelEnableHardwareDecodingFor": "Povolit hardwarové dekódování pro",
|
"LabelEnableHardwareDecodingFor": "Povolit hardwarové dekódování pro",
|
||||||
"LabelHomeNetworkQuality": "Kvalita na domácí síti",
|
"LabelHomeNetworkQuality": "Kvalita na domácí síti",
|
||||||
"LabelInternetQuality": "Kvalita na internetu",
|
"LabelInternetQuality": "Kvalita na internetu",
|
||||||
|
@ -1695,12 +1695,12 @@
|
||||||
"SaveRecordingImages": "Uložit obrázky elektronického programového průvodce nahrávky",
|
"SaveRecordingImages": "Uložit obrázky elektronického programového průvodce nahrávky",
|
||||||
"SaveRecordingImagesHelp": "Uložit obrázky elektronického programového průvodce nahrávky spolu s médii.",
|
"SaveRecordingImagesHelp": "Uložit obrázky elektronického programového průvodce nahrávky spolu s médii.",
|
||||||
"HeaderDummyChapter": "Obrázky kapitol",
|
"HeaderDummyChapter": "Obrázky kapitol",
|
||||||
"LabelDummyChapterDuration": "Rozmezí",
|
"LabelDummyChapterDuration": "Rozmezí:",
|
||||||
"LabelDummyChapterDurationHelp": "Rozmezí v sekundách mezi extrakcí obrázků kapitol.",
|
"LabelDummyChapterDurationHelp": "Rozmezí v sekundách mezi kapitolami. Vytváření kapitol je možné vypnout nastavením na 0. Změna tohoto nastavení nemá vliv na existující kapitoly.",
|
||||||
"LabelDummyChapterCount": "Limit",
|
"LabelDummyChapterCount": "Limit",
|
||||||
"LabelDummyChapterCountHelp": "Maximální počet obrázků kapitol, který má být vytvořen pro jeden soubor médií.",
|
"LabelDummyChapterCountHelp": "Maximální počet obrázků kapitol, který má být vytvořen pro jeden soubor médií.",
|
||||||
"LabelChapterImageResolution": "Rozlišení",
|
"LabelChapterImageResolution": "Rozlišení:",
|
||||||
"LabelChapterImageResolutionHelp": "Rozližení extrahovaných obrázků kapitol.",
|
"LabelChapterImageResolutionHelp": "Rozlišení extrahovaných obrázků kapitol. Změna tohoto nastavení nemá vliv na existující kapitoly.",
|
||||||
"ResolutionMatchSource": "Stejné jako zdroj",
|
"ResolutionMatchSource": "Stejné jako zdroj",
|
||||||
"PreferEmbeddedExtrasTitlesOverFileNames": "Preferovat vložené názvy před názvy souborů pro doplňky",
|
"PreferEmbeddedExtrasTitlesOverFileNames": "Preferovat vložené názvy před názvy souborů pro doplňky",
|
||||||
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Doplňky většinou mají stejný vložený název jako nadřazená položka. Zaškrtnutím je i přesto můžete upřednostnit.",
|
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Doplňky většinou mají stejný vložený název jako nadřazená položka. Zaškrtnutím je i přesto můžete upřednostnit.",
|
||||||
|
@ -1729,5 +1729,9 @@
|
||||||
"MenuClose": "Zavřít nabídku",
|
"MenuClose": "Zavřít nabídku",
|
||||||
"UserMenu": "Uživatelská nabídka",
|
"UserMenu": "Uživatelská nabídka",
|
||||||
"Studio": "Studio",
|
"Studio": "Studio",
|
||||||
"AllowCollectionManagement": "Povolit tomuto uživateli spravovat kolekce"
|
"AllowCollectionManagement": "Povolit tomuto uživateli spravovat kolekce",
|
||||||
|
"EnableAudioNormalizationHelp": "Normalizace hlasitosti udržuje průměrnou hlasitost na požadované úrovni (-18 dB) přidáním konstantního zisku.",
|
||||||
|
"EnableAudioNormalization": "Normalizace hlasitosti",
|
||||||
|
"LabelEnableLUFSScan": "Povolit skenování LUFS",
|
||||||
|
"LabelEnableLUFSScanHelp": "Povolit tvorbu informací LUFS při skenování hudby. Prodlužuje skenování a je náročnější na výkon."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1727,5 +1727,11 @@
|
||||||
"TonemappingModeHelp": "Select the tone mapping mode. If you experience blown out highlights try switching to the RGB mode.",
|
"TonemappingModeHelp": "Select the tone mapping mode. If you experience blown out highlights try switching to the RGB mode.",
|
||||||
"UserMenu": "User Menu",
|
"UserMenu": "User Menu",
|
||||||
"MenuOpen": "Open Menu",
|
"MenuOpen": "Open Menu",
|
||||||
"MenuClose": "Close Menu"
|
"MenuClose": "Close Menu",
|
||||||
|
"Studio": "Studio",
|
||||||
|
"EnableAudioNormalizationHelp": "Audio normalisation will add a constant gain to keep the average at a desired level (-18dB).",
|
||||||
|
"EnableAudioNormalization": "Audio Normalisation",
|
||||||
|
"LabelEnableLUFSScan": "Enable LUFS scan",
|
||||||
|
"LabelEnableLUFSScanHelp": "Enable LUFS scan for music (This will take longer and more resources).",
|
||||||
|
"AllowCollectionManagement": "Allow this user to manage collections"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
"Actor": "Actor",
|
"Actor": "Actor",
|
||||||
"Add": "Add",
|
"Add": "Add",
|
||||||
"AddedOnValue": "Added {0}",
|
"AddedOnValue": "Added {0}",
|
||||||
"AdditionalNotificationServices": "Browse the plugin catalog to install additional notification services.",
|
|
||||||
"AddToCollection": "Add to collection",
|
"AddToCollection": "Add to collection",
|
||||||
"AddToFavorites": "Add to favorites",
|
"AddToFavorites": "Add to favorites",
|
||||||
"AddToPlaylist": "Add to playlist",
|
"AddToPlaylist": "Add to playlist",
|
||||||
|
@ -133,6 +132,7 @@
|
||||||
"ChannelNumber": "Channel number",
|
"ChannelNumber": "Channel number",
|
||||||
"Channels": "Channels",
|
"Channels": "Channels",
|
||||||
"CinemaModeConfigurationHelp": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.",
|
"CinemaModeConfigurationHelp": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.",
|
||||||
|
"EnableAudioNormalizationHelp": "Audio normalization will add a constant gain to keep the average at a desired level (-18dB).",
|
||||||
"ClearQueue": "Clear queue",
|
"ClearQueue": "Clear queue",
|
||||||
"ClientSettings": "Client Settings",
|
"ClientSettings": "Client Settings",
|
||||||
"Collections": "Collections",
|
"Collections": "Collections",
|
||||||
|
@ -216,11 +216,12 @@
|
||||||
"EditImages": "Edit images",
|
"EditImages": "Edit images",
|
||||||
"EditMetadata": "Edit metadata",
|
"EditMetadata": "Edit metadata",
|
||||||
"EditSubtitles": "Edit subtitles",
|
"EditSubtitles": "Edit subtitles",
|
||||||
"EnableAutoCast": "Set as Default",
|
"EnableAutoCast": "Set as default",
|
||||||
"EnableBackdropsHelp": "Display the backdrops in the background of some pages while browsing the library.",
|
"EnableBackdropsHelp": "Display the backdrops in the background of some pages while browsing the library.",
|
||||||
"EnableBlurHash": "Enable blurred placeholders for images",
|
"EnableBlurHash": "Enable blurred placeholders for images",
|
||||||
"EnableBlurHashHelp": "Images that are still being loaded will be displayed with a unique placeholder.",
|
"EnableBlurHashHelp": "Images that are still being loaded will be displayed with a unique placeholder.",
|
||||||
"EnableCinemaMode": "Cinema mode",
|
"EnableCinemaMode": "Cinema mode",
|
||||||
|
"EnableAudioNormalization": "Audio Normalization",
|
||||||
"EnableColorCodedBackgrounds": "Color coded backgrounds",
|
"EnableColorCodedBackgrounds": "Color coded backgrounds",
|
||||||
"EnableDecodingColorDepth10Hevc": "Enable 10-bit hardware decoding for HEVC",
|
"EnableDecodingColorDepth10Hevc": "Enable 10-bit hardware decoding for HEVC",
|
||||||
"EnableDecodingColorDepth10Vp9": "Enable 10-bit hardware decoding for VP9",
|
"EnableDecodingColorDepth10Vp9": "Enable 10-bit hardware decoding for VP9",
|
||||||
|
@ -291,6 +292,7 @@
|
||||||
"General": "General",
|
"General": "General",
|
||||||
"Genre": "Genre",
|
"Genre": "Genre",
|
||||||
"Genres": "Genres",
|
"Genres": "Genres",
|
||||||
|
"GetThePlugin": "Get the Plugin",
|
||||||
"GoogleCastUnsupported": "Google Cast Unsupported",
|
"GoogleCastUnsupported": "Google Cast Unsupported",
|
||||||
"GroupBySeries": "Group by series",
|
"GroupBySeries": "Group by series",
|
||||||
"GroupVersions": "Group versions",
|
"GroupVersions": "Group versions",
|
||||||
|
@ -629,11 +631,9 @@
|
||||||
"LabelDropShadow": "Drop shadow",
|
"LabelDropShadow": "Drop shadow",
|
||||||
"LabelDropSubtitleHere": "Drop subtitle here, or click to browse.",
|
"LabelDropSubtitleHere": "Drop subtitle here, or click to browse.",
|
||||||
"LabelDummyChapterDuration": "Interval",
|
"LabelDummyChapterDuration": "Interval",
|
||||||
"LabelDummyChapterDurationHelp": "The chapter image extraction interval in seconds.",
|
"LabelDummyChapterDurationHelp": "The interval between dummy chapters. Set to 0 to disable dummy chapter generation. Changing this will have no effect on existing dummy chapters.",
|
||||||
"LabelDummyChapterCount": "Limit",
|
|
||||||
"LabelDummyChapterCountHelp": "The maximum number of chapter images that will be extracted for each media file.",
|
|
||||||
"LabelChapterImageResolution": "Resolution",
|
"LabelChapterImageResolution": "Resolution",
|
||||||
"LabelChapterImageResolutionHelp": "The resolution of the extracted chapter images.",
|
"LabelChapterImageResolutionHelp": "The resolution of the extracted chapter images. Changing this will have no effect on existing dummy chapters.",
|
||||||
"LabelDynamicExternalId": "{0} Id",
|
"LabelDynamicExternalId": "{0} Id",
|
||||||
"LabelEasyPinCode": "Easy PIN code",
|
"LabelEasyPinCode": "Easy PIN code",
|
||||||
"LabelEmbedAlbumArtDidl": "Embed album art in DIDL",
|
"LabelEmbedAlbumArtDidl": "Embed album art in DIDL",
|
||||||
|
@ -659,6 +659,8 @@
|
||||||
"LabelEnableIP4Help": "Enable IPv4 functionality.",
|
"LabelEnableIP4Help": "Enable IPv4 functionality.",
|
||||||
"LabelEnableIP6": "Enable IPv6",
|
"LabelEnableIP6": "Enable IPv6",
|
||||||
"LabelEnableIP6Help": "Enable IPv6 functionality.",
|
"LabelEnableIP6Help": "Enable IPv6 functionality.",
|
||||||
|
"LabelEnableLUFSScan": "Enable LUFS scan",
|
||||||
|
"LabelEnableLUFSScanHelp": "Enable LUFS scan for music (This will take longer and more resources).",
|
||||||
"LabelEnableRealtimeMonitor": "Enable real time monitoring",
|
"LabelEnableRealtimeMonitor": "Enable real time monitoring",
|
||||||
"LabelEnableRealtimeMonitorHelp": "Changes to files will be processed immediately on supported file systems.",
|
"LabelEnableRealtimeMonitorHelp": "Changes to files will be processed immediately on supported file systems.",
|
||||||
"LabelEnableSingleImageInDidlLimit": "Limit to single embedded image",
|
"LabelEnableSingleImageInDidlLimit": "Limit to single embedded image",
|
||||||
|
@ -771,7 +773,6 @@
|
||||||
"LabelModelName": "Model name",
|
"LabelModelName": "Model name",
|
||||||
"LabelModelNumber": "Model number",
|
"LabelModelNumber": "Model number",
|
||||||
"LabelModelUrl": "Model URL",
|
"LabelModelUrl": "Model URL",
|
||||||
"LabelMonitorUsers": "Monitor activity from",
|
|
||||||
"LabelMovieCategories": "Movie categories",
|
"LabelMovieCategories": "Movie categories",
|
||||||
"LabelMoviePrefix": "Movie prefix",
|
"LabelMoviePrefix": "Movie prefix",
|
||||||
"LabelMoviePrefixHelp": "If a prefix is applied to movie titles, enter it here so the server can handle it properly.",
|
"LabelMoviePrefixHelp": "If a prefix is applied to movie titles, enter it here so the server can handle it properly.",
|
||||||
|
@ -783,7 +784,6 @@
|
||||||
"LabelNewPassword": "New password",
|
"LabelNewPassword": "New password",
|
||||||
"LabelNewPasswordConfirm": "New password confirm",
|
"LabelNewPasswordConfirm": "New password confirm",
|
||||||
"LabelNewsCategories": "News categories",
|
"LabelNewsCategories": "News categories",
|
||||||
"LabelNotificationEnabled": "Enable this notification",
|
|
||||||
"LabelNumber": "Number",
|
"LabelNumber": "Number",
|
||||||
"LabelNumberOfGuideDays": "Number of days of guide data to download",
|
"LabelNumberOfGuideDays": "Number of days of guide data to download",
|
||||||
"LabelNumberOfGuideDaysHelp": "Downloading more days worth of guide data provides the ability to schedule out further in advance and view more listings, but it will also take longer to download. Auto will pick based on the number of channels.",
|
"LabelNumberOfGuideDaysHelp": "Downloading more days worth of guide data provides the ability to schedule out further in advance and view more listings, but it will also take longer to download. Auto will pick based on the number of channels.",
|
||||||
|
@ -856,9 +856,7 @@
|
||||||
"LabelSeasonNumber": "Season number",
|
"LabelSeasonNumber": "Season number",
|
||||||
"LabelSelectFolderGroups": "Automatically group content from the following folders into views such as 'Movies', 'Music' and 'TV'",
|
"LabelSelectFolderGroups": "Automatically group content from the following folders into views such as 'Movies', 'Music' and 'TV'",
|
||||||
"LabelSelectFolderGroupsHelp": "Folders that are unchecked will be displayed by themselves in their own view.",
|
"LabelSelectFolderGroupsHelp": "Folders that are unchecked will be displayed by themselves in their own view.",
|
||||||
"LabelSelectUsers": "Select users",
|
|
||||||
"LabelSelectVersionToInstall": "Select version to install",
|
"LabelSelectVersionToInstall": "Select version to install",
|
||||||
"LabelSendNotificationToUsers": "Send the notification to",
|
|
||||||
"LabelSerialNumber": "Serial number",
|
"LabelSerialNumber": "Serial number",
|
||||||
"LabelSeriesRecordingPath": "Series recording path",
|
"LabelSeriesRecordingPath": "Series recording path",
|
||||||
"LabelServerHost": "Host",
|
"LabelServerHost": "Host",
|
||||||
|
@ -969,7 +967,6 @@
|
||||||
"LabelUDPPortRange": "UDP Communication Range",
|
"LabelUDPPortRange": "UDP Communication Range",
|
||||||
"LabelUDPPortRangeHelp": "Restrict Jellyfin to use this port range when making UDP connections. (Default is 1024 - 645535).<br/> Note: Certain function require fixed ports that may be outside of this range.",
|
"LabelUDPPortRangeHelp": "Restrict Jellyfin to use this port range when making UDP connections. (Default is 1024 - 645535).<br/> Note: Certain function require fixed ports that may be outside of this range.",
|
||||||
"LabelUnstable": "Unstable",
|
"LabelUnstable": "Unstable",
|
||||||
"LabelUseNotificationServices": "Use the following services",
|
|
||||||
"LabelUser": "User",
|
"LabelUser": "User",
|
||||||
"LabelUserAgent": "User agent",
|
"LabelUserAgent": "User agent",
|
||||||
"LabelUserLibrary": "User library",
|
"LabelUserLibrary": "User library",
|
||||||
|
@ -1173,6 +1170,8 @@
|
||||||
"Normal": "Normal",
|
"Normal": "Normal",
|
||||||
"NoSubtitleSearchResultsFound": "No results found.",
|
"NoSubtitleSearchResultsFound": "No results found.",
|
||||||
"NoSubtitlesHelp": "Subtitles will not be loaded by default. They can still be turned on manually during playback.",
|
"NoSubtitlesHelp": "Subtitles will not be loaded by default. They can still be turned on manually during playback.",
|
||||||
|
"Notifications": "Notifications",
|
||||||
|
"NotificationsMovedMessage": "The notifications functionality has moved to the Webhook plugin.",
|
||||||
"NumLocationsValue": "{0} folders",
|
"NumLocationsValue": "{0} folders",
|
||||||
"Off": "Off",
|
"Off": "Off",
|
||||||
"OnApplicationStartup": "On application startup",
|
"OnApplicationStartup": "On application startup",
|
||||||
|
@ -1182,7 +1181,6 @@
|
||||||
"OnlyImageFormats": "Only Image Formats (VobSub, PGS, SUB)",
|
"OnlyImageFormats": "Only Image Formats (VobSub, PGS, SUB)",
|
||||||
"OnWakeFromSleep": "On wake from sleep",
|
"OnWakeFromSleep": "On wake from sleep",
|
||||||
"Option3D": "3D",
|
"Option3D": "3D",
|
||||||
"OptionAdminUsers": "Administrators",
|
|
||||||
"OptionAllowAudioPlaybackTranscoding": "Allow audio playback that requires transcoding",
|
"OptionAllowAudioPlaybackTranscoding": "Allow audio playback that requires transcoding",
|
||||||
"OptionAllowBrowsingLiveTv": "Allow Live TV access",
|
"OptionAllowBrowsingLiveTv": "Allow Live TV access",
|
||||||
"OptionAllowContentDownload": "Allow media downloads",
|
"OptionAllowContentDownload": "Allow media downloads",
|
||||||
|
@ -1199,14 +1197,12 @@
|
||||||
"OptionAllowUserToManageServer": "Allow this user to manage the server",
|
"OptionAllowUserToManageServer": "Allow this user to manage the server",
|
||||||
"OptionAllowVideoPlaybackRemuxing": "Allow video playback that requires conversion without re-encoding",
|
"OptionAllowVideoPlaybackRemuxing": "Allow video playback that requires conversion without re-encoding",
|
||||||
"OptionAllowVideoPlaybackTranscoding": "Allow video playback that requires transcoding",
|
"OptionAllowVideoPlaybackTranscoding": "Allow video playback that requires transcoding",
|
||||||
"OptionAllUsers": "All users",
|
|
||||||
"OptionAutomaticallyGroupSeries": "Automatically merge series that are spread across multiple folders",
|
"OptionAutomaticallyGroupSeries": "Automatically merge series that are spread across multiple folders",
|
||||||
"OptionAutomaticallyGroupSeriesHelp": "Series that are spread across multiple folders within this library will be automatically merged into a single series.",
|
"OptionAutomaticallyGroupSeriesHelp": "Series that are spread across multiple folders within this library will be automatically merged into a single series.",
|
||||||
"OptionBluray": "BD",
|
"OptionBluray": "BD",
|
||||||
"OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)",
|
"OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)",
|
||||||
"OptionCommunityRating": "Community Rating",
|
"OptionCommunityRating": "Community Rating",
|
||||||
"OptionCriticRating": "Critics Rating",
|
"OptionCriticRating": "Critics Rating",
|
||||||
"OptionCustomUsers": "Custom",
|
|
||||||
"OptionDaily": "Daily",
|
"OptionDaily": "Daily",
|
||||||
"OptionDateAdded": "Date Added",
|
"OptionDateAdded": "Date Added",
|
||||||
"OptionDateEpisodeAdded": "Date Episode Added",
|
"OptionDateEpisodeAdded": "Date Episode Added",
|
||||||
|
@ -1505,7 +1501,6 @@
|
||||||
"TabNetworking": "Networking",
|
"TabNetworking": "Networking",
|
||||||
"TabNetworks": "TV Networks",
|
"TabNetworks": "TV Networks",
|
||||||
"TabNfoSettings": "NFO Settings",
|
"TabNfoSettings": "NFO Settings",
|
||||||
"TabNotifications": "Notifications",
|
|
||||||
"TabOther": "Other",
|
"TabOther": "Other",
|
||||||
"TabParentalControl": "Parental Control",
|
"TabParentalControl": "Parental Control",
|
||||||
"TabPlugins": "Plugins",
|
"TabPlugins": "Plugins",
|
||||||
|
|
|
@ -816,8 +816,8 @@
|
||||||
"LabelEnableBlastAliveMessages": "Lähetä hereilläolo -viesti",
|
"LabelEnableBlastAliveMessages": "Lähetä hereilläolo -viesti",
|
||||||
"LabelEnableBlastAliveMessagesHelp": "Ota tämä käyttöön, jos muilla verkon UPnP-laitteilla on ongelmia palvelimen havaitsemisessa.",
|
"LabelEnableBlastAliveMessagesHelp": "Ota tämä käyttöön, jos muilla verkon UPnP-laitteilla on ongelmia palvelimen havaitsemisessa.",
|
||||||
"LabelEnableDlnaClientDiscoveryInterval": "Päätelaitteen tunnistustiheys",
|
"LabelEnableDlnaClientDiscoveryInterval": "Päätelaitteen tunnistustiheys",
|
||||||
"LabelEasyPinCode": "Helppo PIN -koodi",
|
"LabelEasyPinCode": "Helppo PIN -koodi:",
|
||||||
"LabelDynamicExternalId": "{0} Id",
|
"LabelDynamicExternalId": "{0} Id:",
|
||||||
"LabelDisplayLanguageHelp": "Jellyfin-käännös on jatkuva projekti.",
|
"LabelDisplayLanguageHelp": "Jellyfin-käännös on jatkuva projekti.",
|
||||||
"LabelDisplayLanguage": "Näytön kieli",
|
"LabelDisplayLanguage": "Näytön kieli",
|
||||||
"LabelDiscNumber": "Levynumero",
|
"LabelDiscNumber": "Levynumero",
|
||||||
|
@ -1688,12 +1688,12 @@
|
||||||
"SaveRecordingImages": "Tallenna tallenteen ohjelmaoppaan kuvat",
|
"SaveRecordingImages": "Tallenna tallenteen ohjelmaoppaan kuvat",
|
||||||
"SaveRecordingImagesHelp": "Tallenna ohjelmaoppaan tietolähteen kuvat mediatiedoston oheen.",
|
"SaveRecordingImagesHelp": "Tallenna ohjelmaoppaan tietolähteen kuvat mediatiedoston oheen.",
|
||||||
"HeaderDummyChapter": "Kappalekuvat",
|
"HeaderDummyChapter": "Kappalekuvat",
|
||||||
"LabelDummyChapterDuration": "Aikaväli",
|
"LabelDummyChapterDuration": "Aikaväli:",
|
||||||
"LabelDummyChapterDurationHelp": "Kappalekuvien purkuväli sekunteina.",
|
"LabelDummyChapterDurationHelp": "Kappalekuvien purkuväli sekunteina. Poista kappalekuvien luonti käytöstä asettamalla arvoksi 0. Tämän muutos ei vaikuta olemassa oleviin kappaleisiin.",
|
||||||
"LabelDummyChapterCount": "Enimmäismäärä",
|
"LabelDummyChapterCount": "Enimmäismäärä",
|
||||||
"LabelDummyChapterCountHelp": "Yhdestä mediatiedostosta purettavien kappalekuvien enimmäismäärä.",
|
"LabelDummyChapterCountHelp": "Yhdestä mediatiedostosta purettavien kappalekuvien enimmäismäärä.",
|
||||||
"LabelChapterImageResolution": "Resoluutio",
|
"LabelChapterImageResolution": "Resoluutio:",
|
||||||
"LabelChapterImageResolutionHelp": "Purettujen kappalekuvien resoluutio.",
|
"LabelChapterImageResolutionHelp": "Purettujen kappalekuvien resoluutio. Tämän muutos ei vaikuta olemassa oleviin kappaleisiin.",
|
||||||
"ResolutionMatchSource": "Vastaa lähdettä",
|
"ResolutionMatchSource": "Vastaa lähdettä",
|
||||||
"PreferEmbeddedExtrasTitlesOverFileNames": "Suosi lisämateriaaleille upotettuja otsikoita tiedostonimien sijaan",
|
"PreferEmbeddedExtrasTitlesOverFileNames": "Suosi lisämateriaaleille upotettuja otsikoita tiedostonimien sijaan",
|
||||||
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Lisämateriaaleilla on usein sama otsikko kuin niiden isännällä. Valitse tämä käyttääksesi silti upotettuja otsikoita.",
|
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Lisämateriaaleilla on usein sama otsikko kuin niiden isännällä. Valitse tämä käyttääksesi silti upotettuja otsikoita.",
|
||||||
|
@ -1727,5 +1727,9 @@
|
||||||
"MenuClose": "Sulje valikko",
|
"MenuClose": "Sulje valikko",
|
||||||
"UserMenu": "Käyttäjä-valikko",
|
"UserMenu": "Käyttäjä-valikko",
|
||||||
"Studio": "Studio",
|
"Studio": "Studio",
|
||||||
"AllowCollectionManagement": "Salli tämän käyttäjän hallita kokoelmia"
|
"AllowCollectionManagement": "Salli tämän käyttäjän hallita kokoelmia",
|
||||||
|
"EnableAudioNormalizationHelp": "Äänen normalisointi asettaa äänelle kiinteän vahvistuksen keskivoimakkuuden vakiotasolla (-18 dB).",
|
||||||
|
"EnableAudioNormalization": "Äänen normalisointi",
|
||||||
|
"LabelEnableLUFSScan": "Suorita LUFS-tarkistus",
|
||||||
|
"LabelEnableLUFSScanHelp": "Käytä musiikin LUFS-tarkistusta (tämä vaatii enemmän aikaa ja resurseja)."
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
"AlwaysPlaySubtitlesHelp": "Les sous-titres correspondant à la langue préférée seront chargés indépendamment de la langue de l'audio.",
|
"AlwaysPlaySubtitlesHelp": "Les sous-titres correspondant à la langue préférée seront chargés indépendamment de la langue de l'audio.",
|
||||||
"AnyLanguage": "N'importe quel langage",
|
"AnyLanguage": "N'importe quel langage",
|
||||||
"Anytime": "N'importe quand",
|
"Anytime": "N'importe quand",
|
||||||
"AroundTime": "Aux environs de",
|
"AroundTime": "Aux environs de {0}",
|
||||||
"Artists": "Artistes",
|
"Artists": "Artistes",
|
||||||
"AsManyAsPossible": "Autant que possible",
|
"AsManyAsPossible": "Autant que possible",
|
||||||
"Ascending": "Croissant",
|
"Ascending": "Croissant",
|
||||||
|
@ -1694,10 +1694,10 @@
|
||||||
"HeaderRecordingMetadataSaving": "Enregistrement des métadonnées",
|
"HeaderRecordingMetadataSaving": "Enregistrement des métadonnées",
|
||||||
"SaveRecordingNFO": "Enregistrer les métadonnées du fournisseur dans le fichier NFO",
|
"SaveRecordingNFO": "Enregistrer les métadonnées du fournisseur dans le fichier NFO",
|
||||||
"SaveRecordingImages": "Enregistrer les images des métadonnées du fournisseur",
|
"SaveRecordingImages": "Enregistrer les images des métadonnées du fournisseur",
|
||||||
"LabelDummyChapterDurationHelp": "Intervalle d’extraction des images de chapitre en secondes.",
|
"LabelDummyChapterDurationHelp": "Intervalle de temps entre chapitres factices. La valeur 0 désactive la génération des chapitres. Un changement de valeur n’affectera pas les chapitres existants.",
|
||||||
"LabelDummyChapterCount": "Limite",
|
"LabelDummyChapterCount": "Limite",
|
||||||
"LabelChapterImageResolution": "Résolution",
|
"LabelChapterImageResolution": "Résolution",
|
||||||
"LabelChapterImageResolutionHelp": "La résolution des images de chapitre.",
|
"LabelChapterImageResolutionHelp": "La résolution des images de chapitre. Changer cette valeur n’aura pas d'effet sur les chapitres factices existants.",
|
||||||
"ResolutionMatchSource": "Résolution de la source",
|
"ResolutionMatchSource": "Résolution de la source",
|
||||||
"HeaderDummyChapter": "Images des chapitres",
|
"HeaderDummyChapter": "Images des chapitres",
|
||||||
"LabelDummyChapterDuration": "Intervalle",
|
"LabelDummyChapterDuration": "Intervalle",
|
||||||
|
@ -1729,5 +1729,9 @@
|
||||||
"MenuClose": "Fermer le menu",
|
"MenuClose": "Fermer le menu",
|
||||||
"UserMenu": "Menu utilisateur",
|
"UserMenu": "Menu utilisateur",
|
||||||
"Studio": "Studio",
|
"Studio": "Studio",
|
||||||
"AllowCollectionManagement": "Autoriser cet utilisateur à gérer les collections"
|
"AllowCollectionManagement": "Autoriser cet utilisateur à gérer les collections",
|
||||||
|
"EnableAudioNormalizationHelp": "La normalisation audio ajoutera un gain constant pour maintenir la moyenne au niveau souhaité (-18dB).",
|
||||||
|
"EnableAudioNormalization": "Normalisation audio",
|
||||||
|
"LabelEnableLUFSScan": "Activer l’analyse LUFS",
|
||||||
|
"LabelEnableLUFSScanHelp": "Activer l’analyse LUFS pour la musique (cela prendra plus de temps et de ressources)."
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"AddToPlaylist": "Pridėti į grojaraštį",
|
"AddToPlaylist": "Pridėti į grojaraštį",
|
||||||
"AdditionalNotificationServices": "Naršykite įskiepių katalogą ir įsidiekite papildomų pranešimų paslaugų.",
|
"AdditionalNotificationServices": "Naršykite įskiepių katalogą ir įsidiekite papildomų pranešimų paslaugų.",
|
||||||
"AllChannels": "Visi kanalai",
|
"AllChannels": "Visi kanalai",
|
||||||
"AllEpisodes": "Visas serijas",
|
"AllEpisodes": "Visos serijos",
|
||||||
"Anytime": "Bet kada",
|
"Anytime": "Bet kada",
|
||||||
"AroundTime": "Maždaug {0}",
|
"AroundTime": "Maždaug {0}",
|
||||||
"AsManyAsPossible": "Kiek įmanoma",
|
"AsManyAsPossible": "Kiek įmanoma",
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
"CancelRecording": "Atšaukti įrašymą",
|
"CancelRecording": "Atšaukti įrašymą",
|
||||||
"CancelSeries": "Atšaukti laidą",
|
"CancelSeries": "Atšaukti laidą",
|
||||||
"Categories": "Kategorijos",
|
"Categories": "Kategorijos",
|
||||||
"ChannelAccessHelp": "Pasirinkite kanalus, kuriuos norite dalintis su šiuo vartotoju. Administratoriai galės redaguoti visus kanalus per metaduomenų valdymą.",
|
"ChannelAccessHelp": "Pasirinkite kanalus, kuriuos norite dalintis su šiuo vartotoju. Administratoriai galės redaguoti visus kanalus naudojantis metaduomenų valdymą.",
|
||||||
"ChannelNameOnly": "Kanalas tik {0}",
|
"ChannelNameOnly": "Kanalas tik {0}",
|
||||||
"ChannelNumber": "Kanalo numeris",
|
"ChannelNumber": "Kanalo numeris",
|
||||||
"Composer": "Kompozitorius",
|
"Composer": "Kompozitorius",
|
||||||
|
@ -72,14 +72,14 @@
|
||||||
"HeaderAddToPlaylist": "Pridėti į grojaraštį",
|
"HeaderAddToPlaylist": "Pridėti į grojaraštį",
|
||||||
"HeaderAdditionalParts": "Papildomos dalys",
|
"HeaderAdditionalParts": "Papildomos dalys",
|
||||||
"HeaderCancelRecording": "Atšaukti įrašymą",
|
"HeaderCancelRecording": "Atšaukti įrašymą",
|
||||||
"HeaderCancelSeries": "Atšaukti laidą",
|
"HeaderCancelSeries": "Atšaukti serialą",
|
||||||
"HeaderContinueWatching": "Žiūrėti toliau",
|
"HeaderContinueWatching": "Žiūrėti toliau",
|
||||||
"HeaderCustomDlnaProfiles": "Kiti profiliai",
|
"HeaderCustomDlnaProfiles": "Kiti profiliai",
|
||||||
"HeaderDeleteItem": "Ištrinti elementą",
|
"HeaderDeleteItem": "Ištrinti elementą",
|
||||||
"HeaderDeleteItems": "Ištrinti elementus",
|
"HeaderDeleteItems": "Ištrinti elementus",
|
||||||
"HeaderDeviceAccess": "Įrenginio prieiga",
|
"HeaderDeviceAccess": "Įrenginio prieiga",
|
||||||
"HeaderEasyPinCode": "Lengvas Pin kodas",
|
"HeaderEasyPinCode": "Lengvas Pin kodas",
|
||||||
"HeaderEditImages": "Redaguoti paveikslus",
|
"HeaderEditImages": "Redaguoti atvaizdus",
|
||||||
"HeaderEnabledFields": "Įjungti laukeliai",
|
"HeaderEnabledFields": "Įjungti laukeliai",
|
||||||
"HeaderEnabledFieldsHelp": "Nuimkite varnelę nuo lauko kad jį užrakinti ir neleisti keisti jo duomenų.",
|
"HeaderEnabledFieldsHelp": "Nuimkite varnelę nuo lauko kad jį užrakinti ir neleisti keisti jo duomenų.",
|
||||||
"HeaderFeatureAccess": "Prieiga prie funkcijų",
|
"HeaderFeatureAccess": "Prieiga prie funkcijų",
|
||||||
|
@ -329,9 +329,9 @@
|
||||||
"OptionWeekly": "Savaitinis",
|
"OptionWeekly": "Savaitinis",
|
||||||
"OriginalAirDateValue": "Pirmo eterio data: {0}",
|
"OriginalAirDateValue": "Pirmo eterio data: {0}",
|
||||||
"Overview": "Apžvalga",
|
"Overview": "Apžvalga",
|
||||||
"PackageInstallCancelled": "{0} įdiegimas atšauktas.",
|
"PackageInstallCancelled": "{0} (versija {1}) įdiegimas atšauktas.",
|
||||||
"PackageInstallCompleted": "{0} įdiegimas baigtas.",
|
"PackageInstallCompleted": "{0} (versija {1}) įdiegimas baigtas.",
|
||||||
"PackageInstallFailed": "{0} įdiegimas nepavyko.",
|
"PackageInstallFailed": "{0} (versija {1}) įdiegimas nepavyko.",
|
||||||
"ParentalRating": "Tėvų reitingas",
|
"ParentalRating": "Tėvų reitingas",
|
||||||
"People": "Žmonės",
|
"People": "Žmonės",
|
||||||
"PlaceFavoriteChannelsAtBeginning": "Mėgstamiausius kanalus į pradžią",
|
"PlaceFavoriteChannelsAtBeginning": "Mėgstamiausius kanalus į pradžią",
|
||||||
|
@ -840,7 +840,7 @@
|
||||||
"ButtonSplit": "Skirstyti",
|
"ButtonSplit": "Skirstyti",
|
||||||
"AskAdminToCreateLibrary": "Paprašykite administratoriaus sukurti biblioteką.",
|
"AskAdminToCreateLibrary": "Paprašykite administratoriaus sukurti biblioteką.",
|
||||||
"Album": "Albumas",
|
"Album": "Albumas",
|
||||||
"ButtonSyncPlay": "SyncPlay",
|
"ButtonSyncPlay": "\"SyncPlay\"",
|
||||||
"MusicVideos": "Muzikiniai vaizdo įrašai",
|
"MusicVideos": "Muzikiniai vaizdo įrašai",
|
||||||
"TagsValue": "Žymės: {0}",
|
"TagsValue": "Žymės: {0}",
|
||||||
"AddToFavorites": "Pridėti į mėgstamiausius",
|
"AddToFavorites": "Pridėti į mėgstamiausius",
|
||||||
|
@ -978,7 +978,7 @@
|
||||||
"ButtonSpace": "Tarpas",
|
"ButtonSpace": "Tarpas",
|
||||||
"ButtonActivate": "Aktyvuoti",
|
"ButtonActivate": "Aktyvuoti",
|
||||||
"AllowFfmpegThrottlingHelp": "Kai perkodavimas arba pakartotinis atkodavimas (Remux) yra pakankamai toli nuo dabartinės atkūrimo pozicijos, pristabdykite procesą, kad jis sunaudotų mažiau išteklių. Tai naudingiausia žiūrint be dažno persukimo. Išjunkite šią funkciją, jei kyla atkūrimo problemų.",
|
"AllowFfmpegThrottlingHelp": "Kai perkodavimas arba pakartotinis atkodavimas (Remux) yra pakankamai toli nuo dabartinės atkūrimo pozicijos, pristabdykite procesą, kad jis sunaudotų mažiau išteklių. Tai naudingiausia žiūrint be dažno persukimo. Išjunkite šią funkciją, jei kyla atkūrimo problemų.",
|
||||||
"AllowTonemappingHelp": "Tonų atvaizdavimas gali pakeisti vaizdo įrašo dinaminį diapazoną iš HDR į SDR, išlaikant vaizdo detales ir spalvas, kurios yra labai svarbi informacija, kad būtų galima atvaizduoti pradinę sceną. Šiuo metu veikia tik su HDR10 arba HLG vaizdo įrašais. Tam reikia atitinkamos \"OpenCL\" arba CUDA paleidimo programos.",
|
"AllowTonemappingHelp": "Tonų atvaizdavimas gali pakeisti vaizdo įrašo dinaminį diapazoną iš HDR į SDR, išlaikant vaizdo detales ir spalvas, kurios yra labai svarbi informacija, kad būtų galima atvaizduoti pradinę sceną. Šiuo metu veikia tik su 10 bitų HDR10, HLG ir DoVi vaizdo įrašais. Tam reikia atitinkamos \"OpenCL\" arba CUDA paleidimo programos.",
|
||||||
"Arranger": "Organizatorius",
|
"Arranger": "Organizatorius",
|
||||||
"EnableBlurHashHelp": "Vaizdai, kurie vis dar įkeliami, bus rodomi su unikalia rezervuota vieta.",
|
"EnableBlurHashHelp": "Vaizdai, kurie vis dar įkeliami, bus rodomi su unikalia rezervuota vieta.",
|
||||||
"EnableDetailsBannerHelp": "Rodyti reklamjuostės vaizdą elemento išsamios informacijos puslapio viršuje.",
|
"EnableDetailsBannerHelp": "Rodyti reklamjuostės vaizdą elemento išsamios informacijos puslapio viršuje.",
|
||||||
|
@ -1049,7 +1049,7 @@
|
||||||
"PinCodeResetConfirmation": "Ar tikrai norite iš naujo nustatyti \"Easy PIN\" kodą?",
|
"PinCodeResetConfirmation": "Ar tikrai norite iš naujo nustatyti \"Easy PIN\" kodą?",
|
||||||
"LabelScreensaver": "Ekrano užsklanda",
|
"LabelScreensaver": "Ekrano užsklanda",
|
||||||
"LabelAutoDiscoveryTracingHelp": "Kai ši funkcija įjungta, automatinio aptikimo prievadu gauti paketai bus registruojami.",
|
"LabelAutoDiscoveryTracingHelp": "Kai ši funkcija įjungta, automatinio aptikimo prievadu gauti paketai bus registruojami.",
|
||||||
"LabelBaseUrlHelp": "",
|
"LabelBaseUrlHelp": "Pridėti subdirektorija prie serverio adreso. Pavyzdžiui: <code>http://example.com/<b><baseurl></b></code>",
|
||||||
"LabelBindToLocalNetworkAddressHelp": "Pakeiskite vietinį HTTP serverio IP adresą. Jei paliekama tuščia, serveris bus susietas su visais prieinamais adresais. Keičiant šią reikšmę reikia iš naujo paleisti serverį.",
|
"LabelBindToLocalNetworkAddressHelp": "Pakeiskite vietinį HTTP serverio IP adresą. Jei paliekama tuščia, serveris bus susietas su visais prieinamais adresais. Keičiant šią reikšmę reikia iš naujo paleisti serverį.",
|
||||||
"LabelEnableHttpsHelp": "Klausykitės sukonfigūruotame HTTPS prievade. Taip pat turi būti pateiktas galiojantis sertifikatas, kad tai įsigaliotų.",
|
"LabelEnableHttpsHelp": "Klausykitės sukonfigūruotame HTTPS prievade. Taip pat turi būti pateiktas galiojantis sertifikatas, kad tai įsigaliotų.",
|
||||||
"LabelExtractChaptersDuringLibraryScanHelp": "Generuoti skyrių vaizdus, kai bibliotekos skenavimo metu importuojami vaizdo įrašai. Priešingu atveju jie bus išskirti atliekant suplanuotą skyrių vaizdų užduotį, todėl įprastas bibliotekos skenavimas bus baigtas greičiau.",
|
"LabelExtractChaptersDuringLibraryScanHelp": "Generuoti skyrių vaizdus, kai bibliotekos skenavimo metu importuojami vaizdo įrašai. Priešingu atveju jie bus išskirti atliekant suplanuotą skyrių vaizdų užduotį, todėl įprastas bibliotekos skenavimas bus baigtas greičiau.",
|
||||||
|
@ -1131,5 +1131,36 @@
|
||||||
"LabelDateAddedBehaviorHelp": "Jei yra metaduomenų reikšmė, ji visada bus naudojama prieš bet kurią iš šių parinkčių.",
|
"LabelDateAddedBehaviorHelp": "Jei yra metaduomenų reikšmė, ji visada bus naudojama prieš bet kurią iš šių parinkčių.",
|
||||||
"LabelDefaultUserHelp": "Nustatykite, kuri naudotojo biblioteka turėtų būti rodoma prijungtuose įrenginiuose. Tai galima pakeisti kiekvienam įrenginiui naudojant profilius.",
|
"LabelDefaultUserHelp": "Nustatykite, kuri naudotojo biblioteka turėtų būti rodoma prijungtuose įrenginiuose. Tai galima pakeisti kiekvienam įrenginiui naudojant profilius.",
|
||||||
"LabelDeinterlaceMethod": "Pertempimo panaikinimo metodas",
|
"LabelDeinterlaceMethod": "Pertempimo panaikinimo metodas",
|
||||||
"LabelDownMixAudioScaleHelp": "Jei reikšmė lygi vienetui, bus išsaugotas originalus garsumas."
|
"LabelDownMixAudioScaleHelp": "Jei reikšmė lygi vienetui, bus išsaugotas originalus garsumas.",
|
||||||
|
"LabelLibraryPageSize": "Mediatekos puslapio dydis",
|
||||||
|
"LabelKodiMetadataSaveImagePaths": "Išsaugoti atvaizdų nuorodas NFO failuose",
|
||||||
|
"EnableAudioNormalization": "Garso normalizacija",
|
||||||
|
"LabelEnableAudioVbr": "Įjungti VBR garso kodavimą",
|
||||||
|
"HeaderPerformance": "Našumas",
|
||||||
|
"AllowCollectionManagement": "Leisti šiam vartotojui redaguoti kolekcijas",
|
||||||
|
"EnableAudioNormalizationHelp": "Garso normalizavimas pridės stabilų daugiklį kad palaikyti vidurkį ties norimu lygiu (-18dB).",
|
||||||
|
"LabelKnownProxies": "Žinomi \"proxy\" serveriai",
|
||||||
|
"LabelKidsCategories": "Kategorijos vaikams",
|
||||||
|
"LabelKodiMetadataUser": "Išsaugoti vartotojo peržiūrų informaciją NFO failuose",
|
||||||
|
"LabelLanNetworks": "LAN tinklai",
|
||||||
|
"LabelMaxDaysForNextUp": "Max dienų sekančių eilėje",
|
||||||
|
"LabelLineup": "Rikiuotė",
|
||||||
|
"LabelLoginDisclaimer": "Prisijungimo išsižadėjimas",
|
||||||
|
"LabelMaxAudiobookResume": "Audioknygos likęs pratęsimo laikas",
|
||||||
|
"LabelMaxMuxingQueueSize": "Maksimalus multipleksavimo eilės dydis",
|
||||||
|
"LabelLibraryPageSizeHelp": "Nustatyti rodomų elementų kiekį bibliotekos puslapyje. Įrašius 0 bus išjungti puslapiai.",
|
||||||
|
"LabelLoginDisclaimerHelp": "Žinutė kuri bus rodoma prisijungimo puslapio apačioje.",
|
||||||
|
"LabelKodiMetadataEnablePathSubstitution": "Įjungti kelio pakeitimą",
|
||||||
|
"LabelKodiMetadataUserHelp": "Išsaugoti peržiūrų informaciją NFO failuose, kad kitos aplikacijos galėtų naudotis.",
|
||||||
|
"LabelKodiMetadataSaveImagePathsHelp": "Tai rekomenduojama jei nuotraukų failų pavadinimai neatitinka Kodi rekomenduojamų nuostatų.",
|
||||||
|
"LabelMaxAudiobookResumeHelp": "Turinys skaitysis pilnai peržiūrėtas, jei bus sustabdytas kai likęs laikas bus mažesnis už šią vertę.",
|
||||||
|
"LabelMaxDaysForNextUpHelp": "Nustatyti maksimalų dienų kiekį kiek TV Laida liks sekančių eilėje jos neperžiūrėjus.",
|
||||||
|
"LabelIsForced": "Priverstas",
|
||||||
|
"LabelEnableLUFSScan": "Įjungti LUFS skanavimą",
|
||||||
|
"LabelEnableLUFSScanHelp": "Įjungti LUFS skanavimą muzikai (Užtruks ilgiau ir naudos daugiau resursų).",
|
||||||
|
"LabelImportOnlyFavoriteChannels": "Apriboti kanalus pažymėtus kaip mėgstamiausi",
|
||||||
|
"LabelMaxVideoResolution": "Maksimali leistina video transkodavimo resoliucija",
|
||||||
|
"LabelEnableAudioVbrHelp": "Kintama bitų sparta siūlo geresnę kokybę lyginant su vidutine bitų sparta, bet retais atvejais gali sukelti krovimo ir palaikymo problemas.",
|
||||||
|
"LabelKodiMetadataEnablePathSubstitutionHelp": "Įjungti kelio pakeitimą nuotraukoms naudojant serverio kelio pakeitimo nustatymus.",
|
||||||
|
"LabelKodiMetadataDateFormatHelp": "Visos datos iš NFO failų bus ištraukiamos šiuo formatu."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1351,5 +1351,37 @@
|
||||||
"LabelParallelImageEncodingLimit": "Paralēlas attēlu kodēšanas limits",
|
"LabelParallelImageEncodingLimit": "Paralēlas attēlu kodēšanas limits",
|
||||||
"LabelParallelImageEncodingLimitHelp": "Maksimālais daudzums ar attēlu kodēšanas procesiem, kas drīkst darboties vienlaicīgi. Iestatot šo uz 0, limits automātiski tiks izvēlēts balstoties uz jūsu sistēmas specifikācijām.",
|
"LabelParallelImageEncodingLimitHelp": "Maksimālais daudzums ar attēlu kodēšanas procesiem, kas drīkst darboties vienlaicīgi. Iestatot šo uz 0, limits automātiski tiks izvēlēts balstoties uz jūsu sistēmas specifikācijām.",
|
||||||
"HeaderDummyChapter": "Nodaļu Attēli",
|
"HeaderDummyChapter": "Nodaļu Attēli",
|
||||||
"EnableCardLayout": "Padarīt redzamu CardBox"
|
"EnableCardLayout": "Padarīt redzamu CardBox",
|
||||||
|
"MessageConfirmDeleteGuideProvider": "Vai esat pārliecināti, ka vēlaties izdzēst šo ceļveža pakalpojumu sniedzēju?",
|
||||||
|
"MessageForgotPasswordInNetworkRequired": "Lūdzu, mēģiniet vēlreiz savā mājas tīklā, lai uzsāktu paroles atiestatīšanas procesu.",
|
||||||
|
"MessageNoGenresAvailable": "Ļaujiet dažiem metadatu sniedzējiem iegūt žanrus no interneta.",
|
||||||
|
"MessagePluginInstallError": "Instalējot spraudni, notika kļūda.",
|
||||||
|
"MessageGetInstalledPluginsError": "Instalēto spraudņu saraksta iegūšanas laikā notika kļūda.",
|
||||||
|
"MessageNoNextUpItems": "Neviens netika atrasts. Sāciet skatīties savus raidījumus!",
|
||||||
|
"MessageNoTrailersFound": "Instalējiet treileru kanālu, lai uzlabotu savu filmu pieredzi, pievienojot interneta treileru bibliotēku.",
|
||||||
|
"EnableRewatchingNextUp": "Atkārtotas skatīšanas iespējošana sadaļā Nākamais",
|
||||||
|
"EnableAudioNormalizationHelp": "Audio normalizācija pievienos konstantu skaļuma pastiprinājumu, lai saglabātu vidējo skaļumu vēlamajā līmenī (-18dB).",
|
||||||
|
"EnableAudioNormalization": "Audio Normalizācija",
|
||||||
|
"LabelEnableLUFSScan": "Iespējot LUFS skenēšanu",
|
||||||
|
"LabelEnableLUFSScanHelp": "Iespējot LUFS skenēšanu mūzikai (Tas aizņems vairāk laika un resursu).",
|
||||||
|
"MessageNoItemsAvailable": "Pašlaik nav pieejams neviens vienums.",
|
||||||
|
"MessageNoCollectionsAvailable": "Kolekcijas ļauj izmantot personalizētas filmu, seriālu un albumu grupas. Noklikšķiniet uz pogas \"+\", lai sāktu veidot kolekcijas.",
|
||||||
|
"MessageNoRepositories": "Nav repozitoriju.",
|
||||||
|
"MessageReenableUser": "Skatiet zemāk, lai atkārtoti aktivizētu",
|
||||||
|
"MessageSyncPlayCreateGroupDenied": "Nepieciešama atļauja, lai izveidotu grupu.",
|
||||||
|
"MessageSyncPlayErrorAccessingGroups": "Piekļūstot grupu sarakstam, notika kļūda.",
|
||||||
|
"MessageSyncPlayErrorMissingSession": "Neizdevās iespējot SyncPlay! Trūkst sesija.",
|
||||||
|
"MessageRenameMediaFolder": "Pārdēvējot multivides bibliotēku, visi metadati tiks zaudēti, tāpēc rīkojieties piesardzīgi.",
|
||||||
|
"MenuOpen": "Atvērt Izvēlni",
|
||||||
|
"MenuClose": "Aizvērt Izvēlni",
|
||||||
|
"MessageNoFavoritesAvailable": "Pašlaik nav pieejami nekādi favorīti.",
|
||||||
|
"MessageImageTypeNotSelected": "Lūdzu, izvēlaties attēla veidu no nolaižamās izvēlnes.",
|
||||||
|
"MessageLeaveEmptyToInherit": "Atstājiet tukšu, lai mantotu iestatījumus no vecākā elementa vai globālās noklusējuma vērtības.",
|
||||||
|
"MessagePluginInstallDisclaimer": "Kopienas dalībnieku izveidoti spraudņi ir lielisks veids, kā uzlabot savu pieredzi, izmantojot papildu funkcijas un priekšrocības. Pirms instalēšanas ņemiet vērā to iespējamo ietekmi uz jūsu serveri, piemēram, ilgāku bibliotēkas skenēšanu, papildu fona apstrādi un sistēmas stabilitātes samazināšanos.",
|
||||||
|
"MessagePasswordResetForUsers": "Šādu lietotāju paroles ir atiestatītas. Tagad viņi var pierakstīties, izmantojot Easy PIN kodus, kas tika izmantoti atiestatīšanai.",
|
||||||
|
"MessagePluginInstalled": "Spraudnis tika veiksmīgi instalēts. Lai izmaiņas stātos spēkā, serveris ir jārestartē.",
|
||||||
|
"HeaderRecordingMetadataSaving": "Metadatu Ierakstīšana",
|
||||||
|
"AllowCollectionManagement": "Ļaut konkrētajam lietotājam pārvaldīt kolekciju",
|
||||||
|
"MediaInfoRefFrames": "Atskaites kadri",
|
||||||
|
"MessageDeleteTaskTrigger": "Vai esat pārliecināti, ka vēlaties izdzēst šo uzdevuma trigeri?"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1729,5 +1729,9 @@
|
||||||
"MenuClose": "Zamknij Menu",
|
"MenuClose": "Zamknij Menu",
|
||||||
"UserMenu": "Menu Użytkownika",
|
"UserMenu": "Menu Użytkownika",
|
||||||
"AllowCollectionManagement": "Zezwól temu użytkownikowi na zarządzanie kolekcjami",
|
"AllowCollectionManagement": "Zezwól temu użytkownikowi na zarządzanie kolekcjami",
|
||||||
"Studio": "Wytwórnia"
|
"Studio": "Wytwórnia",
|
||||||
|
"EnableAudioNormalizationHelp": "Normalizacja dźwięku doda stałe wzmocnienie, aby utrzymać średnią na pożądanym poziomie (-18dB).",
|
||||||
|
"EnableAudioNormalization": "Normalizacja dźwięku",
|
||||||
|
"LabelEnableLUFSScan": "Włącz skanowanie głośności dźwięku",
|
||||||
|
"LabelEnableLUFSScanHelp": "Włącz skanowanie głośności dźwięku dla muzyki (To zajmie więcej czasu i więcej zasobów)."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1706,5 +1706,22 @@
|
||||||
"ResolutionMatchSource": "Соответствовать источнику",
|
"ResolutionMatchSource": "Соответствовать источнику",
|
||||||
"MenuOpen": "Открыть Меню",
|
"MenuOpen": "Открыть Меню",
|
||||||
"MenuClose": "Закрыть Меню",
|
"MenuClose": "Закрыть Меню",
|
||||||
"AllowCollectionManagement": "Разрешить этому пользователю управлять настройками коллекций"
|
"AllowCollectionManagement": "Разрешить этому пользователю управлять настройками коллекций",
|
||||||
|
"EnableAudioNormalization": "Нормализация звука",
|
||||||
|
"SubtitleGreen": "Зелёный",
|
||||||
|
"Featurette": "Короткометражка",
|
||||||
|
"Short": "Короткометражка",
|
||||||
|
"Studio": "Студия",
|
||||||
|
"SubtitleGray": "Серый",
|
||||||
|
"SubtitleBlack": "Чёрный",
|
||||||
|
"SubtitleCyan": "Циан",
|
||||||
|
"SubtitleWhite": "Белый",
|
||||||
|
"SubtitleRed": "Красный",
|
||||||
|
"SubtitleYellow": "Жёлтый",
|
||||||
|
"Select": "Выбрать",
|
||||||
|
"SubtitleBlue": "Синий",
|
||||||
|
"SecondarySubtitles": "Дополнительные субтитры",
|
||||||
|
"SubtitleLightGray": "Светло-серый",
|
||||||
|
"UserMenu": "Меню пользователя",
|
||||||
|
"SubtitleMagenta": "Пурпурный"
|
||||||
}
|
}
|
||||||
|
|
|
@ -840,8 +840,8 @@
|
||||||
"LabelEmbedAlbumArtDidlHelp": "Деякі пристрої віддають перевагу цьому методу для отримання обкладинки альбому. Інші можуть не грати з увімкненою опцією.",
|
"LabelEmbedAlbumArtDidlHelp": "Деякі пристрої віддають перевагу цьому методу для отримання обкладинки альбому. Інші можуть не грати з увімкненою опцією.",
|
||||||
"LabelEnableAutomaticPortMap": "Увімкніть автоматичне зіставлення портів",
|
"LabelEnableAutomaticPortMap": "Увімкніть автоматичне зіставлення портів",
|
||||||
"LabelEmbedAlbumArtDidl": "Вставити обкладинку альбому в DIDL",
|
"LabelEmbedAlbumArtDidl": "Вставити обкладинку альбому в DIDL",
|
||||||
"LabelEasyPinCode": "Простий PIN код",
|
"LabelEasyPinCode": "Простий PIN код:",
|
||||||
"LabelDynamicExternalId": "{0} Ідентифікатор",
|
"LabelDynamicExternalId": "{0} Ідентифікатор:",
|
||||||
"LabelDropSubtitleHere": "Перенесіть сюди субтитри або натисніть, щоб переглянути.",
|
"LabelDropSubtitleHere": "Перенесіть сюди субтитри або натисніть, щоб переглянути.",
|
||||||
"LabelDropShadow": "Накладення тіні",
|
"LabelDropShadow": "Накладення тіні",
|
||||||
"LabelDroppedFrames": "Втрачені кадри",
|
"LabelDroppedFrames": "Втрачені кадри",
|
||||||
|
@ -1686,7 +1686,7 @@
|
||||||
"Experimental": "Експериментальний",
|
"Experimental": "Експериментальний",
|
||||||
"LabelStereoDownmixAlgorithm": "Stereo Downmix алгоритм",
|
"LabelStereoDownmixAlgorithm": "Stereo Downmix алгоритм",
|
||||||
"StereoDownmixAlgorithmHelp": "Алгоритм мікшування багатоканального аудіо у стерео.",
|
"StereoDownmixAlgorithmHelp": "Алгоритм мікшування багатоканального аудіо у стерео.",
|
||||||
"LabelChapterImageResolutionHelp": "Роздільна здатність витягнутих зображень розділу.",
|
"LabelChapterImageResolutionHelp": "Роздільна здатність створених зображень розділів. Зміна цього параметра не вплине на існуючі зображення розділів.",
|
||||||
"PreferEmbeddedExtrasTitlesOverFileNames": "Віддавайте перевагу вбудованим назвам, а не назвам файлів для додаткових функцій",
|
"PreferEmbeddedExtrasTitlesOverFileNames": "Віддавайте перевагу вбудованим назвам, а не назвам файлів для додаткових функцій",
|
||||||
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Додатки часто мають таке ж вбудоване ім’я, що й батьківське, позначте це, щоб усе одно використовувати для них вбудовані заголовки.",
|
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Додатки часто мають таке ж вбудоване ім’я, що й батьківське, позначте це, щоб усе одно використовувати для них вбудовані заголовки.",
|
||||||
"ResolutionMatchSource": "Джерело відповідності",
|
"ResolutionMatchSource": "Джерело відповідності",
|
||||||
|
@ -1695,11 +1695,11 @@
|
||||||
"SaveRecordingImages": "Зберегти записані зображення EPG",
|
"SaveRecordingImages": "Зберегти записані зображення EPG",
|
||||||
"SaveRecordingImagesHelp": "Зберігайте зображення з постачальника списків EPG разом із носієм.",
|
"SaveRecordingImagesHelp": "Зберігайте зображення з постачальника списків EPG разом із носієм.",
|
||||||
"HeaderDummyChapter": "Зображення розділів",
|
"HeaderDummyChapter": "Зображення розділів",
|
||||||
"LabelDummyChapterDuration": "Інтервал",
|
"LabelDummyChapterDuration": "Інтервал:",
|
||||||
"LabelDummyChapterDurationHelp": "Інтервал вилучення зображення розділу в секундах.",
|
"LabelDummyChapterDurationHelp": "Інтервал між зображеннями розділів. Встановіть значення 0, щоб вимкнути генерацію зображень розділів. Зміна цього параметра не вплине на існуючі зображення розділів.",
|
||||||
"LabelDummyChapterCount": "Ліміт",
|
"LabelDummyChapterCount": "Ліміт",
|
||||||
"LabelDummyChapterCountHelp": "Максимальна кількість зображень розділів, які буде видобуто для кожного медіафайлу.",
|
"LabelDummyChapterCountHelp": "Максимальна кількість зображень розділів, які буде видобуто для кожного медіафайлу.",
|
||||||
"LabelChapterImageResolution": "Роздільна здатність",
|
"LabelChapterImageResolution": "Роздільна здатність:",
|
||||||
"HeaderRecordingMetadataSaving": "Метадані запису",
|
"HeaderRecordingMetadataSaving": "Метадані запису",
|
||||||
"SubtitleBlack": "Чорний",
|
"SubtitleBlack": "Чорний",
|
||||||
"SubtitleBlue": "Синій",
|
"SubtitleBlue": "Синій",
|
||||||
|
@ -1726,5 +1726,9 @@
|
||||||
"MenuClose": "Закрити меню",
|
"MenuClose": "Закрити меню",
|
||||||
"UserMenu": "Меню користувача",
|
"UserMenu": "Меню користувача",
|
||||||
"Studio": "Студія",
|
"Studio": "Студія",
|
||||||
"AllowCollectionManagement": "Дозволити цьому користувачеві керувати колекціями"
|
"AllowCollectionManagement": "Дозволити цьому користувачеві керувати колекціями",
|
||||||
|
"EnableAudioNormalizationHelp": "Нормалізація звуку додасть постійний коефіцієнт підсилення, щоб утримати середній рівень на потрібному рівні (-18 дБ).",
|
||||||
|
"LabelEnableLUFSScanHelp": "Увімкніть сканування LUFS для пошуку музики (це займе більше часу і ресурсів).",
|
||||||
|
"EnableAudioNormalization": "Нормалізація звуку",
|
||||||
|
"LabelEnableLUFSScan": "Увімкнути сканування LUFS"
|
||||||
}
|
}
|
||||||
|
|
|
@ -458,7 +458,7 @@
|
||||||
"LabelDropImageHere": "拖拽或点击选择图像于此处。",
|
"LabelDropImageHere": "拖拽或点击选择图像于此处。",
|
||||||
"LabelDroppedFrames": "丢弃的帧",
|
"LabelDroppedFrames": "丢弃的帧",
|
||||||
"LabelDropShadow": "阴影",
|
"LabelDropShadow": "阴影",
|
||||||
"LabelDynamicExternalId": "{0} Id",
|
"LabelDynamicExternalId": "{0} Id:",
|
||||||
"LabelEasyPinCode": "简单 PIN 码",
|
"LabelEasyPinCode": "简单 PIN 码",
|
||||||
"LabelEmbedAlbumArtDidl": "在DIDL中嵌入专辑封面",
|
"LabelEmbedAlbumArtDidl": "在DIDL中嵌入专辑封面",
|
||||||
"LabelEmbedAlbumArtDidlHelp": "有些设备首选这种方式获取专辑封面。启用该选项可能导致其他设备播放失败。",
|
"LabelEmbedAlbumArtDidlHelp": "有些设备首选这种方式获取专辑封面。启用该选项可能导致其他设备播放失败。",
|
||||||
|
@ -1690,8 +1690,8 @@
|
||||||
"LabelStereoDownmixAlgorithm": "立体声降混算法",
|
"LabelStereoDownmixAlgorithm": "立体声降混算法",
|
||||||
"StereoDownmixAlgorithmHelp": "用于将多声道下降混合为立体声的算法。",
|
"StereoDownmixAlgorithmHelp": "用于将多声道下降混合为立体声的算法。",
|
||||||
"HeaderRecordingMetadataSaving": "记录元数据",
|
"HeaderRecordingMetadataSaving": "记录元数据",
|
||||||
"LabelDummyChapterDuration": "间隔",
|
"LabelDummyChapterDuration": "间隔:",
|
||||||
"LabelDummyChapterDurationHelp": "章节图像提取间隔,以秒为单位。",
|
"LabelDummyChapterDurationHelp": "虚拟章节之间的时间间隔。将其设置为0以禁用虚拟章节生成。更改此设置不会影响现有的虚拟章节。",
|
||||||
"PreferEmbeddedExtrasTitlesOverFileNames": "对额外内容优先使用内置的标题而不是文件名",
|
"PreferEmbeddedExtrasTitlesOverFileNames": "对额外内容优先使用内置的标题而不是文件名",
|
||||||
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "额外内容通常与父项有相同的内置标题,勾选此项来对它们使用内置标题。",
|
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "额外内容通常与父项有相同的内置标题,勾选此项来对它们使用内置标题。",
|
||||||
"SaveRecordingNFO": "在 NFO 中保存录制的 EPG 元数据",
|
"SaveRecordingNFO": "在 NFO 中保存录制的 EPG 元数据",
|
||||||
|
@ -1702,8 +1702,8 @@
|
||||||
"HeaderDummyChapter": "章节图片",
|
"HeaderDummyChapter": "章节图片",
|
||||||
"LabelDummyChapterCount": "限制",
|
"LabelDummyChapterCount": "限制",
|
||||||
"LabelDummyChapterCountHelp": "每个媒体文件的最大章节图像提取数。",
|
"LabelDummyChapterCountHelp": "每个媒体文件的最大章节图像提取数。",
|
||||||
"LabelChapterImageResolution": "分辨率",
|
"LabelChapterImageResolution": "分辨率:",
|
||||||
"LabelChapterImageResolutionHelp": "提取的章节图像的分辨率。",
|
"LabelChapterImageResolutionHelp": "提取的章节图像的分辨率。更改此设置不会影响现有的虚拟章节。",
|
||||||
"SecondarySubtitles": "次字幕",
|
"SecondarySubtitles": "次字幕",
|
||||||
"SubtitleBlack": "黑色",
|
"SubtitleBlack": "黑色",
|
||||||
"SubtitleGray": "灰色",
|
"SubtitleGray": "灰色",
|
||||||
|
@ -1729,5 +1729,9 @@
|
||||||
"MenuClose": "关闭菜单",
|
"MenuClose": "关闭菜单",
|
||||||
"UserMenu": "用户菜单",
|
"UserMenu": "用户菜单",
|
||||||
"Studio": "广播",
|
"Studio": "广播",
|
||||||
"AllowCollectionManagement": "允许该用户管理收藏夹"
|
"AllowCollectionManagement": "允许该用户管理收藏夹",
|
||||||
|
"EnableAudioNormalizationHelp": "音频标准化将添加一个恒定的增益,以保持平均音量在所需的级别(-18dB)。",
|
||||||
|
"EnableAudioNormalization": "音频标准化",
|
||||||
|
"LabelEnableLUFSScan": "启用 LUFS 扫描",
|
||||||
|
"LabelEnableLUFSScanHelp": "启用音乐的LUFS扫描(这将需要更长时间和更多资源)。"
|
||||||
}
|
}
|
||||||
|
|
11
src/types/playTarget.ts
Normal file
11
src/types/playTarget.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto';
|
||||||
|
|
||||||
|
export interface PlayTarget {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
appName?: string
|
||||||
|
playerName?: string
|
||||||
|
deviceType?: string
|
||||||
|
isLocalPlayer?: boolean
|
||||||
|
user?: UserDto
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue