mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'master' into patch-2
This commit is contained in:
commit
2dbbaeb028
99 changed files with 1396 additions and 1133 deletions
|
@ -77,8 +77,9 @@ Jellyfin Web is the frontend used for most of the clients available for end user
|
|||
.
|
||||
└── src
|
||||
├── apps
|
||||
│ ├── experimental # New experimental app layout
|
||||
│ └── stable # Classic (stable) app layout
|
||||
│ ├── dashboard # Admin dashboard app layout and routes
|
||||
│ ├── experimental # New experimental app layout and routes
|
||||
│ └── stable # Classic (stable) app layout and routes
|
||||
├── assets # Static assets
|
||||
├── components # Higher order visual components and React components
|
||||
├── controllers # Legacy page views and controllers 🧹
|
||||
|
@ -87,7 +88,6 @@ Jellyfin Web is the frontend used for most of the clients available for end user
|
|||
├── legacy # Polyfills for legacy browsers
|
||||
├── libraries # Third party libraries 🧹
|
||||
├── plugins # Client plugins
|
||||
├── routes # React routes/pages
|
||||
├── scripts # Random assortment of visual components and utilities 🐉
|
||||
├── strings # Translation files
|
||||
├── styles # Common app Sass stylesheets
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import loadable from '@loadable/component';
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
import { History } from '@remix-run/router';
|
||||
import React from 'react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||
import React from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { DASHBOARD_APP_PATHS } from 'apps/dashboard/App';
|
||||
import AppHeader from 'components/AppHeader';
|
||||
import Backdrop from 'components/Backdrop';
|
||||
import { HistoryRouter } from 'components/router/HistoryRouter';
|
||||
|
@ -12,6 +14,7 @@ import { ApiProvider } from 'hooks/useApi';
|
|||
import { WebConfigProvider } from 'hooks/useWebConfig';
|
||||
import theme from 'themes/theme';
|
||||
|
||||
const DashboardApp = loadable(() => import('./apps/dashboard/App'));
|
||||
const ExperimentalApp = loadable(() => import('./apps/experimental/App'));
|
||||
const StableApp = loadable(() => import('./apps/stable/App'));
|
||||
|
||||
|
@ -21,16 +24,22 @@ const RootAppLayout = () => {
|
|||
const layoutMode = localStorage.getItem('layout');
|
||||
const isExperimentalLayout = layoutMode === 'experimental';
|
||||
|
||||
const location = useLocation();
|
||||
const isNewLayoutPath = Object.values(DASHBOARD_APP_PATHS)
|
||||
.some(path => location.pathname.startsWith(`/${path}`));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Backdrop />
|
||||
<AppHeader isHidden={isExperimentalLayout} />
|
||||
<AppHeader isHidden={isExperimentalLayout || isNewLayoutPath} />
|
||||
|
||||
{
|
||||
isExperimentalLayout ?
|
||||
<ExperimentalApp /> :
|
||||
<StableApp />
|
||||
}
|
||||
|
||||
<DashboardApp />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
66
src/apps/dashboard/App.tsx
Normal file
66
src/apps/dashboard/App.tsx
Normal file
|
@ -0,0 +1,66 @@
|
|||
import loadable from '@loadable/component';
|
||||
import React from 'react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
|
||||
import ConnectionRequired from 'components/ConnectionRequired';
|
||||
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
|
||||
import { AsyncPageProps, AsyncRoute, toAsyncPageRoute } from 'components/router/AsyncRoute';
|
||||
import { toRedirectRoute } from 'components/router/Redirect';
|
||||
import ServerContentPage from 'components/ServerContentPage';
|
||||
|
||||
import AppLayout from './AppLayout';
|
||||
import { REDIRECTS } from './routes/_redirects';
|
||||
import { ASYNC_ADMIN_ROUTES } from './routes/_asyncRoutes';
|
||||
import { LEGACY_ADMIN_ROUTES } from './routes/_legacyRoutes';
|
||||
|
||||
const DashboardAsyncPage = loadable(
|
||||
(props: { page: string }) => import(/* webpackChunkName: "[request]" */ `./routes/${props.page}`),
|
||||
{ cacheKey: (props: AsyncPageProps) => props.page }
|
||||
);
|
||||
|
||||
const toDashboardAsyncPageRoute = (route: AsyncRoute) => (
|
||||
toAsyncPageRoute({
|
||||
...route,
|
||||
element: DashboardAsyncPage
|
||||
})
|
||||
);
|
||||
|
||||
export const DASHBOARD_APP_PATHS = {
|
||||
Dashboard: 'dashboard',
|
||||
MetadataManager: 'metadata',
|
||||
PluginConfig: 'configurationpage'
|
||||
};
|
||||
|
||||
const DashboardApp = () => (
|
||||
<Routes>
|
||||
<Route element={<ConnectionRequired isAdminRequired />}>
|
||||
<Route element={<AppLayout drawerlessPaths={[ DASHBOARD_APP_PATHS.MetadataManager ]} />}>
|
||||
<Route path={DASHBOARD_APP_PATHS.Dashboard}>
|
||||
{ASYNC_ADMIN_ROUTES.map(toDashboardAsyncPageRoute)}
|
||||
{LEGACY_ADMIN_ROUTES.map(toViewManagerPageRoute)}
|
||||
</Route>
|
||||
|
||||
{/* NOTE: The metadata editor might deserve a dedicated app in the future */}
|
||||
{toViewManagerPageRoute({
|
||||
path: DASHBOARD_APP_PATHS.MetadataManager,
|
||||
pageProps: {
|
||||
controller: 'edititemmetadata',
|
||||
view: 'edititemmetadata.html'
|
||||
}
|
||||
})}
|
||||
|
||||
<Route path={DASHBOARD_APP_PATHS.PluginConfig} element={
|
||||
<ServerContentPage view='/web/configurationpage' />
|
||||
} />
|
||||
</Route>
|
||||
|
||||
{/* Suppress warnings for unhandled routes */}
|
||||
<Route path='*' element={null} />
|
||||
</Route>
|
||||
|
||||
{/* Redirects for old paths */}
|
||||
{REDIRECTS.map(toRedirectRoute)}
|
||||
</Routes>
|
||||
);
|
||||
|
||||
export default DashboardApp;
|
108
src/apps/dashboard/AppLayout.tsx
Normal file
108
src/apps/dashboard/AppLayout.tsx
Normal file
|
@ -0,0 +1,108 @@
|
|||
import AppBar from '@mui/material/AppBar';
|
||||
import Box from '@mui/material/Box';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import React, { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { Outlet, useLocation } from 'react-router-dom';
|
||||
|
||||
import AppBody from 'components/AppBody';
|
||||
import AppToolbar from 'components/toolbar/AppToolbar';
|
||||
import ElevationScroll from 'components/ElevationScroll';
|
||||
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
|
||||
import { useApi } from 'hooks/useApi';
|
||||
import { useLocalStorage } from 'hooks/useLocalStorage';
|
||||
|
||||
import AppDrawer from './components/drawer/AppDrawer';
|
||||
|
||||
import './AppOverrides.scss';
|
||||
|
||||
interface AppLayoutProps {
|
||||
drawerlessPaths: string[]
|
||||
}
|
||||
|
||||
interface DashboardAppSettings {
|
||||
isDrawerPinned: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_APP_SETTINGS: DashboardAppSettings = {
|
||||
isDrawerPinned: false
|
||||
};
|
||||
|
||||
const AppLayout: FC<AppLayoutProps> = ({
|
||||
drawerlessPaths
|
||||
}) => {
|
||||
const [ appSettings, setAppSettings ] = useLocalStorage<DashboardAppSettings>('DashboardAppSettings', DEFAULT_APP_SETTINGS);
|
||||
const [ isDrawerActive, setIsDrawerActive ] = useState(appSettings.isDrawerPinned);
|
||||
const location = useLocation();
|
||||
const theme = useTheme();
|
||||
const { user } = useApi();
|
||||
|
||||
const isDrawerAvailable = !drawerlessPaths.some(path => location.pathname.startsWith(`/${path}`));
|
||||
const isDrawerOpen = isDrawerActive && isDrawerAvailable && Boolean(user);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDrawerActive !== appSettings.isDrawerPinned) {
|
||||
setAppSettings({
|
||||
...appSettings,
|
||||
isDrawerPinned: isDrawerActive
|
||||
});
|
||||
}
|
||||
}, [ appSettings, isDrawerActive, setAppSettings ]);
|
||||
|
||||
const onToggleDrawer = useCallback(() => {
|
||||
setIsDrawerActive(!isDrawerActive);
|
||||
}, [ isDrawerActive, setIsDrawerActive ]);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<ElevationScroll elevate={isDrawerOpen}>
|
||||
<AppBar
|
||||
position='fixed'
|
||||
sx={{ zIndex: (muiTheme) => muiTheme.zIndex.drawer + 1 }}
|
||||
>
|
||||
<AppToolbar
|
||||
isDrawerAvailable={isDrawerAvailable}
|
||||
isDrawerOpen={isDrawerOpen}
|
||||
onDrawerButtonClick={onToggleDrawer}
|
||||
/>
|
||||
</AppBar>
|
||||
</ElevationScroll>
|
||||
|
||||
<AppDrawer
|
||||
open={isDrawerOpen}
|
||||
onClose={onToggleDrawer}
|
||||
onOpen={onToggleDrawer}
|
||||
/>
|
||||
|
||||
<Box
|
||||
component='main'
|
||||
sx={{
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
transition: theme.transitions.create('margin', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
}),
|
||||
marginLeft: 0,
|
||||
...(isDrawerAvailable && {
|
||||
marginLeft: {
|
||||
sm: `-${DRAWER_WIDTH}px`
|
||||
}
|
||||
}),
|
||||
...(isDrawerActive && {
|
||||
transition: theme.transitions.create('margin', {
|
||||
easing: theme.transitions.easing.easeOut,
|
||||
duration: theme.transitions.duration.enteringScreen
|
||||
}),
|
||||
marginLeft: 0
|
||||
})
|
||||
}}
|
||||
>
|
||||
<AppBody>
|
||||
<Outlet />
|
||||
</AppBody>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppLayout;
|
22
src/apps/dashboard/AppOverrides.scss
Normal file
22
src/apps/dashboard/AppOverrides.scss
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Default MUI breakpoints
|
||||
// https://mui.com/material-ui/customization/breakpoints/#default-breakpoints
|
||||
$mui-bp-sm: 600px;
|
||||
$mui-bp-md: 900px;
|
||||
$mui-bp-lg: 1200px;
|
||||
$mui-bp-xl: 1536px;
|
||||
|
||||
// Fix dashboard pages layout to work with drawer
|
||||
.dashboardDocument {
|
||||
.mainAnimatedPage {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.skinBody {
|
||||
position: unset !important;
|
||||
}
|
||||
|
||||
// Fix the padding of dashboard pages
|
||||
.content-primary.content-primary {
|
||||
padding-top: 3.25rem !important;
|
||||
}
|
||||
}
|
29
src/apps/dashboard/components/drawer/AppDrawer.tsx
Normal file
29
src/apps/dashboard/components/drawer/AppDrawer.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
|
||||
|
||||
import ServerDrawerSection from './sections/ServerDrawerSection';
|
||||
import DevicesDrawerSection from './sections/DevicesDrawerSection';
|
||||
import LiveTvDrawerSection from './sections/LiveTvDrawerSection';
|
||||
import AdvancedDrawerSection from './sections/AdvancedDrawerSection';
|
||||
import PluginDrawerSection from './sections/PluginDrawerSection';
|
||||
|
||||
const AppDrawer: FC<ResponsiveDrawerProps> = ({
|
||||
open = false,
|
||||
onClose,
|
||||
onOpen
|
||||
}) => (
|
||||
<ResponsiveDrawer
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onOpen={onOpen}
|
||||
>
|
||||
<ServerDrawerSection />
|
||||
<DevicesDrawerSection />
|
||||
<LiveTvDrawerSection />
|
||||
<AdvancedDrawerSection />
|
||||
<PluginDrawerSection />
|
||||
</ResponsiveDrawer>
|
||||
);
|
||||
|
||||
export default AppDrawer;
|
|
@ -19,10 +19,10 @@ import ListItemLink from 'components/ListItemLink';
|
|||
import globalize from 'scripts/globalize';
|
||||
|
||||
const PLUGIN_PATHS = [
|
||||
'/installedplugins.html',
|
||||
'/availableplugins.html',
|
||||
'/repositories.html',
|
||||
'/addplugin.html',
|
||||
'/dashboard/plugins',
|
||||
'/dashboard/plugins/catalog',
|
||||
'/dashboard/plugins/repositories',
|
||||
'/dashboard/plugins/add',
|
||||
'/configurationpage'
|
||||
];
|
||||
|
||||
|
@ -41,7 +41,7 @@ const AdvancedDrawerSection = () => {
|
|||
}
|
||||
>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/networking.html'>
|
||||
<ListItemLink to='/dashboard/networking'>
|
||||
<ListItemIcon>
|
||||
<Lan />
|
||||
</ListItemIcon>
|
||||
|
@ -49,7 +49,7 @@ const AdvancedDrawerSection = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/apikeys.html'>
|
||||
<ListItemLink to='/dashboard/keys'>
|
||||
<ListItemIcon>
|
||||
<VpnKey />
|
||||
</ListItemIcon>
|
||||
|
@ -57,7 +57,7 @@ const AdvancedDrawerSection = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/log.html'>
|
||||
<ListItemLink to='/dashboard/logs'>
|
||||
<ListItemIcon>
|
||||
<Article />
|
||||
</ListItemIcon>
|
||||
|
@ -65,7 +65,7 @@ const AdvancedDrawerSection = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/notificationsettings.html'>
|
||||
<ListItemLink to='/dashboard/notifications'>
|
||||
<ListItemIcon>
|
||||
<EditNotifications />
|
||||
</ListItemIcon>
|
||||
|
@ -73,7 +73,7 @@ const AdvancedDrawerSection = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/installedplugins.html' selected={false}>
|
||||
<ListItemLink to='/dashboard/plugins' selected={false}>
|
||||
<ListItemIcon>
|
||||
<Extension />
|
||||
</ListItemIcon>
|
||||
|
@ -83,19 +83,19 @@ const AdvancedDrawerSection = () => {
|
|||
</ListItem>
|
||||
<Collapse in={isPluginSectionOpen} timeout='auto' unmountOnExit>
|
||||
<List component='div' disablePadding>
|
||||
<ListItemLink to='/installedplugins.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/plugins' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('TabMyPlugins')} />
|
||||
</ListItemLink>
|
||||
<ListItemLink to='/availableplugins.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/plugins/catalog' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('TabCatalog')} />
|
||||
</ListItemLink>
|
||||
<ListItemLink to='/repositories.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/plugins/repositories' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('TabRepositories')} />
|
||||
</ListItemLink>
|
||||
</List>
|
||||
</Collapse>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/scheduledtasks.html'>
|
||||
<ListItemLink to='/dashboard/tasks'>
|
||||
<ListItemIcon>
|
||||
<Schedule />
|
||||
</ListItemIcon>
|
|
@ -12,8 +12,8 @@ import ListItemLink from 'components/ListItemLink';
|
|||
import globalize from 'scripts/globalize';
|
||||
|
||||
const DLNA_PATHS = [
|
||||
'/dlnasettings.html',
|
||||
'/dlnaprofiles.html'
|
||||
'/dashboard/dlna',
|
||||
'/dashboard/dlna/profiles'
|
||||
];
|
||||
|
||||
const DevicesDrawerSection = () => {
|
||||
|
@ -31,7 +31,7 @@ const DevicesDrawerSection = () => {
|
|||
}
|
||||
>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/devices.html'>
|
||||
<ListItemLink to='/dashboard/devices'>
|
||||
<ListItemIcon>
|
||||
<Devices />
|
||||
</ListItemIcon>
|
||||
|
@ -47,7 +47,7 @@ const DevicesDrawerSection = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/dlnasettings.html' selected={false}>
|
||||
<ListItemLink to='/dashboard/dlna' selected={false}>
|
||||
<ListItemIcon>
|
||||
<Input />
|
||||
</ListItemIcon>
|
||||
|
@ -57,10 +57,10 @@ const DevicesDrawerSection = () => {
|
|||
</ListItem>
|
||||
<Collapse in={isDlnaSectionOpen} timeout='auto' unmountOnExit>
|
||||
<List component='div' disablePadding>
|
||||
<ListItemLink to='/dlnasettings.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/dlna' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('Settings')} />
|
||||
</ListItemLink>
|
||||
<ListItemLink to='/dlnaprofiles.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/dlna/profiles' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('TabProfiles')} />
|
||||
</ListItemLink>
|
||||
</List>
|
|
@ -20,7 +20,7 @@ const LiveTvDrawerSection = () => {
|
|||
}
|
||||
>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/livetvstatus.html'>
|
||||
<ListItemLink to='/dashboard/livetv'>
|
||||
<ListItemIcon>
|
||||
<LiveTv />
|
||||
</ListItemIcon>
|
||||
|
@ -28,7 +28,7 @@ const LiveTvDrawerSection = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/livetvsettings.html'>
|
||||
<ListItemLink to='/dashboard/recordings'>
|
||||
<ListItemIcon>
|
||||
<Dvr />
|
||||
</ListItemIcon>
|
|
@ -12,16 +12,16 @@ import ListItemLink from 'components/ListItemLink';
|
|||
import globalize from 'scripts/globalize';
|
||||
|
||||
const LIBRARY_PATHS = [
|
||||
'/library.html',
|
||||
'/librarydisplay.html',
|
||||
'/metadataimages.html',
|
||||
'/metadatanfo.html'
|
||||
'/dashboard/libraries',
|
||||
'/dashboard/libraries/display',
|
||||
'/dashboard/libraries/metadata',
|
||||
'/dashboard/libraries/nfo'
|
||||
];
|
||||
|
||||
const PLAYBACK_PATHS = [
|
||||
'/encodingsettings.html',
|
||||
'/playbackconfiguration.html',
|
||||
'/streamingsettings.html'
|
||||
'/dashboard/playback/transcoding',
|
||||
'/dashboard/playback/resume',
|
||||
'/dashboard/playback/streaming'
|
||||
];
|
||||
|
||||
const ServerDrawerSection = () => {
|
||||
|
@ -40,7 +40,7 @@ const ServerDrawerSection = () => {
|
|||
}
|
||||
>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/dashboard.html'>
|
||||
<ListItemLink to='/dashboard'>
|
||||
<ListItemIcon>
|
||||
<Dashboard />
|
||||
</ListItemIcon>
|
||||
|
@ -48,7 +48,7 @@ const ServerDrawerSection = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/dashboardgeneral.html'>
|
||||
<ListItemLink to='/dashboard/settings'>
|
||||
<ListItemIcon>
|
||||
<Settings />
|
||||
</ListItemIcon>
|
||||
|
@ -56,7 +56,7 @@ const ServerDrawerSection = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/userprofiles.html'>
|
||||
<ListItemLink to='/dashboard/users'>
|
||||
<ListItemIcon>
|
||||
<People />
|
||||
</ListItemIcon>
|
||||
|
@ -64,7 +64,7 @@ const ServerDrawerSection = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/library.html' selected={false}>
|
||||
<ListItemLink to='/dashboard/libraries' selected={false}>
|
||||
<ListItemIcon>
|
||||
<LibraryAdd />
|
||||
</ListItemIcon>
|
||||
|
@ -74,22 +74,22 @@ const ServerDrawerSection = () => {
|
|||
</ListItem>
|
||||
<Collapse in={isLibrarySectionOpen} timeout='auto' unmountOnExit>
|
||||
<List component='div' disablePadding>
|
||||
<ListItemLink to='/library.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/libraries' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('HeaderLibraries')} />
|
||||
</ListItemLink>
|
||||
<ListItemLink to='/librarydisplay.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/libraries/display' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('Display')} />
|
||||
</ListItemLink>
|
||||
<ListItemLink to='/metadataimages.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/libraries/metadata' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('Metadata')} />
|
||||
</ListItemLink>
|
||||
<ListItemLink to='/metadatanfo.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/libraries/nfo' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('TabNfoSettings')} />
|
||||
</ListItemLink>
|
||||
</List>
|
||||
</Collapse>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/encodingsettings.html' selected={false}>
|
||||
<ListItemLink to='/dashboard/playback/transcoding' selected={false}>
|
||||
<ListItemIcon>
|
||||
<PlayCircle />
|
||||
</ListItemIcon>
|
||||
|
@ -99,13 +99,13 @@ const ServerDrawerSection = () => {
|
|||
</ListItem>
|
||||
<Collapse in={isPlaybackSectionOpen} timeout='auto' unmountOnExit>
|
||||
<List component='div' disablePadding>
|
||||
<ListItemLink to='/encodingsettings.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/playback/transcoding' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('Transcoding')} />
|
||||
</ListItemLink>
|
||||
<ListItemLink to='/playbackconfiguration.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/playback/resume' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('ButtonResume')} />
|
||||
</ListItemLink>
|
||||
<ListItemLink to='/streamingsettings.html' sx={{ pl: 4 }}>
|
||||
<ListItemLink to='/dashboard/playback/streaming' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('TabStreaming')} />
|
||||
</ListItemLink>
|
||||
</List>
|
12
src/apps/dashboard/routes/_asyncRoutes.ts
Normal file
12
src/apps/dashboard/routes/_asyncRoutes.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import type { AsyncRoute } from 'components/router/AsyncRoute';
|
||||
|
||||
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
||||
{ path: 'activity' },
|
||||
{ path: 'notifications' },
|
||||
{ path: 'users' },
|
||||
{ path: 'users/access' },
|
||||
{ path: 'users/add' },
|
||||
{ path: 'users/parentalcontrol' },
|
||||
{ path: 'users/password' },
|
||||
{ path: 'users/profile' }
|
||||
];
|
|
@ -1,170 +1,164 @@
|
|||
import { LegacyRoute } from '../../../../components/router/LegacyRoute';
|
||||
import type { LegacyRoute } from 'components/router/LegacyRoute';
|
||||
|
||||
export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
|
||||
{
|
||||
path: 'dashboard.html',
|
||||
path: '/dashboard',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dashboard',
|
||||
view: 'dashboard/dashboard.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dashboardgeneral.html',
|
||||
path: 'settings',
|
||||
pageProps: {
|
||||
controller: 'dashboard/general',
|
||||
view: 'dashboard/general.html'
|
||||
}
|
||||
}, {
|
||||
path: 'networking.html',
|
||||
path: 'networking',
|
||||
pageProps: {
|
||||
controller: 'dashboard/networking',
|
||||
view: 'dashboard/networking.html'
|
||||
}
|
||||
}, {
|
||||
path: 'devices.html',
|
||||
path: 'devices',
|
||||
pageProps: {
|
||||
controller: 'dashboard/devices/devices',
|
||||
view: 'dashboard/devices/devices.html'
|
||||
}
|
||||
}, {
|
||||
path: 'device.html',
|
||||
path: 'devices/edit',
|
||||
pageProps: {
|
||||
controller: 'dashboard/devices/device',
|
||||
view: 'dashboard/devices/device.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dlnaprofile.html',
|
||||
path: 'dlna/profiles/edit',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dlna/profile',
|
||||
view: 'dashboard/dlna/profile.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dlnaprofiles.html',
|
||||
path: 'dlna/profiles',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dlna/profiles',
|
||||
view: 'dashboard/dlna/profiles.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dlnasettings.html',
|
||||
path: 'dlna',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dlna/settings',
|
||||
view: 'dashboard/dlna/settings.html'
|
||||
}
|
||||
}, {
|
||||
path: 'addplugin.html',
|
||||
path: 'plugins/add',
|
||||
pageProps: {
|
||||
controller: 'dashboard/plugins/add/index',
|
||||
view: 'dashboard/plugins/add/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'library.html',
|
||||
path: 'libraries',
|
||||
pageProps: {
|
||||
controller: 'dashboard/library',
|
||||
view: 'dashboard/library.html'
|
||||
}
|
||||
}, {
|
||||
path: 'librarydisplay.html',
|
||||
path: 'libraries/display',
|
||||
pageProps: {
|
||||
controller: 'dashboard/librarydisplay',
|
||||
view: 'dashboard/librarydisplay.html'
|
||||
}
|
||||
}, {
|
||||
path: 'edititemmetadata.html',
|
||||
pageProps: {
|
||||
controller: 'edititemmetadata',
|
||||
view: 'edititemmetadata.html'
|
||||
}
|
||||
}, {
|
||||
path: 'encodingsettings.html',
|
||||
path: 'playback/transcoding',
|
||||
pageProps: {
|
||||
controller: 'dashboard/encodingsettings',
|
||||
view: 'dashboard/encodingsettings.html'
|
||||
}
|
||||
}, {
|
||||
path: 'log.html',
|
||||
path: 'logs',
|
||||
pageProps: {
|
||||
controller: 'dashboard/logs',
|
||||
view: 'dashboard/logs.html'
|
||||
}
|
||||
}, {
|
||||
path: 'metadataimages.html',
|
||||
path: 'libraries/metadata',
|
||||
pageProps: {
|
||||
controller: 'dashboard/metadataImages',
|
||||
view: 'dashboard/metadataimages.html'
|
||||
}
|
||||
}, {
|
||||
path: 'metadatanfo.html',
|
||||
path: 'libraries/nfo',
|
||||
pageProps: {
|
||||
controller: 'dashboard/metadatanfo',
|
||||
view: 'dashboard/metadatanfo.html'
|
||||
}
|
||||
}, {
|
||||
path: 'playbackconfiguration.html',
|
||||
path: 'playback/resume',
|
||||
pageProps: {
|
||||
controller: 'dashboard/playback',
|
||||
view: 'dashboard/playback.html'
|
||||
}
|
||||
}, {
|
||||
path: 'availableplugins.html',
|
||||
path: 'plugins/catalog',
|
||||
pageProps: {
|
||||
controller: 'dashboard/plugins/available/index',
|
||||
view: 'dashboard/plugins/available/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'repositories.html',
|
||||
path: 'plugins/repositories',
|
||||
pageProps: {
|
||||
controller: 'dashboard/plugins/repositories/index',
|
||||
view: 'dashboard/plugins/repositories/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'livetvguideprovider.html',
|
||||
path: 'livetv/guide',
|
||||
pageProps: {
|
||||
controller: 'livetvguideprovider',
|
||||
view: 'livetvguideprovider.html'
|
||||
}
|
||||
}, {
|
||||
path: 'livetvsettings.html',
|
||||
path: 'recordings',
|
||||
pageProps: {
|
||||
controller: 'livetvsettings',
|
||||
view: 'livetvsettings.html'
|
||||
}
|
||||
}, {
|
||||
path: 'livetvstatus.html',
|
||||
path: 'livetv',
|
||||
pageProps: {
|
||||
controller: 'livetvstatus',
|
||||
view: 'livetvstatus.html'
|
||||
}
|
||||
}, {
|
||||
path: 'livetvtuner.html',
|
||||
path: 'livetv/tuner',
|
||||
pageProps: {
|
||||
controller: 'livetvtuner',
|
||||
view: 'livetvtuner.html'
|
||||
}
|
||||
}, {
|
||||
path: 'installedplugins.html',
|
||||
path: 'plugins',
|
||||
pageProps: {
|
||||
controller: 'dashboard/plugins/installed/index',
|
||||
view: 'dashboard/plugins/installed/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'scheduledtask.html',
|
||||
path: 'tasks/edit',
|
||||
pageProps: {
|
||||
controller: 'dashboard/scheduledtasks/scheduledtask',
|
||||
view: 'dashboard/scheduledtasks/scheduledtask.html'
|
||||
}
|
||||
}, {
|
||||
path: 'scheduledtasks.html',
|
||||
path: 'tasks',
|
||||
pageProps: {
|
||||
controller: 'dashboard/scheduledtasks/scheduledtasks',
|
||||
view: 'dashboard/scheduledtasks/scheduledtasks.html'
|
||||
}
|
||||
}, {
|
||||
path: 'apikeys.html',
|
||||
path: 'keys',
|
||||
pageProps: {
|
||||
controller: 'dashboard/apikeys',
|
||||
view: 'dashboard/apikeys.html'
|
||||
}
|
||||
}, {
|
||||
path: 'streamingsettings.html',
|
||||
path: 'playback/streaming',
|
||||
pageProps: {
|
||||
view: 'dashboard/streaming.html',
|
||||
controller: 'dashboard/streaming'
|
40
src/apps/dashboard/routes/_redirects.ts
Normal file
40
src/apps/dashboard/routes/_redirects.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import type { Redirect } from 'components/router/Redirect';
|
||||
|
||||
export const REDIRECTS: Redirect[] = [
|
||||
{ from: 'addplugin.html', to: '/dashboard/plugins/add' },
|
||||
{ from: 'apikeys.html', to: '/dashboard/keys' },
|
||||
{ from: 'availableplugins.html', to: '/dashboard/plugins/catalog' },
|
||||
{ from: 'dashboard.html', to: '/dashboard' },
|
||||
{ from: 'dashboardgeneral.html', to: '/dashboard/settings' },
|
||||
{ from: 'device.html', to: '/dashboard/devices/edit' },
|
||||
{ from: 'devices.html', to: '/dashboard/devices' },
|
||||
{ from: 'dlnaprofile.html', to: '/dashboard/dlna/profiles/edit' },
|
||||
{ from: 'dlnaprofiles.html', to: '/dashboard/dlna/profiles' },
|
||||
{ from: 'dlnasettings.html', to: '/dashboard/dlna' },
|
||||
{ from: 'edititemmetadata.html', to: '/metadata' },
|
||||
{ from: 'encodingsettings.html', to: '/dashboard/playback/transcoding' },
|
||||
{ from: 'installedplugins.html', to: '/dashboard/plugins' },
|
||||
{ from: 'library.html', to: '/dashboard/libraries' },
|
||||
{ from: 'librarydisplay.html', to: '/dashboard/libraries/display' },
|
||||
{ from: 'livetvguideprovider.html', to: '/dashboard/livetv/guide' },
|
||||
{ from: 'livetvsettings.html', to: '/dashboard/recordings' },
|
||||
{ from: 'livetvstatus.html', to: '/dashboard/livetv' },
|
||||
{ from: 'livetvtuner.html', to: '/dashboard/livetv/tuner' },
|
||||
{ from: 'log.html', to: '/dashboard/logs' },
|
||||
{ from: 'metadataimages.html', to: '/dashboard/libraries/metadata' },
|
||||
{ from: 'metadatanfo.html', to: '/dashboard/libraries/nfo' },
|
||||
{ from: 'networking.html', to: '/dashboard/networking' },
|
||||
{ from: 'notificationsettings.html', to: '/dashboard/notifications' },
|
||||
{ from: 'playbackconfiguration.html', to: '/dashboard/playback/resume' },
|
||||
{ from: 'repositories.html', to: '/dashboard/plugins/repositories' },
|
||||
{ from: 'scheduledtask.html', to: '/dashboard/tasks/edit' },
|
||||
{ from: 'scheduledtasks.html', to: '/dashboard/tasks' },
|
||||
{ from: 'serveractivity.html', to: '/dashboard/activity' },
|
||||
{ from: 'streamingsettings.html', to: '/dashboard/playback/streaming' },
|
||||
{ from: 'useredit.html', to: '/dashboard/users/profile' },
|
||||
{ from: 'userlibraryaccess.html', to: '/dashboard/users/access' },
|
||||
{ from: 'usernew.html', to: '/dashboard/users/add' },
|
||||
{ from: 'userparentalcontrol.html', to: '/dashboard/users/parentalcontrol' },
|
||||
{ from: 'userpassword.html', to: '/dashboard/users/password' },
|
||||
{ from: 'userprofiles.html', to: '/dashboard/users' }
|
||||
];
|
|
@ -19,9 +19,9 @@ import { parseISO8601Date, toLocaleDateString, toLocaleTimeString } from 'script
|
|||
import globalize from 'scripts/globalize';
|
||||
import { toBoolean } from 'utils/string';
|
||||
|
||||
import LogLevelChip from '../../components/activityTable/LogLevelChip';
|
||||
import OverviewCell from '../../components/activityTable/OverviewCell';
|
||||
import GridActionsCellLink from '../../components/GridActionsCellLink';
|
||||
import LogLevelChip from '../components/activityTable/LogLevelChip';
|
||||
import OverviewCell from '../components/activityTable/OverviewCell';
|
||||
import GridActionsCellLink from '../components/dataGrid/GridActionsCellLink';
|
||||
|
||||
const DEFAULT_PAGE_SIZE = 25;
|
||||
const VIEW_PARAM = 'useractivity';
|
||||
|
@ -68,7 +68,7 @@ const Activity = () => {
|
|||
sx={{ padding: 0 }}
|
||||
title={users[row.UserId]?.Name ?? undefined}
|
||||
component={Link}
|
||||
to={`/useredit.html?userId=${row.UserId}`}
|
||||
to={`/dashboard/users/profile?userId=${row.UserId}`}
|
||||
>
|
||||
<UserAvatar user={users[row.UserId]} />
|
||||
</IconButton>
|
|
@ -9,7 +9,7 @@ const PluginLink = () => (
|
|||
__html: `<a
|
||||
is='emby-linkbutton'
|
||||
class='button-link'
|
||||
href='#/addplugin.html?name=Webhook&guid=71552a5a5c5c4350a2aeebe451a30173'
|
||||
href='#/dashboard/plugins/add?name=Webhook&guid=71552a5a5c5c4350a2aeebe451a30173'
|
||||
>
|
||||
${globalize.translate('GetThePlugin')}
|
||||
</a>`
|
|
@ -140,7 +140,7 @@ const UserNew: FunctionComponent = () => {
|
|||
}
|
||||
|
||||
window.ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () {
|
||||
Dashboard.navigate('useredit.html?userId=' + user.Id)
|
||||
Dashboard.navigate('/dashboard/users/profile?userId=' + user.Id)
|
||||
.catch(err => {
|
||||
console.error('[usernew] failed to navigate to edit user page', err);
|
||||
});
|
|
@ -85,21 +85,21 @@ const UserProfiles: FunctionComponent = () => {
|
|||
callback: function (id: string) {
|
||||
switch (id) {
|
||||
case 'open':
|
||||
Dashboard.navigate('useredit.html?userId=' + userId)
|
||||
Dashboard.navigate('/dashboard/users/profile?userId=' + userId)
|
||||
.catch(err => {
|
||||
console.error('[userprofiles] failed to navigate to user edit page', err);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'access':
|
||||
Dashboard.navigate('userlibraryaccess.html?userId=' + userId)
|
||||
Dashboard.navigate('/dashboard/users/access?userId=' + userId)
|
||||
.catch(err => {
|
||||
console.error('[userprofiles] failed to navigate to user library page', err);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'parentalcontrol':
|
||||
Dashboard.navigate('userparentalcontrol.html?userId=' + userId)
|
||||
Dashboard.navigate('/dashboard/users/parentalcontrol?userId=' + userId)
|
||||
.catch(err => {
|
||||
console.error('[userprofiles] failed to navigate to parental control page', err);
|
||||
});
|
||||
|
@ -146,7 +146,7 @@ const UserProfiles: FunctionComponent = () => {
|
|||
});
|
||||
|
||||
(page.querySelector('#btnAddUser') as HTMLButtonElement).addEventListener('click', function() {
|
||||
Dashboard.navigate('usernew.html')
|
||||
Dashboard.navigate('/dashboard/users/add')
|
||||
.catch(err => {
|
||||
console.error('[userprofiles] failed to navigate to new user page', err);
|
||||
});
|
|
@ -32,7 +32,7 @@ const getCheckedElementDataIds = (elements: NodeListOf<Element>) => (
|
|||
);
|
||||
|
||||
function onSaveComplete() {
|
||||
Dashboard.navigate('userprofiles.html')
|
||||
Dashboard.navigate('/dashboard/users')
|
||||
.catch(err => {
|
||||
console.error('[useredit] failed to navigate to user profile', err);
|
||||
});
|
|
@ -1,16 +1,16 @@
|
|||
import React from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { DASHBOARD_APP_PATHS } from 'apps/dashboard/App';
|
||||
import { REDIRECTS } from 'apps/stable/routes/_redirects';
|
||||
import ConnectionRequired from 'components/ConnectionRequired';
|
||||
import ServerContentPage from 'components/ServerContentPage';
|
||||
import { toAsyncPageRoute } from 'components/router/AsyncRoute';
|
||||
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
|
||||
import { toRedirectRoute } from 'components/router/Redirect';
|
||||
|
||||
import AppLayout from './AppLayout';
|
||||
import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES } from './routes/asyncRoutes';
|
||||
import { LEGACY_ADMIN_ROUTES, LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './routes/legacyRoutes';
|
||||
import { ASYNC_USER_ROUTES } from './routes/asyncRoutes';
|
||||
import { LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './routes/legacyRoutes';
|
||||
|
||||
const ExperimentalApp = () => {
|
||||
return (
|
||||
|
@ -22,16 +22,6 @@ const ExperimentalApp = () => {
|
|||
{LEGACY_USER_ROUTES.map(toViewManagerPageRoute)}
|
||||
</Route>
|
||||
|
||||
{/* Admin routes */}
|
||||
<Route element={<ConnectionRequired isAdminRequired />}>
|
||||
{ASYNC_ADMIN_ROUTES.map(toAsyncPageRoute)}
|
||||
{LEGACY_ADMIN_ROUTES.map(toViewManagerPageRoute)}
|
||||
|
||||
<Route path='configurationpage' element={
|
||||
<ServerContentPage view='/web/configurationpage' />
|
||||
} />
|
||||
</Route>
|
||||
|
||||
{/* Public routes */}
|
||||
<Route element={<ConnectionRequired isUserRequired={false} />}>
|
||||
<Route index element={<Navigate replace to='/home.html' />} />
|
||||
|
@ -42,6 +32,15 @@ const ExperimentalApp = () => {
|
|||
|
||||
{/* Redirects for old paths */}
|
||||
{REDIRECTS.map(toRedirectRoute)}
|
||||
|
||||
{/* Ignore dashboard routes */}
|
||||
{Object.entries(DASHBOARD_APP_PATHS).map(([ key, path ]) => (
|
||||
<Route
|
||||
key={key}
|
||||
path={`/${path}/*`}
|
||||
element={null}
|
||||
/>
|
||||
))}
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -10,11 +10,6 @@ $mui-bp-xl: 1536px;
|
|||
position: relative;
|
||||
}
|
||||
|
||||
// Fix dashboard pages layout to work with drawer
|
||||
.dashboardDocument .skinBody {
|
||||
position: unset;
|
||||
}
|
||||
|
||||
// Hide some items from the user "settings" page that are in the drawer
|
||||
#myPreferencesMenuPage {
|
||||
.lnkQuickConnectPreferences,
|
||||
|
@ -26,8 +21,7 @@ $mui-bp-xl: 1536px;
|
|||
|
||||
// Fix the padding of some pages
|
||||
.homePage.libraryPage, // Home page
|
||||
.libraryPage:not(.withTabs), // Tabless library pages
|
||||
.content-primary.content-primary { // Dashboard pages
|
||||
.libraryPage:not(.withTabs) { // Tabless library pages
|
||||
padding-top: 3.25rem !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
import React, { FC } from 'react';
|
||||
import { Route, Routes, useLocation } from 'react-router-dom';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
|
||||
|
||||
import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES } from '../../routes/asyncRoutes';
|
||||
import { LEGACY_ADMIN_ROUTES, LEGACY_USER_ROUTES } from '../../routes/legacyRoutes';
|
||||
|
||||
import AdvancedDrawerSection from './dashboard/AdvancedDrawerSection';
|
||||
import DevicesDrawerSection from './dashboard/DevicesDrawerSection';
|
||||
import LiveTvDrawerSection from './dashboard/LiveTvDrawerSection';
|
||||
import PluginDrawerSection from './dashboard/PluginDrawerSection';
|
||||
import ServerDrawerSection from './dashboard/ServerDrawerSection';
|
||||
import MainDrawerContent from './MainDrawerContent';
|
||||
import { ASYNC_USER_ROUTES } from '../../routes/asyncRoutes';
|
||||
import { LEGACY_USER_ROUTES } from '../../routes/legacyRoutes';
|
||||
import { isTabPath } from '../tabs/tabRoutes';
|
||||
|
||||
export const DRAWER_WIDTH = 240;
|
||||
import MainDrawerContent from './MainDrawerContent';
|
||||
|
||||
const DRAWERLESS_ROUTES = [
|
||||
'edititemmetadata.html', // metadata manager
|
||||
'video' // video player
|
||||
];
|
||||
|
||||
|
@ -26,77 +18,29 @@ const MAIN_DRAWER_ROUTES = [
|
|||
...LEGACY_USER_ROUTES
|
||||
].filter(route => !DRAWERLESS_ROUTES.includes(route.path));
|
||||
|
||||
const ADMIN_DRAWER_ROUTES = [
|
||||
...ASYNC_ADMIN_ROUTES,
|
||||
...LEGACY_ADMIN_ROUTES,
|
||||
{ path: '/configurationpage' } // Plugin configuration page
|
||||
].filter(route => !DRAWERLESS_ROUTES.includes(route.path));
|
||||
|
||||
/** Utility function to check if a path has a drawer. */
|
||||
export const isDrawerPath = (path: string) => (
|
||||
MAIN_DRAWER_ROUTES.some(route => route.path === path || `/${route.path}` === path)
|
||||
|| ADMIN_DRAWER_ROUTES.some(route => route.path === path || `/${route.path}` === path)
|
||||
);
|
||||
|
||||
const Drawer: FC<ResponsiveDrawerProps> = ({ children, ...props }) => {
|
||||
const location = useLocation();
|
||||
const hasSecondaryToolBar = isTabPath(location.pathname);
|
||||
|
||||
return (
|
||||
<ResponsiveDrawer
|
||||
{...props}
|
||||
hasSecondaryToolBar={hasSecondaryToolBar}
|
||||
>
|
||||
{children}
|
||||
</ResponsiveDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
const AppDrawer: FC<ResponsiveDrawerProps> = ({
|
||||
open = false,
|
||||
onClose,
|
||||
onOpen
|
||||
}) => (
|
||||
<Routes>
|
||||
{
|
||||
MAIN_DRAWER_ROUTES.map(route => (
|
||||
<Route
|
||||
key={route.path}
|
||||
path={route.path}
|
||||
element={
|
||||
<Drawer
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onOpen={onOpen}
|
||||
>
|
||||
<MainDrawerContent />
|
||||
</Drawer>
|
||||
}
|
||||
/>
|
||||
))
|
||||
}
|
||||
{
|
||||
ADMIN_DRAWER_ROUTES.map(route => (
|
||||
<Route
|
||||
key={route.path}
|
||||
path={route.path}
|
||||
element={
|
||||
<Drawer
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onOpen={onOpen}
|
||||
>
|
||||
<ServerDrawerSection />
|
||||
<DevicesDrawerSection />
|
||||
<LiveTvDrawerSection />
|
||||
<AdvancedDrawerSection />
|
||||
<PluginDrawerSection />
|
||||
</Drawer>
|
||||
}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Routes>
|
||||
);
|
||||
}) => {
|
||||
const location = useLocation();
|
||||
const hasSecondaryToolBar = isTabPath(location.pathname);
|
||||
|
||||
return (
|
||||
<ResponsiveDrawer
|
||||
hasSecondaryToolBar={hasSecondaryToolBar}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onOpen={onOpen}
|
||||
>
|
||||
<MainDrawerContent />
|
||||
</ResponsiveDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppDrawer;
|
||||
|
|
|
@ -150,7 +150,7 @@ const MainDrawerContent = () => {
|
|||
}
|
||||
>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/dashboard.html'>
|
||||
<ListItemLink to='/dashboard'>
|
||||
<ListItemIcon>
|
||||
<Dashboard />
|
||||
</ListItemIcon>
|
||||
|
@ -158,7 +158,7 @@ const MainDrawerContent = () => {
|
|||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/edititemmetadata.html'>
|
||||
<ListItemLink to='/metadata'>
|
||||
<ListItemIcon>
|
||||
<Edit />
|
||||
</ListItemIcon>
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
const NewCollectionButton: FC = () => {
|
||||
const showCollectionEditor = useCallback(() => {
|
||||
import('components/collectionEditor/collectionEditor').then(
|
||||
({ default: CollectionEditor }) => {
|
||||
const serverId = window.ApiClient.serverId();
|
||||
const collectionEditor = new CollectionEditor();
|
||||
collectionEditor.show({
|
||||
items: [],
|
||||
serverId: serverId
|
||||
}).catch(() => {
|
||||
// closed collection editor
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('[NewCollection] failed to load collection editor', err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
title={globalize.translate('Add')}
|
||||
className='paper-icon-button-light btnNewCollection autoSize'
|
||||
onClick={showCollectionEditor}
|
||||
>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewCollectionButton;
|
57
src/apps/experimental/components/library/PlayAllButton.tsx
Normal file
57
src/apps/experimental/components/library/PlayAllButton.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import globalize from 'scripts/globalize';
|
||||
import { getFiltersQuery } from 'utils/items';
|
||||
import { LibraryViewSettings } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
interface PlayAllButtonProps {
|
||||
item: BaseItemDto | undefined;
|
||||
items: BaseItemDto[];
|
||||
viewType: LibraryTab;
|
||||
hasFilters: boolean;
|
||||
libraryViewSettings: LibraryViewSettings
|
||||
}
|
||||
|
||||
const PlayAllButton: FC<PlayAllButtonProps> = ({ item, items, viewType, hasFilters, libraryViewSettings }) => {
|
||||
const play = useCallback(() => {
|
||||
if (item && !hasFilters) {
|
||||
playbackManager.play({
|
||||
items: [item],
|
||||
autoplay: true,
|
||||
queryOptions: {
|
||||
SortBy: [libraryViewSettings.SortBy],
|
||||
SortOrder: [libraryViewSettings.SortOrder]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
playbackManager.play({
|
||||
items: items,
|
||||
autoplay: true,
|
||||
queryOptions: {
|
||||
ParentId: item?.Id ?? undefined,
|
||||
...getFiltersQuery(viewType, libraryViewSettings),
|
||||
SortBy: [libraryViewSettings.SortBy],
|
||||
SortOrder: [libraryViewSettings.SortOrder]
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}, [hasFilters, item, items, libraryViewSettings, viewType]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
title={globalize.translate('HeaderPlayAll')}
|
||||
className='paper-icon-button-light btnPlay autoSize'
|
||||
onClick={play}
|
||||
>
|
||||
<PlayArrowIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayAllButton;
|
39
src/apps/experimental/components/library/QueueButton.tsx
Normal file
39
src/apps/experimental/components/library/QueueButton.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import QueueIcon from '@mui/icons-material/Queue';
|
||||
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
interface QueueButtonProps {
|
||||
item: BaseItemDto | undefined
|
||||
items: BaseItemDto[];
|
||||
hasFilters: boolean;
|
||||
}
|
||||
|
||||
const QueueButton: FC<QueueButtonProps> = ({ item, items, hasFilters }) => {
|
||||
const queue = useCallback(() => {
|
||||
if (item && !hasFilters) {
|
||||
playbackManager.queue({
|
||||
items: [item]
|
||||
});
|
||||
} else {
|
||||
playbackManager.queue({
|
||||
items: items
|
||||
});
|
||||
}
|
||||
}, [hasFilters, item, items]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
title={globalize.translate('AddToPlayQueue')}
|
||||
className='paper-icon-button-light btnQueue autoSize'
|
||||
onClick={queue}
|
||||
>
|
||||
<QueueIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default QueueButton;
|
49
src/apps/experimental/components/library/ShuffleButton.tsx
Normal file
49
src/apps/experimental/components/library/ShuffleButton.tsx
Normal file
|
@ -0,0 +1,49 @@
|
|||
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
||||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import ShuffleIcon from '@mui/icons-material/Shuffle';
|
||||
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import globalize from 'scripts/globalize';
|
||||
import { getFiltersQuery } from 'utils/items';
|
||||
import { LibraryViewSettings } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
interface ShuffleButtonProps {
|
||||
item: BaseItemDto | undefined;
|
||||
items: BaseItemDto[];
|
||||
viewType: LibraryTab
|
||||
hasFilters: boolean;
|
||||
libraryViewSettings: LibraryViewSettings
|
||||
}
|
||||
|
||||
const ShuffleButton: FC<ShuffleButtonProps> = ({ item, items, viewType, hasFilters, libraryViewSettings }) => {
|
||||
const shuffle = useCallback(() => {
|
||||
if (item && !hasFilters) {
|
||||
playbackManager.shuffle(item);
|
||||
} else {
|
||||
playbackManager.play({
|
||||
items: items,
|
||||
autoplay: true,
|
||||
queryOptions: {
|
||||
ParentId: item?.Id ?? undefined,
|
||||
...getFiltersQuery(viewType, libraryViewSettings),
|
||||
SortBy: [ItemSortBy.Random]
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [hasFilters, item, items, libraryViewSettings, viewType]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
title={globalize.translate('Shuffle')}
|
||||
className='paper-icon-button-light btnShuffle autoSize'
|
||||
onClick={shuffle}
|
||||
>
|
||||
<ShuffleIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShuffleButton;
|
|
@ -98,7 +98,7 @@ const SortButton: FC<SortButtonProps> = ({
|
|||
title={globalize.translate('Sort')}
|
||||
sx={{ ml: 2 }}
|
||||
aria-describedby={id}
|
||||
className='paper-icon-button-light btnShuffle autoSize'
|
||||
className='paper-icon-button-light btnSort autoSize'
|
||||
onClick={handleClick}
|
||||
>
|
||||
<SortByAlphaIcon />
|
||||
|
|
|
@ -100,7 +100,7 @@ const ViewSettingsButton: FC<ViewSettingsButtonProps> = ({
|
|||
title={globalize.translate('ButtonSelectView')}
|
||||
sx={{ ml: 2 }}
|
||||
aria-describedby={id}
|
||||
className='paper-icon-button-light btnShuffle autoSize'
|
||||
className='paper-icon-button-light btnSelectView autoSize'
|
||||
onClick={handleClick}
|
||||
>
|
||||
<ViewComfyIcon />
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import { AsyncRoute, AsyncRouteType } from 'components/router/AsyncRoute';
|
||||
|
||||
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
||||
{ path: 'dashboard/activity', page: 'dashboard/activity', type: AsyncRouteType.Experimental },
|
||||
{ path: 'notificationsettings.html', page: 'dashboard/notifications' },
|
||||
{ path: 'usernew.html', page: 'user/usernew' },
|
||||
{ path: 'userprofiles.html', page: 'user/userprofiles' },
|
||||
{ path: 'useredit.html', page: 'user/useredit' },
|
||||
{ path: 'userlibraryaccess.html', page: 'user/userlibraryaccess' },
|
||||
{ path: 'userparentalcontrol.html', page: 'user/userparentalcontrol' },
|
||||
{ path: 'userpassword.html', page: 'user/userpassword' }
|
||||
];
|
|
@ -1,2 +1 @@
|
|||
export * from './admin';
|
||||
export * from './user';
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
export * from './admin';
|
||||
export * from './public';
|
||||
export * from './user';
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import React from 'react';
|
||||
import { Navigate, Outlet, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { DASHBOARD_APP_PATHS } from 'apps/dashboard/App';
|
||||
import AppBody from 'components/AppBody';
|
||||
import ServerContentPage from 'components/ServerContentPage';
|
||||
import ConnectionRequired from 'components/ConnectionRequired';
|
||||
import { toAsyncPageRoute } from 'components/router/AsyncRoute';
|
||||
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
|
||||
|
||||
import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES } from './routes/asyncRoutes';
|
||||
import { LEGACY_ADMIN_ROUTES, LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './routes/legacyRoutes';
|
||||
import { REDIRECTS } from './routes/_redirects';
|
||||
import { toRedirectRoute } from 'components/router/Redirect';
|
||||
|
||||
import { ASYNC_USER_ROUTES } from './routes/asyncRoutes';
|
||||
import { LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES } from './routes/legacyRoutes';
|
||||
import { REDIRECTS } from './routes/_redirects';
|
||||
|
||||
const Layout = () => (
|
||||
<AppBody>
|
||||
<Outlet />
|
||||
|
@ -27,16 +27,6 @@ const StableApp = () => (
|
|||
{LEGACY_USER_ROUTES.map(toViewManagerPageRoute)}
|
||||
</Route>
|
||||
|
||||
{/* Admin routes */}
|
||||
<Route path='/' element={<ConnectionRequired isAdminRequired />}>
|
||||
{ASYNC_ADMIN_ROUTES.map(toAsyncPageRoute)}
|
||||
{LEGACY_ADMIN_ROUTES.map(toViewManagerPageRoute)}
|
||||
|
||||
<Route path='configurationpage' element={
|
||||
<ServerContentPage view='/web/configurationpage' />
|
||||
} />
|
||||
</Route>
|
||||
|
||||
{/* Public routes */}
|
||||
<Route path='/' element={<ConnectionRequired isUserRequired={false} />}>
|
||||
<Route index element={<Navigate replace to='/home.html' />} />
|
||||
|
@ -50,6 +40,15 @@ const StableApp = () => (
|
|||
|
||||
{/* Redirects for old paths */}
|
||||
{REDIRECTS.map(toRedirectRoute)}
|
||||
|
||||
{/* Ignore dashboard routes */}
|
||||
{Object.entries(DASHBOARD_APP_PATHS).map(([ key, path ]) => (
|
||||
<Route
|
||||
key={key}
|
||||
path={`/${path}/*`}
|
||||
element={null}
|
||||
/>
|
||||
))}
|
||||
</Routes>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { Redirect } from 'components/router/Redirect';
|
||||
|
||||
export const REDIRECTS: Redirect[] = [
|
||||
{ from: 'mypreferencesquickconnect.html', to: '/quickconnect' },
|
||||
{ from: 'serveractivity.html', to: '/dashboard/activity' }
|
||||
{ from: 'mypreferencesquickconnect.html', to: '/quickconnect' }
|
||||
];
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
|
||||
|
||||
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
||||
{ path: 'notificationsettings.html', page: 'dashboard/notifications' },
|
||||
{ path: 'usernew.html', page: 'user/usernew' },
|
||||
{ path: 'userprofiles.html', page: 'user/userprofiles' },
|
||||
{ path: 'useredit.html', page: 'user/useredit' },
|
||||
{ path: 'userlibraryaccess.html', page: 'user/userlibraryaccess' },
|
||||
{ path: 'userparentalcontrol.html', page: 'user/userparentalcontrol' },
|
||||
{ path: 'userpassword.html', page: 'user/userpassword' }
|
||||
];
|
|
@ -1,2 +1 @@
|
|||
export * from './admin';
|
||||
export * from './user';
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
import { LegacyRoute } from '../../../../components/router/LegacyRoute';
|
||||
|
||||
export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
|
||||
{
|
||||
path: 'dashboard.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dashboard',
|
||||
view: 'dashboard/dashboard.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dashboardgeneral.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/general',
|
||||
view: 'dashboard/general.html'
|
||||
}
|
||||
}, {
|
||||
path: 'networking.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/networking',
|
||||
view: 'dashboard/networking.html'
|
||||
}
|
||||
}, {
|
||||
path: 'devices.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/devices/devices',
|
||||
view: 'dashboard/devices/devices.html'
|
||||
}
|
||||
}, {
|
||||
path: 'device.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/devices/device',
|
||||
view: 'dashboard/devices/device.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dlnaprofile.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dlna/profile',
|
||||
view: 'dashboard/dlna/profile.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dlnaprofiles.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dlna/profiles',
|
||||
view: 'dashboard/dlna/profiles.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dlnasettings.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dlna/settings',
|
||||
view: 'dashboard/dlna/settings.html'
|
||||
}
|
||||
}, {
|
||||
path: 'addplugin.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/plugins/add/index',
|
||||
view: 'dashboard/plugins/add/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'library.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/library',
|
||||
view: 'dashboard/library.html'
|
||||
}
|
||||
}, {
|
||||
path: 'librarydisplay.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/librarydisplay',
|
||||
view: 'dashboard/librarydisplay.html'
|
||||
}
|
||||
}, {
|
||||
path: 'edititemmetadata.html',
|
||||
pageProps: {
|
||||
controller: 'edititemmetadata',
|
||||
view: 'edititemmetadata.html'
|
||||
}
|
||||
}, {
|
||||
path: 'encodingsettings.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/encodingsettings',
|
||||
view: 'dashboard/encodingsettings.html'
|
||||
}
|
||||
}, {
|
||||
path: 'log.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/logs',
|
||||
view: 'dashboard/logs.html'
|
||||
}
|
||||
}, {
|
||||
path: 'metadataimages.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/metadataImages',
|
||||
view: 'dashboard/metadataimages.html'
|
||||
}
|
||||
}, {
|
||||
path: 'metadatanfo.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/metadatanfo',
|
||||
view: 'dashboard/metadatanfo.html'
|
||||
}
|
||||
}, {
|
||||
path: 'playbackconfiguration.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/playback',
|
||||
view: 'dashboard/playback.html'
|
||||
}
|
||||
}, {
|
||||
path: 'availableplugins.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/plugins/available/index',
|
||||
view: 'dashboard/plugins/available/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'repositories.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/plugins/repositories/index',
|
||||
view: 'dashboard/plugins/repositories/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'livetvguideprovider.html',
|
||||
pageProps: {
|
||||
controller: 'livetvguideprovider',
|
||||
view: 'livetvguideprovider.html'
|
||||
}
|
||||
}, {
|
||||
path: 'livetvsettings.html',
|
||||
pageProps: {
|
||||
controller: 'livetvsettings',
|
||||
view: 'livetvsettings.html'
|
||||
}
|
||||
}, {
|
||||
path: 'livetvstatus.html',
|
||||
pageProps: {
|
||||
controller: 'livetvstatus',
|
||||
view: 'livetvstatus.html'
|
||||
}
|
||||
}, {
|
||||
path: 'livetvtuner.html',
|
||||
pageProps: {
|
||||
controller: 'livetvtuner',
|
||||
view: 'livetvtuner.html'
|
||||
}
|
||||
}, {
|
||||
path: 'installedplugins.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/plugins/installed/index',
|
||||
view: 'dashboard/plugins/installed/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'scheduledtask.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/scheduledtasks/scheduledtask',
|
||||
view: 'dashboard/scheduledtasks/scheduledtask.html'
|
||||
}
|
||||
}, {
|
||||
path: 'scheduledtasks.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/scheduledtasks/scheduledtasks',
|
||||
view: 'dashboard/scheduledtasks/scheduledtasks.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dashboard/activity',
|
||||
pageProps: {
|
||||
controller: 'dashboard/serveractivity',
|
||||
view: 'dashboard/serveractivity.html'
|
||||
}
|
||||
}, {
|
||||
path: 'apikeys.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/apikeys',
|
||||
view: 'dashboard/apikeys.html'
|
||||
}
|
||||
}, {
|
||||
path: 'streamingsettings.html',
|
||||
pageProps: {
|
||||
view: 'dashboard/streaming.html',
|
||||
controller: 'dashboard/streaming'
|
||||
}
|
||||
}
|
||||
];
|
|
@ -1,3 +1,2 @@
|
|||
export * from './admin';
|
||||
export * from './public';
|
||||
export * from './user';
|
||||
|
|
|
@ -5,24 +5,29 @@
|
|||
*/
|
||||
|
||||
import escapeHtml from 'escape-html';
|
||||
import datetime from '../../scripts/datetime';
|
||||
import imageLoader from '../images/imageLoader';
|
||||
import itemHelper from '../itemHelper';
|
||||
|
||||
import browser from 'scripts/browser';
|
||||
import datetime from 'scripts/datetime';
|
||||
import dom from 'scripts/dom';
|
||||
import globalize from 'scripts/globalize';
|
||||
import imageHelper from 'scripts/imagehelper';
|
||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||
import { randomInt } from 'utils/number';
|
||||
|
||||
import focusManager from '../focusManager';
|
||||
import imageLoader from '../images/imageLoader';
|
||||
import indicators from '../indicators/indicators';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import itemHelper from '../itemHelper';
|
||||
import layoutManager from '../layoutManager';
|
||||
import dom from '../../scripts/dom';
|
||||
import browser from '../../scripts/browser';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import itemShortcuts from '../shortcuts';
|
||||
import imageHelper from '../../scripts/imagehelper';
|
||||
import { randomInt } from '../../utils/number.ts';
|
||||
import './card.scss';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../guide/programs.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import { appRouter } from '../router/appRouter';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import itemShortcuts from '../shortcuts';
|
||||
|
||||
import 'elements/emby-button/paper-icon-button-light';
|
||||
|
||||
import './card.scss';
|
||||
import '../guide/programs.scss';
|
||||
|
||||
const enableFocusTransform = !browser.slow && !browser.edge;
|
||||
|
||||
|
@ -301,16 +306,16 @@ function setCardData(items, options) {
|
|||
options.shape = 'banner';
|
||||
options.coverImage = true;
|
||||
} else if (primaryImageAspectRatio >= 1.33) {
|
||||
options.shape = requestedShape === 'autooverflow' ? 'overflowBackdrop' : 'backdrop';
|
||||
options.shape = getBackdropShape(requestedShape === 'autooverflow');
|
||||
} else if (primaryImageAspectRatio > 0.71) {
|
||||
options.shape = requestedShape === 'autooverflow' ? 'overflowSquare' : 'square';
|
||||
options.shape = getSquareShape(requestedShape === 'autooverflow');
|
||||
} else {
|
||||
options.shape = requestedShape === 'autooverflow' ? 'overflowPortrait' : 'portrait';
|
||||
options.shape = getPortraitShape(requestedShape === 'autooverflow');
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.shape) {
|
||||
options.shape = options.defaultShape || (requestedShape === 'autooverflow' ? 'overflowSquare' : 'square');
|
||||
options.shape = options.defaultShape || getSquareShape(requestedShape === 'autooverflow');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,28 +10,28 @@ const createLinkElement = (activeTab: string) => ({
|
|||
is="emby-linkbutton"
|
||||
data-role="button"
|
||||
class="${activeTab === 'useredit' ? 'ui-btn-active' : ''}"
|
||||
onclick="Dashboard.navigate('useredit.html', true);">
|
||||
onclick="Dashboard.navigate('/dashboard/users/profile', true);">
|
||||
${globalize.translate('Profile')}
|
||||
</a>
|
||||
<a href="#"
|
||||
is="emby-linkbutton"
|
||||
data-role="button"
|
||||
class="${activeTab === 'userlibraryaccess' ? 'ui-btn-active' : ''}"
|
||||
onclick="Dashboard.navigate('userlibraryaccess.html', true);">
|
||||
onclick="Dashboard.navigate('/dashboard/users/access', true);">
|
||||
${globalize.translate('TabAccess')}
|
||||
</a>
|
||||
<a href="#"
|
||||
is="emby-linkbutton"
|
||||
data-role="button"
|
||||
class="${activeTab === 'userparentalcontrol' ? 'ui-btn-active' : ''}"
|
||||
onclick="Dashboard.navigate('userparentalcontrol.html', true);">
|
||||
onclick="Dashboard.navigate('/dashboard/users/parentalcontrol', true);">
|
||||
${globalize.translate('TabParentalControl')}
|
||||
</a>
|
||||
<a href="#"
|
||||
is="emby-linkbutton"
|
||||
data-role="button"
|
||||
class="${activeTab === 'userpassword' ? 'ui-btn-active' : ''}"
|
||||
onclick="Dashboard.navigate('userpassword.html', true);">
|
||||
onclick="Dashboard.navigate('/dashboard/users/password', true);">
|
||||
${globalize.translate('HeaderPassword')}
|
||||
</a>`
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ const createLinkElement = ({ user, renderImgUrl }: { user: UserDto, renderImgUrl
|
|||
__html: `<a
|
||||
is="emby-linkbutton"
|
||||
class="cardContent"
|
||||
href="#/useredit.html?userId=${user.Id}"
|
||||
href="#/dashboard/users/profile?userId=${user.Id}"
|
||||
>
|
||||
${renderImgUrl}
|
||||
</a>`
|
||||
|
|
|
@ -1,50 +1,42 @@
|
|||
import loading from './loading/loading';
|
||||
import cardBuilder from './cardbuilder/cardBuilder';
|
||||
import dom from '../scripts/dom';
|
||||
import dom from 'scripts/dom';
|
||||
import globalize from 'scripts/globalize';
|
||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||
import { getParameterByName } from 'utils/url';
|
||||
|
||||
import { appHost } from './apphost';
|
||||
import cardBuilder from './cardbuilder/cardBuilder';
|
||||
import imageLoader from './images/imageLoader';
|
||||
import globalize from '../scripts/globalize';
|
||||
import layoutManager from './layoutManager';
|
||||
import { getParameterByName } from '../utils/url.ts';
|
||||
import '../styles/scrollstyles.scss';
|
||||
import '../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import loading from './loading/loading';
|
||||
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
|
||||
import 'styles/scrollstyles.scss';
|
||||
|
||||
function enableScrollX() {
|
||||
return !layoutManager.desktop;
|
||||
}
|
||||
|
||||
function getThumbShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
function getPosterShape() {
|
||||
return enableScrollX() ? 'overflowPortrait' : 'portrait';
|
||||
}
|
||||
|
||||
function getSquareShape() {
|
||||
return enableScrollX() ? 'overflowSquare' : 'square';
|
||||
}
|
||||
|
||||
function getSections() {
|
||||
return [{
|
||||
name: 'Movies',
|
||||
types: 'Movie',
|
||||
id: 'favoriteMovies',
|
||||
shape: getPosterShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
showTitle: false,
|
||||
overlayPlayButton: true
|
||||
}, {
|
||||
name: 'Shows',
|
||||
types: 'Series',
|
||||
id: 'favoriteShows',
|
||||
shape: getPosterShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
showTitle: false,
|
||||
overlayPlayButton: true
|
||||
}, {
|
||||
name: 'Episodes',
|
||||
types: 'Episode',
|
||||
id: 'favoriteEpisode',
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
showParentTitle: true,
|
||||
|
@ -55,7 +47,7 @@ function getSections() {
|
|||
name: 'Videos',
|
||||
types: 'Video,MusicVideo',
|
||||
id: 'favoriteVideos',
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
preferThumb: true,
|
||||
showTitle: true,
|
||||
overlayPlayButton: true,
|
||||
|
@ -65,7 +57,7 @@ function getSections() {
|
|||
name: 'Artists',
|
||||
types: 'MusicArtist',
|
||||
id: 'favoriteArtists',
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
overlayText: false,
|
||||
|
@ -77,7 +69,7 @@ function getSections() {
|
|||
name: 'Albums',
|
||||
types: 'MusicAlbum',
|
||||
id: 'favoriteAlbums',
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
overlayText: false,
|
||||
|
@ -89,7 +81,7 @@ function getSections() {
|
|||
name: 'Songs',
|
||||
types: 'Audio',
|
||||
id: 'favoriteSongs',
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
overlayText: false,
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
import escapeHtml from 'escape-html';
|
||||
|
||||
import globalize from 'scripts/globalize';
|
||||
import imageHelper from 'scripts/imagehelper';
|
||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
|
||||
import cardBuilder from '../cardbuilder/cardBuilder';
|
||||
import layoutManager from '../layoutManager';
|
||||
import imageLoader from '../images/imageLoader';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import layoutManager from '../layoutManager';
|
||||
import { appRouter } from '../router/appRouter';
|
||||
import imageHelper from '../../scripts/imagehelper';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-scroller/emby-scroller';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import './homesections.scss';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
import 'elements/emby-button/paper-icon-button-light';
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import 'elements/emby-scroller/emby-scroller';
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
import './homesections.scss';
|
||||
|
||||
export function getDefaultSection(index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
|
@ -94,7 +99,7 @@ export function loadSections(elem, apiClient, user, userSettings) {
|
|||
const createNowLink = elem.querySelector('#button-createLibrary');
|
||||
if (createNowLink) {
|
||||
createNowLink.addEventListener('click', function () {
|
||||
Dashboard.navigate('library.html');
|
||||
Dashboard.navigate('dashboard/libraries');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -169,18 +174,6 @@ function enableScrollX() {
|
|||
return true;
|
||||
}
|
||||
|
||||
function getSquareShape() {
|
||||
return enableScrollX() ? 'overflowSquare' : 'square';
|
||||
}
|
||||
|
||||
function getThumbShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
function getPortraitShape() {
|
||||
return enableScrollX() ? 'overflowPortrait' : 'portrait';
|
||||
}
|
||||
|
||||
function getLibraryButtonsHtml(items) {
|
||||
let html = '';
|
||||
|
||||
|
@ -244,11 +237,11 @@ function getLatestItemsHtmlFn(itemType, viewType) {
|
|||
const cardLayout = false;
|
||||
let shape;
|
||||
if (itemType === 'Channel' || viewType === 'movies' || viewType === 'books' || viewType === 'tvshows') {
|
||||
shape = getPortraitShape();
|
||||
shape = getPortraitShape(enableScrollX());
|
||||
} else if (viewType === 'music' || viewType === 'homevideos') {
|
||||
shape = getSquareShape();
|
||||
shape = getSquareShape(enableScrollX());
|
||||
} else {
|
||||
shape = getThumbShape();
|
||||
shape = getBackdropShape(enableScrollX());
|
||||
}
|
||||
|
||||
return cardBuilder.getCardsHtml({
|
||||
|
@ -345,7 +338,7 @@ export function loadLibraryTiles(elem, apiClient, user, userSettings, shape, use
|
|||
|
||||
html += cardBuilder.getCardsHtml({
|
||||
items: userViews,
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
centerText: true,
|
||||
overlayText: false,
|
||||
|
@ -423,7 +416,9 @@ function getItemsToResumeHtmlFn(useEpisodeImages, mediaType) {
|
|||
items: items,
|
||||
preferThumb: true,
|
||||
inheritThumb: !useEpisodeImages,
|
||||
shape: (mediaType === 'Book') ? getPortraitShape() : getThumbShape(),
|
||||
shape: (mediaType === 'Book') ?
|
||||
getPortraitShape(enableScrollX()) :
|
||||
getBackdropShape(enableScrollX()),
|
||||
overlayText: false,
|
||||
showTitle: true,
|
||||
showParentTitle: true,
|
||||
|
@ -471,7 +466,7 @@ function getOnNowItemsHtml(items) {
|
|||
showChannelName: false,
|
||||
showAirDateTime: false,
|
||||
showAirEndTime: true,
|
||||
defaultShape: getThumbShape(),
|
||||
defaultShape: getBackdropShape(enableScrollX()),
|
||||
lines: 3,
|
||||
overlayPlayButton: true
|
||||
});
|
||||
|
@ -614,7 +609,7 @@ function getNextUpItemsHtmlFn(useEpisodeImages) {
|
|||
items: items,
|
||||
preferThumb: true,
|
||||
inheritThumb: !useEpisodeImages,
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
overlayText: false,
|
||||
showTitle: true,
|
||||
showParentTitle: true,
|
||||
|
|
|
@ -527,7 +527,7 @@ class AppRouter {
|
|||
}
|
||||
|
||||
if (item === 'manageserver') {
|
||||
return '#/dashboard.html';
|
||||
return '#/dashboard';
|
||||
}
|
||||
|
||||
if (item === 'recordedtv') {
|
||||
|
|
|
@ -115,7 +115,7 @@ const AppUserMenu: FC<AppUserMenuProps> = ({
|
|||
<MenuItem
|
||||
key='admin-dashboard-link'
|
||||
component={Link}
|
||||
to='/dashboard.html'
|
||||
to='/dashboard'
|
||||
onClick={onMenuClose}
|
||||
>
|
||||
|
||||
|
@ -127,7 +127,7 @@ const AppUserMenu: FC<AppUserMenuProps> = ({
|
|||
<MenuItem
|
||||
key='admin-metadata-link'
|
||||
component={Link}
|
||||
to='/edititemmetadata.html'
|
||||
to='/metadata'
|
||||
onClick={onMenuClose}
|
||||
>
|
||||
<ListItemIcon>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="dashboardSections" style="padding-top:.5em;">
|
||||
<div class="dashboardColumn dashboardColumn-2-60 dashboardColumn-3-46">
|
||||
<div class="dashboardSection">
|
||||
<a is="emby-linkbutton" href="#/dashboardgeneral.html" class="button-flat sectionTitleTextButton">
|
||||
<a is="emby-linkbutton" href="#/dashboard/settings" class="button-flat sectionTitleTextButton">
|
||||
<h3>${TabServer}</h3>
|
||||
<span class="material-icons chevron_right" aria-hidden="true"></span>
|
||||
</a>
|
||||
|
@ -33,7 +33,7 @@
|
|||
</div>
|
||||
|
||||
<div class="dashboardSection">
|
||||
<a is="emby-linkbutton" href="#/devices.html" class="button-flat sectionTitleTextButton">
|
||||
<a is="emby-linkbutton" href="#/dashboard/devices" class="button-flat sectionTitleTextButton">
|
||||
<h3>${HeaderActiveDevices}</h3>
|
||||
<span class="material-icons chevron_right" aria-hidden="true"></span>
|
||||
</a>
|
||||
|
@ -70,7 +70,7 @@
|
|||
</div>
|
||||
|
||||
<div class="dashboardSection">
|
||||
<a is="emby-linkbutton" href="#/dashboardgeneral.html" class="button-flat sectionTitleTextButton">
|
||||
<a is="emby-linkbutton" href="#/dashboard/settings" class="button-flat sectionTitleTextButton">
|
||||
<h3>${HeaderPaths}</h3>
|
||||
<span class="material-icons chevron_right" aria-hidden="true"></span>
|
||||
</a>
|
||||
|
|
|
@ -73,7 +73,7 @@ function showDeviceMenu(view, btn, deviceId) {
|
|||
callback: function (id) {
|
||||
switch (id) {
|
||||
case 'open':
|
||||
Dashboard.navigate('device.html?id=' + deviceId);
|
||||
Dashboard.navigate('dashboard/devices/edit?id=' + deviceId);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
|
@ -94,7 +94,7 @@ function load(page, devices) {
|
|||
deviceHtml += '<div class="cardBox visualCardBox">';
|
||||
deviceHtml += '<div class="cardScalable">';
|
||||
deviceHtml += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
deviceHtml += `<a is="emby-linkbutton" href="#!/device.html?id=${escapeHtml(device.Id)}" class="cardContent cardImageContainer ${cardBuilder.getDefaultBackgroundClass()}">`;
|
||||
deviceHtml += `<a is="emby-linkbutton" href="#/dashboard/devices/edit?id=${escapeHtml(device.Id)}" class="cardContent cardImageContainer ${cardBuilder.getDefaultBackgroundClass()}">`;
|
||||
// audit note: getDeviceIcon returns static text
|
||||
const iconUrl = imageHelper.getDeviceIcon(device);
|
||||
|
||||
|
|
|
@ -264,7 +264,7 @@
|
|||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="button-cancel raised block" onclick="Dashboard.navigate('dlnaprofiles.html');">
|
||||
<button is="emby-button" type="button" class="button-cancel raised block" onclick="Dashboard.navigate('dashboard/dlna/profiles');">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -639,7 +639,7 @@ function saveProfile(page, profile) {
|
|||
data: JSON.stringify(profile),
|
||||
contentType: 'application/json'
|
||||
}).then(function () {
|
||||
Dashboard.navigate('dlnaprofiles.html');
|
||||
Dashboard.navigate('dashboard/dlna/profiles');
|
||||
}, Dashboard.processErrorResponse);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${HeaderCustomDlnaProfiles}</h2>
|
||||
<a is="emby-linkbutton" href="#/dlnaprofile.html" class="fab submit" style="margin:0 0 0 1em">
|
||||
<a is="emby-linkbutton" href="#/dashboard/dlna/profiles/edit" class="fab submit" style="margin:0 0 0 1em">
|
||||
<span class="material-icons add" aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -40,7 +40,7 @@ function renderProfiles(page, element, profiles) {
|
|||
html += '<div class="listItem listItem-border">';
|
||||
html += '<span class="listItemIcon material-icons live_tv" aria-hidden="true"></span>';
|
||||
html += '<div class="listItemBody two-line">';
|
||||
html += "<a is='emby-linkbutton' style='padding:0;margin:0;' data-ripple='false' class='clearLink' href='#/dlnaprofile.html?id=" + profile.Id + "'>";
|
||||
html += "<a is='emby-linkbutton' style='padding:0;margin:0;' data-ripple='false' class='clearLink' href='#/dashboard/dlna/profiles/edit?id=" + profile.Id + "'>";
|
||||
html += '<div>' + escapeHtml(profile.Name) + '</div>';
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
|
@ -78,10 +78,10 @@ function deleteProfile(page, id) {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/dlnasettings.html',
|
||||
href: '#/dashboard/dlna',
|
||||
name: globalize.translate('Settings')
|
||||
}, {
|
||||
href: '#/dlnaprofiles.html',
|
||||
href: '#/dashboard/dlna/profiles',
|
||||
name: globalize.translate('TabProfiles')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -37,10 +37,10 @@ function onSubmit() {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/dlnasettings.html',
|
||||
href: '#/dashboard/dlna',
|
||||
name: globalize.translate('Settings')
|
||||
}, {
|
||||
href: '#/dlnaprofiles.html',
|
||||
href: '#/dashboard/dlna/profiles',
|
||||
name: globalize.translate('TabProfiles')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -167,13 +167,13 @@ function setDecodingCodecsVisible(context, value) {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/encodingsettings.html',
|
||||
href: '#/dashboard/playback/transcoding',
|
||||
name: globalize.translate('Transcoding')
|
||||
}, {
|
||||
href: '#/playbackconfiguration.html',
|
||||
href: '#/dashboard/playback/resume',
|
||||
name: globalize.translate('ButtonResume')
|
||||
}, {
|
||||
href: '#/streamingsettings.html',
|
||||
href: '#/dashboard/playback/streaming',
|
||||
name: globalize.translate('TabStreaming')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -360,16 +360,16 @@ function getVirtualFolderHtml(page, virtualFolder, index) {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/library.html',
|
||||
href: '#/dashboard/libraries',
|
||||
name: globalize.translate('HeaderLibraries')
|
||||
}, {
|
||||
href: '#/librarydisplay.html',
|
||||
href: '#/dashboard/libraries/display',
|
||||
name: globalize.translate('Display')
|
||||
}, {
|
||||
href: '#/metadataimages.html',
|
||||
href: '#/dashboard/libraries/metadata',
|
||||
name: globalize.translate('Metadata')
|
||||
}, {
|
||||
href: '#/metadatanfo.html',
|
||||
href: '#/dashboard/libraries/nfo',
|
||||
name: globalize.translate('TabNfoSettings')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -7,16 +7,16 @@ import Dashboard from '../../utils/dashboard';
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/library.html',
|
||||
href: '#/dashboard/libraries',
|
||||
name: globalize.translate('HeaderLibraries')
|
||||
}, {
|
||||
href: '#/librarydisplay.html',
|
||||
href: '#/dashboard/libraries/display',
|
||||
name: globalize.translate('Display')
|
||||
}, {
|
||||
href: '#/metadataimages.html',
|
||||
href: '#/dashboard/libraries/metadata',
|
||||
name: globalize.translate('Metadata')
|
||||
}, {
|
||||
href: '#/metadatanfo.html',
|
||||
href: '#/dashboard/libraries/nfo',
|
||||
name: globalize.translate('TabNfoSettings')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -88,16 +88,16 @@ function onSubmit() {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/library.html',
|
||||
href: '#/dashboard/libraries',
|
||||
name: globalize.translate('HeaderLibraries')
|
||||
}, {
|
||||
href: '#/librarydisplay.html',
|
||||
href: '#/dashboard/libraries/display',
|
||||
name: globalize.translate('Display')
|
||||
}, {
|
||||
href: '#/metadataimages.html',
|
||||
href: '#/dashboard/libraries/metadata',
|
||||
name: globalize.translate('Metadata')
|
||||
}, {
|
||||
href: '#/metadatanfo.html',
|
||||
href: '#/dashboard/libraries/nfo',
|
||||
name: globalize.translate('TabNfoSettings')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -46,16 +46,16 @@ function showConfirmMessage() {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/library.html',
|
||||
href: '#/dashboard/libraries',
|
||||
name: globalize.translate('HeaderLibraries')
|
||||
}, {
|
||||
href: '#/librarydisplay.html',
|
||||
href: '#/dashboard/libraries/display',
|
||||
name: globalize.translate('Display')
|
||||
}, {
|
||||
href: '#/metadataimages.html',
|
||||
href: '#/dashboard/libraries/metadata',
|
||||
name: globalize.translate('Metadata')
|
||||
}, {
|
||||
href: '#/metadatanfo.html',
|
||||
href: '#/dashboard/libraries/nfo',
|
||||
name: globalize.translate('TabNfoSettings')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -31,13 +31,13 @@ function onSubmit() {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/encodingsettings.html',
|
||||
href: '#/dashboard/playback/transcoding',
|
||||
name: globalize.translate('Transcoding')
|
||||
}, {
|
||||
href: '#/playbackconfiguration.html',
|
||||
href: '#/dashboard/playback/resume',
|
||||
name: globalize.translate('ButtonResume')
|
||||
}, {
|
||||
href: '#/streamingsettings.html',
|
||||
href: '#/dashboard/playback/streaming',
|
||||
name: globalize.translate('TabStreaming')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ function onSearchBarType(searchBar) {
|
|||
|
||||
function getPluginHtml(plugin, options, installedPlugins) {
|
||||
let html = '';
|
||||
let href = plugin.externalUrl ? plugin.externalUrl : '#/addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid;
|
||||
let href = plugin.externalUrl ? plugin.externalUrl : '#/dashboard/plugins/add?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid;
|
||||
|
||||
if (options.context) {
|
||||
href += '&context=' + options.context;
|
||||
|
@ -161,13 +161,13 @@ function getPluginHtml(plugin, options, installedPlugins) {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/installedplugins.html',
|
||||
href: '#/dashboard/plugins',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: '#/availableplugins.html',
|
||||
href: '#/dashboard/plugins/catalog',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: '#/repositories.html',
|
||||
href: '#/dashboard/plugins/repositories',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ function populateList(page, plugins, pluginConfigurationPages) {
|
|||
} else {
|
||||
html += '<div class="centerMessage">';
|
||||
html += '<h1>' + globalize.translate('MessageNoPluginsInstalled') + '</h1>';
|
||||
html += '<p><a is="emby-linkbutton" class="button-link" href="#/availableplugins.html">';
|
||||
html += '<p><a is="emby-linkbutton" class="button-link" href="#/dashboard/plugins/catalog">';
|
||||
html += globalize.translate('MessageBrowsePluginCatalog');
|
||||
html += '</a></p>';
|
||||
html += '</div>';
|
||||
|
@ -221,13 +221,13 @@ function reloadList(page) {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/installedplugins.html',
|
||||
href: '#/dashboard/plugins',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: '#/availableplugins.html',
|
||||
href: '#/dashboard/plugins/catalog',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: '#/repositories.html',
|
||||
href: '#/dashboard/plugins/repositories',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -105,13 +105,13 @@ function getRepositoryElement(repository) {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/installedplugins.html',
|
||||
href: '#/dashboard/plugins',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: '#/availableplugins.html',
|
||||
href: '#/dashboard/plugins/catalog',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: '#/repositories.html',
|
||||
href: '#/dashboard/plugins/repositories',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -53,12 +53,12 @@ function populateList(page, tasks) {
|
|||
html += '<div class="paperList">';
|
||||
}
|
||||
html += '<div class="listItem listItem-border scheduledTaskPaperIconItem" data-status="' + task.State + '">';
|
||||
html += "<a is='emby-linkbutton' style='margin:0;padding:0;' class='clearLink listItemIconContainer' href='scheduledtask.html?id=" + task.Id + "'>";
|
||||
html += "<a is='emby-linkbutton' style='margin:0;padding:0;' class='clearLink listItemIconContainer' href='/dashboard/tasks/edit?id=" + task.Id + "'>";
|
||||
html += '<span class="material-icons listItemIcon schedule" aria-hidden="true"></span>';
|
||||
html += '</a>';
|
||||
html += '<div class="listItemBody two-line">';
|
||||
const textAlignStyle = globalize.getIsRTL() ? 'right' : 'left';
|
||||
html += "<a class='clearLink' style='margin:0;padding:0;display:block;text-align:" + textAlignStyle + ";' is='emby-linkbutton' href='scheduledtask.html?id=" + task.Id + "'>";
|
||||
html += "<a class='clearLink' style='margin:0;padding:0;display:block;text-align:" + textAlignStyle + ";' is='emby-linkbutton' href='/dashboard/tasks/edit?id=" + task.Id + "'>";
|
||||
html += "<h3 class='listItemBodyText'>" + task.Name + '</h3>';
|
||||
html += "<div class='secondary listItemBodyText' id='taskProgress" + task.Id + "'>" + getTaskProgressHtml(task) + '</div>';
|
||||
html += '</a>';
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<div id="serverActivityPage" data-role="page" class="page type-interior serverActivityPage noSecondaryNavPage" data-title="${HeaderActivity}">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="verticalSection">
|
||||
<h2 class="sectionTitle"></h2>
|
||||
</div>
|
||||
<div class="readOnlyContent">
|
||||
<div class="paperList activityItems" data-activitylimit="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,32 +0,0 @@
|
|||
import ActivityLog from '../../components/activitylog';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import { toBoolean } from '../../utils/string.ts';
|
||||
|
||||
export default function (view, params) {
|
||||
let activityLog;
|
||||
|
||||
if (toBoolean(params.useractivity, true)) {
|
||||
view.querySelector('.activityItems').setAttribute('data-useractivity', 'true');
|
||||
view.querySelector('.sectionTitle').innerHTML = globalize.translate('HeaderActivity');
|
||||
} else {
|
||||
view.querySelector('.activityItems').setAttribute('data-useractivity', 'false');
|
||||
view.querySelector('.sectionTitle').innerHTML = globalize.translate('Alerts');
|
||||
}
|
||||
|
||||
view.addEventListener('viewshow', function () {
|
||||
if (!activityLog) {
|
||||
activityLog = new ActivityLog({
|
||||
serverId: ApiClient.serverId(),
|
||||
element: view.querySelector('.activityItems')
|
||||
});
|
||||
}
|
||||
});
|
||||
view.addEventListener('viewdestroy', function () {
|
||||
if (activityLog) {
|
||||
activityLog.destroy();
|
||||
}
|
||||
|
||||
activityLog = null;
|
||||
});
|
||||
}
|
||||
|
|
@ -22,13 +22,13 @@ function onSubmit() {
|
|||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/encodingsettings.html',
|
||||
href: '#/dashboard/playback/transcoding',
|
||||
name: globalize.translate('Transcoding')
|
||||
}, {
|
||||
href: '#/playbackconfiguration.html',
|
||||
href: '#/dashboard/playback/resume',
|
||||
name: globalize.translate('ButtonResume')
|
||||
}, {
|
||||
href: '#/streamingsettings.html',
|
||||
href: '#/dashboard/playback/streaming',
|
||||
name: globalize.translate('TabStreaming')
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -1,35 +1,25 @@
|
|||
import { appRouter } from '../components/router/appRouter';
|
||||
import cardBuilder from '../components/cardbuilder/cardBuilder';
|
||||
import dom from '../scripts/dom';
|
||||
import globalize from '../scripts/globalize';
|
||||
import { appHost } from '../components/apphost';
|
||||
import layoutManager from '../components/layoutManager';
|
||||
import focusManager from '../components/focusManager';
|
||||
import '../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../elements/emby-scroller/emby-scroller';
|
||||
import ServerConnections from '../components/ServerConnections';
|
||||
import { appHost } from 'components/apphost';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import focusManager from 'components/focusManager';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import dom from 'scripts/dom';
|
||||
import globalize from 'scripts/globalize';
|
||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import 'elements/emby-scroller/emby-scroller';
|
||||
|
||||
function enableScrollX() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function getThumbShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
function getPosterShape() {
|
||||
return enableScrollX() ? 'overflowPortrait' : 'portrait';
|
||||
}
|
||||
|
||||
function getSquareShape() {
|
||||
return enableScrollX() ? 'overflowSquare' : 'square';
|
||||
}
|
||||
|
||||
function getSections() {
|
||||
return [{
|
||||
name: 'Movies',
|
||||
types: 'Movie',
|
||||
shape: getPosterShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
overlayPlayButton: true,
|
||||
|
@ -38,7 +28,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'Shows',
|
||||
types: 'Series',
|
||||
shape: getPosterShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
overlayPlayButton: true,
|
||||
|
@ -47,7 +37,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'Episodes',
|
||||
types: 'Episode',
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
showParentTitle: true,
|
||||
|
@ -57,7 +47,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'Videos',
|
||||
types: 'Video',
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
preferThumb: true,
|
||||
showTitle: true,
|
||||
overlayPlayButton: true,
|
||||
|
@ -66,7 +56,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'Collections',
|
||||
types: 'BoxSet',
|
||||
shape: getPosterShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
overlayPlayButton: true,
|
||||
overlayText: false,
|
||||
|
@ -74,7 +64,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'Playlists',
|
||||
types: 'Playlist',
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
overlayText: false,
|
||||
|
@ -85,7 +75,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'People',
|
||||
types: 'Person',
|
||||
shape: getPosterShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
overlayText: false,
|
||||
|
@ -96,7 +86,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'Artists',
|
||||
types: 'MusicArtist',
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
overlayText: false,
|
||||
|
@ -107,7 +97,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'Albums',
|
||||
types: 'MusicAlbum',
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
overlayText: false,
|
||||
|
@ -118,7 +108,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'Songs',
|
||||
types: 'Audio',
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
preferThumb: false,
|
||||
showTitle: true,
|
||||
overlayText: false,
|
||||
|
@ -130,7 +120,7 @@ function getSections() {
|
|||
}, {
|
||||
name: 'Books',
|
||||
types: 'Book',
|
||||
shape: getPosterShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
overlayPlayButton: true,
|
||||
|
|
|
@ -4,39 +4,42 @@ import { marked } from 'marked';
|
|||
import escapeHtml from 'escape-html';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
|
||||
import { appHost } from '../../components/apphost';
|
||||
import loading from '../../components/loading/loading';
|
||||
import { appRouter } from '../../components/router/appRouter';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import Events from '../../utils/events.ts';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import datetime from '../../scripts/datetime';
|
||||
import mediaInfo from '../../components/mediainfo/mediainfo';
|
||||
import { clearBackdrop, setBackdrops } from '../../components/backdrop/backdrop';
|
||||
import listView from '../../components/listview/listview';
|
||||
import itemContextMenu from '../../components/itemContextMenu';
|
||||
import itemHelper from '../../components/itemHelper';
|
||||
import dom from '../../scripts/dom';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import libraryMenu from '../../scripts/libraryMenu';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import browser from '../../scripts/browser';
|
||||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-playstatebutton/emby-playstatebutton';
|
||||
import '../../elements/emby-ratingbutton/emby-ratingbutton';
|
||||
import '../../elements/emby-scroller/emby-scroller';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
import itemShortcuts from '../../components/shortcuts';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import confirm from '../../components/confirm/confirm';
|
||||
import { download } from '../../scripts/fileDownloader';
|
||||
import { getItemBackdropImageUrl } from '../../utils/jellyfin-apiclient/backdropImage';
|
||||
import { appHost } from 'components/apphost';
|
||||
import { clearBackdrop, setBackdrops } from 'components/backdrop/backdrop';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import confirm from 'components/confirm/confirm';
|
||||
import imageLoader from 'components/images/imageLoader';
|
||||
import itemContextMenu from 'components/itemContextMenu';
|
||||
import itemHelper from 'components/itemHelper';
|
||||
import mediaInfo from 'components/mediainfo/mediainfo';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import listView from 'components/listview/listview';
|
||||
import loading from 'components/loading/loading';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
import itemShortcuts from 'components/shortcuts';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import browser from 'scripts/browser';
|
||||
import datetime from 'scripts/datetime';
|
||||
import dom from 'scripts/dom';
|
||||
import { download } from 'scripts/fileDownloader';
|
||||
import globalize from 'scripts/globalize';
|
||||
import libraryMenu from 'scripts/libraryMenu';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { getPortraitShape, getSquareShape } from 'utils/card';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import Events from 'utils/events';
|
||||
import { getItemBackdropImageUrl } from 'utils/jellyfin-apiclient/backdropImage';
|
||||
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import 'elements/emby-checkbox/emby-checkbox';
|
||||
import 'elements/emby-button/emby-button';
|
||||
import 'elements/emby-playstatebutton/emby-playstatebutton';
|
||||
import 'elements/emby-ratingbutton/emby-ratingbutton';
|
||||
import 'elements/emby-scroller/emby-scroller';
|
||||
import 'elements/emby-select/emby-select';
|
||||
|
||||
import 'styles/scrollstyles.scss';
|
||||
|
||||
function autoFocus(container) {
|
||||
import('../../components/autoFocuser').then(({ default: autoFocuser }) => {
|
||||
|
@ -1069,22 +1072,6 @@ function enableScrollX() {
|
|||
return browser.mobile && window.screen.availWidth <= 1000;
|
||||
}
|
||||
|
||||
function getPortraitShape(scrollX) {
|
||||
if (scrollX == null) {
|
||||
scrollX = enableScrollX();
|
||||
}
|
||||
|
||||
return scrollX ? 'overflowPortrait' : 'portrait';
|
||||
}
|
||||
|
||||
function getSquareShape(scrollX) {
|
||||
if (scrollX == null) {
|
||||
scrollX = enableScrollX();
|
||||
}
|
||||
|
||||
return scrollX ? 'overflowSquare' : 'square';
|
||||
}
|
||||
|
||||
function renderMoreFromSeason(view, item, apiClient) {
|
||||
const section = view.querySelector('.moreFromSeasonSection');
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import loading from '../../components/loading/loading';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import '../../scripts/livetvcomponents';
|
||||
import '../../components/listview/listview.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import imageLoader from 'components/images/imageLoader';
|
||||
import loading from 'components/loading/loading';
|
||||
import { getBackdropShape } from 'utils/card';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
|
||||
import 'scripts/livetvcomponents';
|
||||
import 'components/listview/listview.scss';
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
|
||||
function renderRecordings(elem, recordings, cardOptions, scrollX) {
|
||||
if (!elem) {
|
||||
|
@ -32,7 +34,7 @@ function renderRecordings(elem, recordings, cardOptions, scrollX) {
|
|||
recordingItems.innerHTML = cardBuilder.getCardsHtml(Object.assign({
|
||||
items: recordings,
|
||||
shape: scrollX ? 'autooverflow' : 'auto',
|
||||
defaultShape: scrollX ? 'overflowBackdrop' : 'backdrop',
|
||||
defaultShape: getBackdropShape(scrollX),
|
||||
showTitle: true,
|
||||
showParentTitle: true,
|
||||
coverImage: true,
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import layoutManager from '../../components/layoutManager';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import loading from '../../components/loading/loading';
|
||||
import '../../scripts/livetvcomponents';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import imageLoader from 'components/images/imageLoader';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import loading from 'components/loading/loading';
|
||||
import { getBackdropShape } from 'utils/card';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
|
||||
import 'elements/emby-button/emby-button';
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import 'scripts/livetvcomponents';
|
||||
|
||||
function enableScrollX() {
|
||||
return !layoutManager.desktop;
|
||||
|
@ -50,15 +52,11 @@ function renderRecordings(elem, recordings, cardOptions) {
|
|||
imageLoader.lazyChildren(recordingItems);
|
||||
}
|
||||
|
||||
function getBackdropShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
function renderActiveRecordings(context, promise) {
|
||||
promise.then(function (result) {
|
||||
renderRecordings(context.querySelector('#activeRecordings'), result.Items, {
|
||||
shape: enableScrollX() ? 'autooverflow' : 'auto',
|
||||
defaultShape: getBackdropShape(),
|
||||
defaultShape: getBackdropShape(enableScrollX()),
|
||||
showParentTitle: false,
|
||||
showParentTitleOrTitle: true,
|
||||
showTitle: true,
|
||||
|
|
|
@ -1,36 +1,25 @@
|
|||
import layoutManager from '../../components/layoutManager';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import inputManager from '../../scripts/inputManager';
|
||||
import loading from '../../components/loading/loading';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import * as mainTabsManager from '../../components/maintabsmanager';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-tabs/emby-tabs';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import { LibraryTab } from '../../types/libraryTab.ts';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import imageLoader from 'components/images/imageLoader';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import loading from 'components/loading/loading';
|
||||
import * as mainTabsManager from 'components/maintabsmanager';
|
||||
import globalize from 'scripts/globalize';
|
||||
import inputManager from 'scripts/inputManager';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import { getBackdropShape, getPortraitShape } from 'utils/card';
|
||||
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import 'elements/emby-tabs/emby-tabs';
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
import 'styles/scrollstyles.scss';
|
||||
|
||||
function enableScrollX() {
|
||||
return !layoutManager.desktop;
|
||||
}
|
||||
|
||||
function getBackdropShape() {
|
||||
if (enableScrollX()) {
|
||||
return 'overflowBackdrop';
|
||||
}
|
||||
return 'backdrop';
|
||||
}
|
||||
|
||||
function getPortraitShape() {
|
||||
if (enableScrollX()) {
|
||||
return 'overflowPortrait';
|
||||
}
|
||||
return 'portrait';
|
||||
}
|
||||
|
||||
function getLimit() {
|
||||
if (enableScrollX()) {
|
||||
return 12;
|
||||
|
@ -96,7 +85,7 @@ function reload(page, enableFullRender) {
|
|||
EnableImageTypes: 'Primary,Thumb'
|
||||
}).then(function (result) {
|
||||
renderItems(page, result.Items, 'upcomingTvMovieItems', null, {
|
||||
shape: getPortraitShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
preferThumb: null,
|
||||
showParentTitle: false
|
||||
});
|
||||
|
@ -147,7 +136,7 @@ function renderItems(page, items, sectionClass, overlayButton, cardOptions) {
|
|||
preferThumb: 'auto',
|
||||
inheritThumb: false,
|
||||
shape: enableScrollX() ? 'autooverflow' : 'auto',
|
||||
defaultShape: getBackdropShape(),
|
||||
defaultShape: getBackdropShape(enableScrollX()),
|
||||
showParentTitle: true,
|
||||
showTitle: true,
|
||||
centerText: true,
|
||||
|
|
|
@ -5,7 +5,7 @@ import { getParameterByName } from '../utils/url.ts';
|
|||
import Events from '../utils/events.ts';
|
||||
|
||||
function onListingsSubmitted() {
|
||||
Dashboard.navigate('livetvstatus.html');
|
||||
Dashboard.navigate('dashboard/livetv');
|
||||
}
|
||||
|
||||
function init(page, type, providerId) {
|
||||
|
|
|
@ -220,9 +220,9 @@ function getProviderName(providerId) {
|
|||
function getProviderConfigurationUrl(providerId) {
|
||||
switch (providerId.toLowerCase()) {
|
||||
case 'xmltv':
|
||||
return '#/livetvguideprovider.html?type=xmltv';
|
||||
return '#/dashboard/livetv/guide?type=xmltv';
|
||||
case 'schedulesdirect':
|
||||
return '#/livetvguideprovider.html?type=schedulesdirect';
|
||||
return '#/dashboard/livetv/guide?type=schedulesdirect';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,7 +249,7 @@ function addProvider(button) {
|
|||
}
|
||||
|
||||
function addDevice() {
|
||||
Dashboard.navigate('livetvtuner.html');
|
||||
Dashboard.navigate('dashboard/livetv/tuner');
|
||||
}
|
||||
|
||||
function showDeviceMenu(button, tunerDeviceId) {
|
||||
|
@ -274,7 +274,7 @@ function showDeviceMenu(button, tunerDeviceId) {
|
|||
break;
|
||||
|
||||
case 'edit':
|
||||
Dashboard.navigate('livetvtuner.html?id=' + tunerDeviceId);
|
||||
Dashboard.navigate('dashboard/livetv/tuner?id=' + tunerDeviceId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -290,7 +290,7 @@ function onDevicesListClick(e) {
|
|||
if (btnCardOptions) {
|
||||
showDeviceMenu(btnCardOptions, id);
|
||||
} else {
|
||||
Dashboard.navigate('livetvtuner.html?id=' + id);
|
||||
Dashboard.navigate('dashboard/livetv/tuner?id=' + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ function submitForm(page) {
|
|||
contentType: 'application/json'
|
||||
}).then(function () {
|
||||
Dashboard.processServerConfigurationUpdateResult();
|
||||
Dashboard.navigate('livetvstatus.html');
|
||||
Dashboard.navigate('dashboard/livetv');
|
||||
}, function () {
|
||||
loading.hide();
|
||||
Dashboard.alert({
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import escapeHtml from 'escape-html';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import loading from '../../components/loading/loading';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import lazyLoader from '../../components/lazyLoader/lazyLoaderIntersectionObserver';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import { appRouter } from '../../components/router/appRouter';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import lazyLoader from 'components/lazyLoader/lazyLoaderIntersectionObserver';
|
||||
import loading from 'components/loading/loading';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
import globalize from 'scripts/globalize';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { getBackdropShape, getPortraitShape } from 'utils/card';
|
||||
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
export default function (view, params, tabContent) {
|
||||
function getPageData() {
|
||||
|
@ -49,14 +52,6 @@ export default function (view, params, tabContent) {
|
|||
return !layoutManager.desktop;
|
||||
}
|
||||
|
||||
function getThumbShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
function getPortraitShape() {
|
||||
return enableScrollX() ? 'overflowPortrait' : 'portrait';
|
||||
}
|
||||
|
||||
const fillItemsContainer = (entry) => {
|
||||
const elem = entry.target;
|
||||
const id = elem.getAttribute('data-id');
|
||||
|
@ -85,7 +80,7 @@ export default function (view, params, tabContent) {
|
|||
if (viewStyle == 'Thumb') {
|
||||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: elem,
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
preferThumb: true,
|
||||
showTitle: true,
|
||||
scalable: true,
|
||||
|
@ -96,7 +91,7 @@ export default function (view, params, tabContent) {
|
|||
} else if (viewStyle == 'ThumbCard') {
|
||||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: elem,
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
preferThumb: true,
|
||||
showTitle: true,
|
||||
scalable: true,
|
||||
|
@ -107,7 +102,7 @@ export default function (view, params, tabContent) {
|
|||
} else if (viewStyle == 'PosterCard') {
|
||||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: elem,
|
||||
shape: getPortraitShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
scalable: true,
|
||||
centerText: false,
|
||||
|
@ -117,7 +112,7 @@ export default function (view, params, tabContent) {
|
|||
} else if (viewStyle == 'Poster') {
|
||||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: elem,
|
||||
shape: getPortraitShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
scalable: true,
|
||||
overlayMoreButton: true,
|
||||
allowBottomPadding: true,
|
||||
|
|
|
@ -1,35 +1,29 @@
|
|||
import escapeHtml from 'escape-html';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import inputManager from '../../scripts/inputManager';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import libraryMenu from '../../scripts/libraryMenu';
|
||||
import * as mainTabsManager from '../../components/maintabsmanager';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import dom from '../../scripts/dom';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import { LibraryTab } from '../../types/libraryTab.ts';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
||||
import '../../elements/emby-scroller/emby-scroller';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-tabs/emby-tabs';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import imageLoader from 'components/images/imageLoader';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import * as mainTabsManager from 'components/maintabsmanager';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import dom from 'scripts/dom';
|
||||
import globalize from 'scripts/globalize';
|
||||
import inputManager from 'scripts/inputManager';
|
||||
import libraryMenu from 'scripts/libraryMenu';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import { getBackdropShape, getPortraitShape } from 'utils/card';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import Events from 'utils/events';
|
||||
|
||||
import 'elements/emby-scroller/emby-scroller';
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import 'elements/emby-tabs/emby-tabs';
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
function enableScrollX() {
|
||||
return !layoutManager.desktop;
|
||||
}
|
||||
|
||||
function getPortraitShape() {
|
||||
return enableScrollX() ? 'overflowPortrait' : 'portrait';
|
||||
}
|
||||
|
||||
function getThumbShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
function loadLatest(page, userId, parentId) {
|
||||
const options = {
|
||||
IncludeItemTypes: 'Movie',
|
||||
|
@ -45,7 +39,7 @@ function loadLatest(page, userId, parentId) {
|
|||
const container = page.querySelector('#recentlyAddedItems');
|
||||
cardBuilder.buildCards(items, {
|
||||
itemsContainer: container,
|
||||
shape: getPortraitShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: allowBottomPadding,
|
||||
|
@ -87,7 +81,7 @@ function loadResume(page, userId, parentId) {
|
|||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: container,
|
||||
preferThumb: true,
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: allowBottomPadding,
|
||||
|
@ -138,7 +132,7 @@ function getRecommendationHtml(recommendation) {
|
|||
}
|
||||
|
||||
html += cardBuilder.getCardsHtml(recommendation.Items, {
|
||||
shape: getPortraitShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: allowBottomPadding,
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
import browser from '../../scripts/browser';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import inputManager from '../../scripts/inputManager';
|
||||
import loading from '../../components/loading/loading';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import dom from '../../scripts/dom';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import libraryMenu from '../../scripts/libraryMenu';
|
||||
import * as mainTabsManager from '../../components/maintabsmanager';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import { LibraryTab } from '../../types/libraryTab.ts';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import imageLoader from 'components/images/imageLoader';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import loading from 'components/loading/loading';
|
||||
import * as mainTabsManager from 'components/maintabsmanager';
|
||||
import browser from 'scripts/browser';
|
||||
import dom from 'scripts/dom';
|
||||
import globalize from 'scripts/globalize';
|
||||
import inputManager from 'scripts/inputManager';
|
||||
import libraryMenu from 'scripts/libraryMenu';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import { getSquareShape } from 'utils/card';
|
||||
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-tabs/emby-tabs';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import 'elements/emby-tabs/emby-tabs';
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
import 'styles/flexstyles.scss';
|
||||
import 'styles/scrollstyles.scss';
|
||||
|
||||
function itemsPerRow() {
|
||||
const screenWidth = dom.getWindowSize().innerWidth;
|
||||
|
@ -40,10 +42,6 @@ function enableScrollX() {
|
|||
return !layoutManager.desktop;
|
||||
}
|
||||
|
||||
function getSquareShape() {
|
||||
return enableScrollX() ? 'overflowSquare' : 'square';
|
||||
}
|
||||
|
||||
function loadLatest(page, parentId) {
|
||||
loading.show();
|
||||
const userId = ApiClient.getCurrentUserId();
|
||||
|
@ -62,7 +60,7 @@ function loadLatest(page, parentId) {
|
|||
items: items,
|
||||
showUnplayedIndicator: false,
|
||||
showLatestItemsPopup: false,
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
showParentTitle: true,
|
||||
lazy: true,
|
||||
|
@ -108,7 +106,7 @@ function loadRecentlyPlayed(page, parentId) {
|
|||
itemsContainer.innerHTML = cardBuilder.getCardsHtml({
|
||||
items: result.Items,
|
||||
showUnplayedIndicator: false,
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
showParentTitle: true,
|
||||
action: 'instantmix',
|
||||
|
@ -150,7 +148,7 @@ function loadFrequentlyPlayed(page, parentId) {
|
|||
itemsContainer.innerHTML = cardBuilder.getCardsHtml({
|
||||
items: result.Items,
|
||||
showUnplayedIndicator: false,
|
||||
shape: getSquareShape(),
|
||||
shape: getSquareShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
showParentTitle: true,
|
||||
action: 'instantmix',
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import escapeHtml from 'escape-html';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import loading from '../../components/loading/loading';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import lazyLoader from '../../components/lazyLoader/lazyLoaderIntersectionObserver';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import { appRouter } from '../../components/router/appRouter';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import lazyLoader from 'components/lazyLoader/lazyLoaderIntersectionObserver';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import loading from 'components/loading/loading';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
import globalize from 'scripts/globalize';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { getBackdropShape, getPortraitShape } from 'utils/card';
|
||||
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
export default function (view, params, tabContent) {
|
||||
function getPageData() {
|
||||
|
@ -49,14 +52,6 @@ export default function (view, params, tabContent) {
|
|||
return !layoutManager.desktop;
|
||||
}
|
||||
|
||||
function getThumbShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
function getPortraitShape() {
|
||||
return enableScrollX() ? 'overflowPortrait' : 'portrait';
|
||||
}
|
||||
|
||||
function fillItemsContainer(entry) {
|
||||
const elem = entry.target;
|
||||
const id = elem.getAttribute('data-id');
|
||||
|
@ -85,7 +80,7 @@ export default function (view, params, tabContent) {
|
|||
if (viewStyle == 'Thumb') {
|
||||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: elem,
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
preferThumb: true,
|
||||
showTitle: true,
|
||||
scalable: true,
|
||||
|
@ -96,7 +91,7 @@ export default function (view, params, tabContent) {
|
|||
} else if (viewStyle == 'ThumbCard') {
|
||||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: elem,
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
preferThumb: true,
|
||||
showTitle: true,
|
||||
scalable: true,
|
||||
|
@ -107,7 +102,7 @@ export default function (view, params, tabContent) {
|
|||
} else if (viewStyle == 'PosterCard') {
|
||||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: elem,
|
||||
shape: getPortraitShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
scalable: true,
|
||||
centerText: false,
|
||||
|
@ -117,7 +112,7 @@ export default function (view, params, tabContent) {
|
|||
} else if (viewStyle == 'Poster') {
|
||||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: elem,
|
||||
shape: getPortraitShape(),
|
||||
shape: getPortraitShape(enableScrollX()),
|
||||
scalable: true,
|
||||
showTitle: true,
|
||||
centerText: true,
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
import autoFocuser from 'components/autoFocuser';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import loading from 'components/loading/loading';
|
||||
import * as mainTabsManager from 'components/maintabsmanager';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import dom from 'scripts/dom';
|
||||
import globalize from 'scripts/globalize';
|
||||
import inputManager from 'scripts/inputManager';
|
||||
import libraryMenu from 'scripts/libraryMenu';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import { getBackdropShape } from 'utils/card';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import Events from 'utils/events';
|
||||
|
||||
import inputManager from '../../scripts/inputManager';
|
||||
import libraryMenu from '../../scripts/libraryMenu';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import loading from '../../components/loading/loading';
|
||||
import dom from '../../scripts/dom';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
import * as mainTabsManager from '../../components/maintabsmanager';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import { LibraryTab } from '../../types/libraryTab.ts';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import Events from '../../utils/events.ts';
|
||||
import autoFocuser from '../../components/autoFocuser';
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
import 'styles/scrollstyles.scss';
|
||||
|
||||
function getTabs() {
|
||||
return [{
|
||||
|
@ -119,7 +121,7 @@ function loadResume(view, userId, parentId) {
|
|||
itemsContainer: container,
|
||||
preferThumb: true,
|
||||
inheritThumb: !userSettings.useEpisodeImagesInNextUpAndResume(),
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
scalable: true,
|
||||
overlayPlayButton: true,
|
||||
allowBottomPadding: allowBottomPadding,
|
||||
|
@ -217,10 +219,6 @@ function enableScrollX() {
|
|||
return !layoutManager.desktop;
|
||||
}
|
||||
|
||||
function getThumbShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
export default function (view, params) {
|
||||
function onBeforeTabChange(e) {
|
||||
preLoadTab(view, parseInt(e.detail.selectedTabIndex, 10));
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import layoutManager from '../../components/layoutManager';
|
||||
import loading from '../../components/loading/loading';
|
||||
import datetime from '../../scripts/datetime';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import imageLoader from 'components/images/imageLoader';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import loading from 'components/loading/loading';
|
||||
import datetime from 'scripts/datetime';
|
||||
import globalize from 'scripts/globalize';
|
||||
import { getBackdropShape } from 'utils/card';
|
||||
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
|
||||
import 'styles/scrollstyles.scss';
|
||||
|
||||
function getUpcomingPromise(context, params) {
|
||||
loading.show();
|
||||
|
@ -40,10 +43,6 @@ function enableScrollX() {
|
|||
return !layoutManager.desktop;
|
||||
}
|
||||
|
||||
function getThumbShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
function renderUpcoming(elem, items) {
|
||||
const groups = [];
|
||||
let currentGroupName = '';
|
||||
|
@ -105,7 +104,7 @@ function renderUpcoming(elem, items) {
|
|||
html += cardBuilder.getCardsHtml({
|
||||
items: group.items,
|
||||
showLocationTypeIndicator: false,
|
||||
shape: getThumbShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
preferThumb: true,
|
||||
lazy: true,
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
</div>
|
||||
<div class="adminSection verticalSection verticalSection-extrabottompadding hide">
|
||||
<h2 class="sectionTitle" style="padding-left:.25em;">${HeaderAdmin}</h2>
|
||||
<a is="emby-linkbutton" href="#/dashboard.html" style="display:block;padding:0;margin:0;" class="listItem-border">
|
||||
<a is="emby-linkbutton" href="#/dashboard" style="display:block;padding:0;margin:0;" class="listItem-border">
|
||||
<div class="listItem">
|
||||
<span class="material-icons listItemIcon listItemIcon-transparent dashboard" aria-hidden="true"></span>
|
||||
<div class="listItemBody">
|
||||
|
@ -85,7 +85,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a is="emby-linkbutton" href="#/edititemmetadata.html" style="display:block;padding:0;margin:0;" class="listItem-border">
|
||||
<a is="emby-linkbutton" href="#/metadata" style="display:block;padding:0;margin:0;" class="listItem-border">
|
||||
<div class="listItem">
|
||||
<span class="material-icons listItemIcon listItemIcon-transparent mode_edit" aria-hidden="true"></span>
|
||||
<div class="listItemBody">
|
||||
|
|
|
@ -327,8 +327,8 @@ function refreshLibraryInfoInDrawer(user) {
|
|||
html += '<h3 class="sidebarHeader">';
|
||||
html += globalize.translate('HeaderAdmin');
|
||||
html += '</h3>';
|
||||
html += `<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder lnkManageServer" data-itemid="dashboard" href="#/dashboard.html"><span class="material-icons navMenuOptionIcon dashboard" aria-hidden="true"></span><span class="navMenuOptionText">${globalize.translate('TabDashboard')}</span></a>`;
|
||||
html += `<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder editorViewMenu" data-itemid="editor" href="#/edititemmetadata.html"><span class="material-icons navMenuOptionIcon mode_edit" aria-hidden="true"></span><span class="navMenuOptionText">${globalize.translate('Metadata')}</span></a>`;
|
||||
html += `<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder lnkManageServer" data-itemid="dashboard" href="#/dashboard"><span class="material-icons navMenuOptionIcon dashboard" aria-hidden="true"></span><span class="navMenuOptionText">${globalize.translate('TabDashboard')}</span></a>`;
|
||||
html += `<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder editorViewMenu" data-itemid="editor" href="#/metadata"><span class="material-icons navMenuOptionIcon mode_edit" aria-hidden="true"></span><span class="navMenuOptionText">${globalize.translate('Metadata')}</span></a>`;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
|
@ -376,249 +376,6 @@ function refreshLibraryInfoInDrawer(user) {
|
|||
}
|
||||
}
|
||||
|
||||
function refreshDashboardInfoInDrawer(page, apiClient) {
|
||||
currentDrawerType = 'admin';
|
||||
loadNavDrawer();
|
||||
|
||||
if (navDrawerScrollContainer.querySelector('.adminDrawerLogo')) {
|
||||
updateDashboardMenuSelectedItem(page);
|
||||
} else {
|
||||
createDashboardMenu(page, apiClient);
|
||||
}
|
||||
}
|
||||
|
||||
function isUrlInCurrentView(url) {
|
||||
return window.location.href.toString().toLowerCase().indexOf(url.toLowerCase()) !== -1;
|
||||
}
|
||||
|
||||
function updateDashboardMenuSelectedItem(page) {
|
||||
const links = navDrawerScrollContainer.querySelectorAll('.navMenuOption');
|
||||
const currentViewId = page.id;
|
||||
|
||||
for (let i = 0, length = links.length; i < length; i++) {
|
||||
let link = links[i];
|
||||
let selected = false;
|
||||
let pageIds = link.getAttribute('data-pageids');
|
||||
|
||||
if (pageIds) {
|
||||
pageIds = pageIds.split('|');
|
||||
selected = pageIds.indexOf(currentViewId) != -1;
|
||||
}
|
||||
|
||||
let pageUrls = link.getAttribute('data-pageurls');
|
||||
|
||||
if (pageUrls) {
|
||||
pageUrls = pageUrls.split('|');
|
||||
selected = pageUrls.filter(isUrlInCurrentView).length > 0;
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
link.classList.add('navMenuOption-selected');
|
||||
let title = '';
|
||||
link = link.querySelector('.navMenuOptionText') || link;
|
||||
title += (link.innerText || link.textContent).trim();
|
||||
LibraryMenu.setTitle(title);
|
||||
} else {
|
||||
link.classList.remove('navMenuOption-selected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createToolsMenuList(pluginItems) {
|
||||
const links = [{
|
||||
name: globalize.translate('TabServer')
|
||||
}, {
|
||||
name: globalize.translate('TabDashboard'),
|
||||
href: '#/dashboard.html',
|
||||
pageIds: ['dashboardPage'],
|
||||
icon: 'dashboard'
|
||||
}, {
|
||||
name: globalize.translate('General'),
|
||||
href: '#/dashboardgeneral.html',
|
||||
pageIds: ['dashboardGeneralPage'],
|
||||
icon: 'settings'
|
||||
}, {
|
||||
name: globalize.translate('HeaderUsers'),
|
||||
href: '#/userprofiles.html',
|
||||
pageIds: ['userProfilesPage', 'newUserPage', 'editUserPage', 'userLibraryAccessPage', 'userParentalControlPage', 'userPasswordPage'],
|
||||
icon: 'people'
|
||||
}, {
|
||||
name: globalize.translate('HeaderLibraries'),
|
||||
href: '#/library.html',
|
||||
pageIds: ['mediaLibraryPage', 'librarySettingsPage', 'libraryDisplayPage', 'metadataImagesConfigurationPage', 'metadataNfoPage'],
|
||||
icon: 'folder'
|
||||
}, {
|
||||
name: globalize.translate('TitlePlayback'),
|
||||
icon: 'play_arrow',
|
||||
href: '#/encodingsettings.html',
|
||||
pageIds: ['encodingSettingsPage', 'playbackConfigurationPage', 'streamingSettingsPage']
|
||||
}];
|
||||
addPluginPagesToMainMenu(links, pluginItems, 'server');
|
||||
links.push({
|
||||
divider: true,
|
||||
name: globalize.translate('HeaderDevices')
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('HeaderDevices'),
|
||||
href: '#/devices.html',
|
||||
pageIds: ['devicesPage', 'devicePage'],
|
||||
icon: 'devices'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('HeaderActivity'),
|
||||
href: '#/dashboard/activity',
|
||||
pageIds: ['serverActivityPage'],
|
||||
icon: 'assessment'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('DLNA'),
|
||||
href: '#/dlnasettings.html',
|
||||
pageIds: ['dlnaSettingsPage', 'dlnaProfilesPage', 'dlnaProfilePage'],
|
||||
icon: 'input'
|
||||
});
|
||||
links.push({
|
||||
divider: true,
|
||||
name: globalize.translate('LiveTV')
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('LiveTV'),
|
||||
href: '#/livetvstatus.html',
|
||||
pageIds: ['liveTvStatusPage', 'liveTvTunerPage'],
|
||||
icon: 'live_tv'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('HeaderDVR'),
|
||||
href: '#/livetvsettings.html',
|
||||
pageIds: ['liveTvSettingsPage'],
|
||||
icon: 'dvr'
|
||||
});
|
||||
addPluginPagesToMainMenu(links, pluginItems, 'livetv');
|
||||
links.push({
|
||||
divider: true,
|
||||
name: globalize.translate('TabAdvanced')
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('TabNetworking'),
|
||||
icon: 'cloud',
|
||||
href: '#/networking.html',
|
||||
pageIds: ['networkingPage']
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('HeaderApiKeys'),
|
||||
icon: 'vpn_key',
|
||||
href: '#/apikeys.html',
|
||||
pageIds: ['apiKeysPage']
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('TabLogs'),
|
||||
href: '#/log.html',
|
||||
pageIds: ['logPage'],
|
||||
icon: 'bug_report'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('Notifications'),
|
||||
icon: 'notifications',
|
||||
href: '#/notificationsettings.html'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('TabPlugins'),
|
||||
icon: 'shopping_cart',
|
||||
href: '#/installedplugins.html',
|
||||
pageIds: ['pluginsPage', 'pluginCatalogPage']
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('TabScheduledTasks'),
|
||||
href: '#/scheduledtasks.html',
|
||||
pageIds: ['scheduledTasksPage', 'scheduledTaskPage'],
|
||||
icon: 'schedule'
|
||||
});
|
||||
if (hasUnsortedPlugins(pluginItems)) {
|
||||
links.push({
|
||||
divider: true,
|
||||
name: globalize.translate('TabPlugins')
|
||||
});
|
||||
addPluginPagesToMainMenu(links, pluginItems);
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
function hasUnsortedPlugins(pluginItems) {
|
||||
for (const pluginItem of pluginItems) {
|
||||
if (pluginItem.EnableInMainMenu && pluginItem.MenuSection === undefined) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function addPluginPagesToMainMenu(links, pluginItems, section) {
|
||||
for (const pluginItem of pluginItems) {
|
||||
if (pluginItem.EnableInMainMenu && pluginItem.MenuSection === section) {
|
||||
links.push({
|
||||
name: pluginItem.DisplayName,
|
||||
icon: pluginItem.MenuIcon || 'folder',
|
||||
href: Dashboard.getPluginUrl(pluginItem.Name),
|
||||
pageUrls: [Dashboard.getPluginUrl(pluginItem.Name)]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getToolsMenuLinks(apiClient) {
|
||||
return apiClient.getJSON(apiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration&EnableInMainMenu=true').then(createToolsMenuList, function () {
|
||||
return createToolsMenuList([]);
|
||||
});
|
||||
}
|
||||
|
||||
function getToolsLinkHtml(item) {
|
||||
let menuHtml = '';
|
||||
let pageIds = item.pageIds ? item.pageIds.join('|') : '';
|
||||
pageIds = pageIds ? ' data-pageids="' + pageIds + '"' : '';
|
||||
let pageUrls = item.pageUrls ? item.pageUrls.join('|') : '';
|
||||
pageUrls = pageUrls ? ' data-pageurls="' + pageUrls + '"' : '';
|
||||
menuHtml += '<a is="emby-linkbutton" class="navMenuOption" href="' + item.href + '"' + pageIds + pageUrls + '>';
|
||||
|
||||
if (item.icon) {
|
||||
menuHtml += '<span class="material-icons navMenuOptionIcon ' + item.icon + '" aria-hidden="true"></span>';
|
||||
}
|
||||
|
||||
menuHtml += '<span class="navMenuOptionText">';
|
||||
menuHtml += escapeHtml(item.name);
|
||||
menuHtml += '</span>';
|
||||
return menuHtml + '</a>';
|
||||
}
|
||||
|
||||
function getToolsMenuHtml(apiClient) {
|
||||
return getToolsMenuLinks(apiClient).then(function (items) {
|
||||
let menuHtml = '';
|
||||
menuHtml += '<div class="drawerContent">';
|
||||
|
||||
for (const item of items) {
|
||||
if (item.href) {
|
||||
menuHtml += getToolsLinkHtml(item);
|
||||
} else if (item.name) {
|
||||
menuHtml += '<h3 class="sidebarHeader">';
|
||||
menuHtml += escapeHtml(item.name);
|
||||
menuHtml += '</h3>';
|
||||
}
|
||||
}
|
||||
|
||||
return menuHtml + '</div>';
|
||||
});
|
||||
}
|
||||
|
||||
function createDashboardMenu(page, apiClient) {
|
||||
return getToolsMenuHtml(apiClient).then(function (toolsMenuHtml) {
|
||||
let html = '';
|
||||
html += '<a class="adminDrawerLogo clearLink" is="emby-linkbutton" href="#/home.html">';
|
||||
html += '<img src="assets/img/icon-transparent.png" />';
|
||||
html += '</a>';
|
||||
html += toolsMenuHtml;
|
||||
navDrawerScrollContainer.innerHTML = html;
|
||||
updateDashboardMenuSelectedItem(page);
|
||||
});
|
||||
}
|
||||
|
||||
function onSidebarLinkClick() {
|
||||
const section = this.getElementsByClassName('sectionName')[0];
|
||||
const text = section ? section.innerHTML : this.innerHTML;
|
||||
|
@ -1026,15 +783,8 @@ pageClassOn('pageshow', 'page', function (e) {
|
|||
const isDashboardPage = page.classList.contains('type-interior');
|
||||
const isHomePage = page.classList.contains('homePage');
|
||||
const isLibraryPage = !isDashboardPage && page.classList.contains('libraryPage');
|
||||
const apiClient = getCurrentApiClient();
|
||||
|
||||
if (isDashboardPage) {
|
||||
if (mainDrawerButton) {
|
||||
mainDrawerButton.classList.remove('hide');
|
||||
}
|
||||
|
||||
refreshDashboardInfoInDrawer(page, apiClient);
|
||||
} else {
|
||||
if (!isDashboardPage) {
|
||||
if (mainDrawerButton) {
|
||||
if (enableLibraryNavDrawer || (isHomePage && enableLibraryNavDrawerHome)) {
|
||||
mainDrawerButton.classList.remove('hide');
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import layoutManager from '../components/layoutManager';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import { getBackdropShape } from 'utils/card';
|
||||
|
||||
import datetime from './datetime';
|
||||
import cardBuilder from '../components/cardbuilder/cardBuilder';
|
||||
|
||||
function enableScrollX() {
|
||||
return !layoutManager.desktop;
|
||||
}
|
||||
|
||||
function getBackdropShape() {
|
||||
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
|
||||
}
|
||||
|
||||
function getTimersHtml(timers, options) {
|
||||
options = options || {};
|
||||
|
||||
|
@ -78,7 +76,7 @@ function getTimersHtml(timers, options) {
|
|||
|
||||
html += cardBuilder.getCardsHtml({
|
||||
items: group.items,
|
||||
shape: getBackdropShape(),
|
||||
shape: getBackdropShape(enableScrollX()),
|
||||
showTitle: true,
|
||||
showParentTitleOrTitle: true,
|
||||
showAirTime: true,
|
||||
|
|
|
@ -528,7 +528,7 @@
|
|||
"Screenshots": "Снимки на екрана",
|
||||
"Search": "Търсене",
|
||||
"SearchForCollectionInternetMetadata": "Търсене в интернет за картини и метаданни",
|
||||
"SearchForMissingMetadata": "Търсене за лисващи метаданни",
|
||||
"SearchForMissingMetadata": "Търсене за липсващи метаданни",
|
||||
"SearchForSubtitles": "Търсене на субтитри",
|
||||
"SendMessage": "Изпращане на съобщение",
|
||||
"SeriesYearToPresent": "{0} - Настояще",
|
||||
|
@ -1166,11 +1166,11 @@
|
|||
"MessageUnsetContentHelp": "Съдържанието ще се показва като обикновени папки. За най-добри резултати използвайте мениджъра на метаданни, за да зададете типовете съдържание на подпапките.",
|
||||
"MessageUnableToConnectToServer": "В момента не можем да се свържем с избрания сървър. Моля, уверете се, че работи и опитайте отново.",
|
||||
"MessageReenableUser": "Вижте по-долу, за да активирате отново",
|
||||
"MessagePluginInstallDisclaimer": "Приставките, създадени от членове на общността, са чудесен начин да подобрите изживяването с Джелифин чрез допълнителните функции и предимства.Преди да инсталирате, имайте предвид ефектите, които те могат да имат върху вашия Джелифин сървър, като по-дълго време за сканиране на библиотеки, допълнителна обработка на заден фон и намалена стабилност на системата.",
|
||||
"MessagePluginInstallDisclaimer": "ПРЕДУПРЕЖДЕНИЕ: Инсталирането на плъгин на трета страна носи рискове. Може да съдържа нестабилен или злонамерен код и може да се промени по всяко време. Инсталирайте само плъгини от автори на които имате доверие! Имайте предвид потенциалните ефекти, които може да има, включително заявки към външни услуги, по-дълги сканирания на библиотеки или допълнителна фонова обработка.",
|
||||
"MessagePluginConfigurationRequiresLocalAccess": "За да конфигурирате тази приставка, моля, впишете се директно в локалния си сървър.",
|
||||
"MessagePleaseWait": "Моля,изчакайте. Това може да отнеме минута.",
|
||||
"MessagePlayAccessRestricted": "Възпроизвеждането на това съдържание в момента е ограничено.Моля, свържете се с администратора на вашия сървър за повече информация.",
|
||||
"MessagePasswordResetForUsers": "Следните потребители са занулили паролите си.Те вече могат да влязат с пин кодовете, използвани за извършване на нулирането.",
|
||||
"MessagePasswordResetForUsers": "Следните потребители са занулили паролите си.Те вече могат да влязат с ПИН кодовете, използвани за извършване на нулирането.",
|
||||
"MessageNoTrailersFound": "За да подобрите филмовото изживяване инсталирайте канал за трейлъри,може да подредите няколко канала в библиотека.",
|
||||
"MessageNoServersAvailable": "Не са намерени сървъри, използващи функцията за автоматично откриване на сървър.",
|
||||
"MessageNoMovieSuggestionsAvailable": "Понастоящем няма предложени филми. Започнете да гледате и оценявате филмите си, а след това се върнете, за да видите препоръките си.",
|
||||
|
@ -1452,7 +1452,7 @@
|
|||
"LabelAlbumArtMaxResHelp": "Максимална резолюция на изображенията предоставена чрез \"upnp:albumArtURI\" полето.",
|
||||
"KnownProxiesHelp": "Списък от IP ареси или хост имена на известни прокси сървъри, разделени със запетая, използвани при свързване с Jellyfin сървър. Това е задължително за да се използва правилнен \"X-Forwarded-For\" хедър. Изисква рестартиране след прилагане.",
|
||||
"HomeVideosPhotos": "Домашни видеа и снимки",
|
||||
"DirectPlayHelp": "Основният файл е напълно съвместим с този клиент, което значи че го получавате без модификации.",
|
||||
"DirectPlayHelp": "Премахване на изображение",
|
||||
"AllowTonemappingHelp": "Тоналното картографиране може да трансформира динамичния обхват на видеото от HDR към SDR, като същевременно запазва детайлите и цветовете на изображението, които са много важна информация за представяне на оригиналната сцена. В момента работи само с 10-битови HDR10,HLG и DoVi видеоклипове. Това изисква съответното време за изпълнение от OpenCL или CUDA.",
|
||||
"LabelMaxAudiobookResumeHelp": "Приема се ,че файловете се възпроизведени до края , ако се спре след като оставащото време е по-малко от тази стойност.",
|
||||
"Experimental": "Експериментални",
|
||||
|
@ -1478,5 +1478,10 @@
|
|||
"EnableAudioNormalizationHelp": "Нормализацията на звука ще усили сигналът за да поддържа средните честоти на желано ниво (-18dB).",
|
||||
"EnableAudioNormalization": "Нормализация на звука",
|
||||
"Unknown": "Неизвестен",
|
||||
"LabelThrottleDelaySeconds": "Ограничи след"
|
||||
"LabelThrottleDelaySeconds": "Ограничи след",
|
||||
"GetThePlugin": "Вземете приставката",
|
||||
"LabelLocalCustomCss": "Персонализиран CSS код за стилизиране, който се отнася само за този клиент. Може да искате да деактивирате персонализирания CSS код на сървъра.",
|
||||
"LabelOriginalName": "Оригинално име",
|
||||
"LabelQuickConnectCode": "Код за бързо свързване",
|
||||
"LabelMaxVideoResolution": "Максимално разрешена разделителна способност на транскодиране на видео"
|
||||
}
|
||||
|
|
|
@ -1377,7 +1377,7 @@
|
|||
"LabelColorSpace": "Farbraum",
|
||||
"MediaInfoColorSpace": "Farbraum",
|
||||
"VideoAudio": "Videoton",
|
||||
"AllowTonemappingHelp": "Tone-Mapping kann den Dynamikumfang eines Videos von HDR nach SDR wandeln und dabei die für die Darstellung der Originalszene sehr wichtigen Bilddetails und Farben beibehalten. Dies funktioniert zurzeit nur bei HDR10-, HLG- und Dolby-Vision-Videos und benötigt die entsprechende OpenCL- oder CUDA-Laufzeitumgebung.",
|
||||
"AllowTonemappingHelp": "Tone-Mapping kann den Dynamikumfang eines Videos von HDR nach SDR wandeln und dabei die für die Darstellung der Originalszene sehr wichtigen Bilddetails und Farben beibehalten. Dies funktioniert zurzeit nur bei HDR10, HLG und Dolby-Vision Videos und benötigt die entsprechende OpenCL- oder CUDA-Laufzeitumgebung.",
|
||||
"TonemappingRangeHelp": "Wähle den Ausgabefarbraum aus. Auto ist derselbe wie der Eingabefarbraum.",
|
||||
"TonemappingAlgorithmHelp": "Das Tone-Mapping kann fein abgestimmt werden. Wenn du mit diesen Optionen nicht vertraut bist, behalte einfach den Standardwert bei. Der empfohlene Wert ist \"BT.2390\".",
|
||||
"LabelTonemappingAlgorithm": "Wähle den zu verwendenden Tone-Mapping-Algorithmus aus",
|
||||
|
@ -1407,7 +1407,7 @@
|
|||
"QuickConnectDescription": "Für das Einloggen mit Quick Connect wähle den 'Quick Connect'-Knopf auf deinem Gerät, mit dem du dich anmelden möchtest, und gib den unten angezeigten Code ein.",
|
||||
"QuickConnectDeactivated": "Quick Connect wurde deaktiviert, bevor der Login verifiziert werden konnte",
|
||||
"QuickConnectAuthorizeFail": "Unbekannter Quick Connect-Code",
|
||||
"QuickConnectAuthorizeSuccess": "Anfrage autorisiert",
|
||||
"QuickConnectAuthorizeSuccess": "Das Gerät wurde erfolgreich authentifiziert!",
|
||||
"QuickConnectAuthorizeCode": "Login Code {0} eingeben",
|
||||
"QuickConnectActivationSuccessful": "Erfolgreich aktiviert",
|
||||
"EnableQuickConnect": "Quick Connect auf diesem Server aktivieren",
|
||||
|
@ -1733,7 +1733,7 @@
|
|||
"PasswordRequiredForAdmin": "Für Admin Konten wird ein Passwort benötigt.",
|
||||
"LabelEnableLUFSScan": "LUFS-Scan aktivieren",
|
||||
"LabelSyncPlayNoGroups": "Keine Gruppen verfügbar",
|
||||
"LabelEnableLUFSScanHelp": "Aktiviert den LUFS-Scan für Musik (Dies erfordert mehr Zeit und Ressourcen).",
|
||||
"LabelEnableLUFSScanHelp": "Clients können die Audio Wiedergabe normalisieren um die selbe Lautstärke für mehrere Stücke zu bekommen.\nDie verlängert den Bibiliotheks Scan und benötigt mehr Ressourcen.",
|
||||
"Notifications": "Benachrichtigungen",
|
||||
"NotificationsMovedMessage": "Die Benachrichtigungsfunktion wurde zum Webhook Plugin verschoben.",
|
||||
"EnableAudioNormalizationHelp": "Die Audionormalisierung fügt eine konstante Verstärkung hinzu, um den Durchschnitt auf einem gewünschten Pegel zu halten (-18 dB).",
|
||||
|
@ -1769,5 +1769,12 @@
|
|||
"GoHome": "Startseite",
|
||||
"AiTranslated": "AI übersetzt",
|
||||
"MachineTranslated": "maschinenübersetzt",
|
||||
"AllowAv1Encoding": "Encodierung ins AV1 Format erlauben"
|
||||
"AllowAv1Encoding": "Encodierung ins AV1 Format erlauben",
|
||||
"LabelIsHearingImpaired": "Für Hörgeschädigte (SDH)",
|
||||
"LabelBackdropScreensaverInterval": "Hintergrund-Bildschirmschoner-Intervall",
|
||||
"BackdropScreensaver": "Hintergrund Bildschirmschoner",
|
||||
"ForeignPartsOnly": "Erzwungen/Nur ausländische Teile",
|
||||
"HearingImpairedShort": "BaFa/SDH",
|
||||
"HeaderGuestCast": "Gast Stars",
|
||||
"LabelBackdropScreensaverIntervalHelp": "Die Zeit in Sekunden zwischen dem Wechsel verschiedener Hintergrundbilder im Bildschirmschoner"
|
||||
}
|
||||
|
|
|
@ -1773,5 +1773,6 @@
|
|||
"MachineTranslated": "Traducido por Máquina",
|
||||
"ForeignPartsOnly": "Solamente partes Forzadas/Foráneas",
|
||||
"HearingImpairedShort": "HI/SDH",
|
||||
"HeaderGuestCast": "Estrellas Invitadas"
|
||||
"HeaderGuestCast": "Estrellas Invitadas",
|
||||
"LabelIsHearingImpaired": "Para personas con discapacidad auditiva (SDH)"
|
||||
}
|
||||
|
|
|
@ -1775,5 +1775,6 @@
|
|||
"AiTranslated": "Traducido por IA",
|
||||
"MachineTranslated": "Traducido por Máquina",
|
||||
"HeaderGuestCast": "Estrellas Invitadas",
|
||||
"ForeignPartsOnly": "Partes Forzadas/Foráneas solamente"
|
||||
"ForeignPartsOnly": "Partes Forzadas/Foráneas solamente",
|
||||
"LabelIsHearingImpaired": "Para personas con discapacidad auditiva (SDH)"
|
||||
}
|
||||
|
|
216
src/strings/fo.json
Normal file
216
src/strings/fo.json
Normal file
|
@ -0,0 +1,216 @@
|
|||
{
|
||||
"AccessRestrictedTryAgainLater": "Atgongd er avmarkað. Vinaliga royn aftur seinni.",
|
||||
"Actor": "Sjónleikari",
|
||||
"Add": "Legg afturat",
|
||||
"AddedOnValue": "{0} lagt afturat",
|
||||
"AddToCollection": "Koyr í samling",
|
||||
"AddToFavorites": "Legg til yndislistan",
|
||||
"AddToPlaylist": "Legg til spælilistan",
|
||||
"Alerts": "Ávaringar",
|
||||
"All": "Øll",
|
||||
"AllEpisodes": "Allir partar",
|
||||
"AllLanguages": "Øll tungumál",
|
||||
"LabelTonemappingMode": "Tónaavmyndingarháttur",
|
||||
"HearingImpairedShort": "Hoyriveik/SDH",
|
||||
"MachineTranslated": "Maskin týðing",
|
||||
"AiTranslated": "Vitlíkistýðing",
|
||||
"AllowAv1Encoding": "Loyva koding í AV1 bygnaði",
|
||||
"LabelIsHearingImpaired": "Til hoyriveik (SDH)",
|
||||
"Unknown": "Ókend",
|
||||
"TonemappingModeHelp": "Vel tónaavmyndingarháttin. Um tú verður fyri útblástum hálýsingum, royn so heldur RGB-støðuna.",
|
||||
"Unreleased": "Ikki latið út enn",
|
||||
"AlbumArtist": "Album Listafólk",
|
||||
"AllChannels": "Allar rásir",
|
||||
"AllComplexFormats": "Allar Kompleksu Formatir (ASS, SSA, VobSub, PGS, SUB, IDX, ...)",
|
||||
"Directors": "Leikstjórar",
|
||||
"AgeValue": "({0} ára gamalt)",
|
||||
"AllLibraries": "Øll søvn",
|
||||
"Artist": "Listafólk",
|
||||
"Artists": "Listafólk",
|
||||
"Books": "Bøkur",
|
||||
"Composer": "Tónaskald",
|
||||
"DailyAt": "Dagligani kl. {0}",
|
||||
"DashboardVersionNumber": "Útgáva: {0}",
|
||||
"DeathDateValue": "Deyð(ur): {0}",
|
||||
"Digital": "Talgilt",
|
||||
"Director": "Leikstjóri",
|
||||
"Friday": "Fríggjadag",
|
||||
"HeaderAdmin": "Umsiting",
|
||||
"HeaderDevices": "Eindir",
|
||||
"HeaderError": "Feilur",
|
||||
"HeaderForKids": "Fyri Børn",
|
||||
"AllowSegmentDeletion": "Strika partar",
|
||||
"LabelThrottleDelaySeconds": "Kyrkja",
|
||||
"AllowMediaConversion": "miðla",
|
||||
"AllowOnTheFlySubtitleExtraction": "undirteksta",
|
||||
"AllowCollectionManagement": "brúkara",
|
||||
"AllowFfmpegThrottling": "Kyrkja",
|
||||
"AllowFfmpegThrottlingHelp": "umkoding avspæling avspælingar",
|
||||
"AllowSegmentDeletionHelp": "avspælingar",
|
||||
"LabelSegmentKeepSecondsHelp": "Kyrkja parta.",
|
||||
"AllowHWTranscodingHelp": "avkoda ambætarinum",
|
||||
"AllowMediaConversionHelp": "miðla",
|
||||
"AllowOnTheFlySubtitleExtractionHelp": "Íkervnir undirtekstir avspæling íkervnar undirtekstir",
|
||||
"AllowRemoteAccess": "ambætaran",
|
||||
"HeaderPassword": "Loyniorð",
|
||||
"HeaderLibraries": "Søvn",
|
||||
"HeaderParentalRatings": "Aldursmark",
|
||||
"HeaderSecondsValue": "{0} sekund",
|
||||
"HeaderSendMessage": "Send boð",
|
||||
"Kids": "Børn",
|
||||
"LabelArtists": "Listafólk",
|
||||
"LabelCountry": "Land",
|
||||
"LabelDeveloper": "Mennari",
|
||||
"Absolute": "Absolut",
|
||||
"AddToPlayQueue": "Legg til spæl bíðirøð",
|
||||
"AirDate": "Útgávu ár",
|
||||
"Aired": "Útgivið",
|
||||
"Album": "Album",
|
||||
"Albums": "Album",
|
||||
"LabelSegmentKeepSeconds": "Tíð at halda petti",
|
||||
"AroundTime": "Umleið {0}",
|
||||
"Ascending": "Hækkandi",
|
||||
"SearchForMissingMetadata": "Leita eftir manglandi metadata",
|
||||
"SearchForSubtitles": "Leita eftir undirtekstið",
|
||||
"SearchResults": "Leitiúrslit",
|
||||
"SelectServer": "Vel ambætara",
|
||||
"SendMessage": "Send boð",
|
||||
"Season": "Sesong",
|
||||
"SeriesDisplayOrderHelp": "Raða partar eftir dato, DVD ordan ella absolut nummerering.",
|
||||
"ServerNameIsRestarting": "Ambætarin á {0} endurbyrjar.",
|
||||
"ServerNameIsShuttingDown": "Ambætarin á {0} sløknar.",
|
||||
"ServerUpdateNeeded": "Hesin ambætarin má dagførast. Fyri at heinta nýggjastu útgávuna, vinarliga vitja {0}.",
|
||||
"Share": "Býta",
|
||||
"ShowIndicatorsFor": "Vís indikator fyri",
|
||||
"ShowLess": "Vís minni",
|
||||
"ShowMore": "Vís meiri",
|
||||
"ShowParentImages": "Vís røð myndir",
|
||||
"Shuffle": "Blanda",
|
||||
"Shows": "Røðir",
|
||||
"ShowTitle": "Vís heitið",
|
||||
"Small": "Lítið",
|
||||
"SmallCaps": "Lítlir stavir",
|
||||
"Smaller": "Minni",
|
||||
"Songs": "Sangir",
|
||||
"Sort": "Skipa",
|
||||
"SpecialFeatures": "Serstøk eyðkenni",
|
||||
"StopRecording": "Steðga upptøku",
|
||||
"Studio": "Filmsfelag",
|
||||
"Studios": "Filmsfeløg",
|
||||
"Subtitle": "Undirtekstur",
|
||||
"SubtitleCyan": "Blágrønur",
|
||||
"SubtitleGreen": "Grønt",
|
||||
"SubtitleOffset": "Undirtekstur offset",
|
||||
"SubtitleRed": "Reytt",
|
||||
"Subtitles": "Undirtekstur",
|
||||
"Suggestions": "Uppskot",
|
||||
"Sync": "Synkronisera",
|
||||
"SyncPlayGroupDefaultTitle": "{0}'sa bólkur",
|
||||
"TabAdvanced": "Framkomin",
|
||||
"TabCatalog": "Skrá",
|
||||
"TabDashboard": "Kunningarbretti",
|
||||
"TabDirectPlay": "Beinleiðis avspæling",
|
||||
"TabLatest": "Lagt afturat fyri stuttum",
|
||||
"TabLogs": "Gerðalistar",
|
||||
"TabNetworking": "Net",
|
||||
"TabNfoSettings": "NFO stillingar",
|
||||
"TabPlugins": "Ískoytisforrit",
|
||||
"TabProfiles": "Vangamyndir",
|
||||
"TabRepositories": "Goymslur",
|
||||
"TabServer": "Ambætari",
|
||||
"TabStreaming": "Stroyming",
|
||||
"TabUpcoming": "Komandi",
|
||||
"Tags": "Frámerkir",
|
||||
"TellUsAboutYourself": "Fortel um teg sjálvan",
|
||||
"ThemeSongs": "Tema sangir",
|
||||
"ThemeVideos": "Tema sjónbond",
|
||||
"ThumbCard": "Tummlakort",
|
||||
"TitleHardwareAcceleration": "Tólbúnað ferðøking",
|
||||
"TitleHostingSettings": "Hýsingastillingar",
|
||||
"TrackCount": "{0} spor",
|
||||
"Trailers": "Forfilmar",
|
||||
"Tuesday": "Týsdag",
|
||||
"TV": "Sjónvarp",
|
||||
"TypeOptionPluralAudio": "Ljóð",
|
||||
"TypeOptionPluralBoxSet": "Boks sett",
|
||||
"TypeOptionPluralMusicAlbum": "Tónleika album",
|
||||
"TypeOptionPluralMusicVideo": "Tónleikasjónbond",
|
||||
"TypeOptionPluralSeason": "Sesongir",
|
||||
"TypeOptionPluralVideo": "Sjónbond",
|
||||
"Typewriter": "Skrivimaskina",
|
||||
"Uniform": "Einsháttað",
|
||||
"Unmute": "Skrúva ljóðið upp",
|
||||
"Unrated": "Eingin meting",
|
||||
"Up": "Upp",
|
||||
"ValueAudioCodec": "Ljóð codec: {0}",
|
||||
"ValueCodec": "Codec: {0}",
|
||||
"ValueContainer": "Bingja: {0}",
|
||||
"ValueEpisodeCount": "{0} partar",
|
||||
"ValueMinutes": "{0} min",
|
||||
"ValueMovieCount": "{0} filmar",
|
||||
"ValueOneEpisode": "1 partur",
|
||||
"ValueOneMovie": "1 filmur",
|
||||
"ValueOneMusicVideo": "1 tónleika sjónband",
|
||||
"ValueOneSeries": "1 røð",
|
||||
"ValueSeconds": "{0} sekund",
|
||||
"ValueSeriesCount": "{0} røðir",
|
||||
"Sports": "Ítróttur",
|
||||
"Smart": "Smart",
|
||||
"ShowYear": "Vís árið",
|
||||
"SimultaneousConnectionLimitHelp": "Mest loyvda antalið av samstundis stroymingum. Skriva 0 fyri einki hámark.",
|
||||
"Settings": "Stillingar",
|
||||
"SettingsSaved": "Stillingar eru goymdar.",
|
||||
"SelectAdminUsername": "Vinarliga vel eitt brúkaranavn til fyrisitara kontu.",
|
||||
"Series": "Røð",
|
||||
"SeriesCancelled": "Røðin er steðga.",
|
||||
"Search": "Leita",
|
||||
"SearchForCollectionInternetMetadata": "Leita eftir list og metadata á alnótini",
|
||||
"SeriesSettings": "Røð stillingar",
|
||||
"SeriesYearToPresent": "{0} - Núverandi",
|
||||
"ServerRestartNeededAfterPluginInstall": "Jellyfin má endurbyrjast, aftaná at eitt ískoytisforrit er lagt inn.",
|
||||
"ShowAdvancedSettings": "Vís framkomnar stillingar",
|
||||
"StopPlayback": "Steðga avspæling",
|
||||
"SubtitleGray": "Grátt",
|
||||
"SubtitleLightGray": "Ljósagráður",
|
||||
"SubtitleMagenta": "Viólreyður",
|
||||
"AllowedRemoteAddressesHelp": "Komma býttur listið av IP addressum ella IP/netmask inngangur fyri netverk, ið verða loyvd at fjarbinda. Um hesin teigur er blankur, so eru allar fjar addressur loyvdar.",
|
||||
"SubtitleWhite": "Hvítt",
|
||||
"Arranger": "Fyrireikari",
|
||||
"SubtitleYellow": "Gult",
|
||||
"AskAdminToCreateLibrary": "Bið ein fyrisitari upprætta eitt savn.",
|
||||
"TabAccess": "Atgongd",
|
||||
"TypeOptionPluralEpisode": "Partar",
|
||||
"TabContainers": "Kassar",
|
||||
"TitlePlayback": "Avspæling",
|
||||
"TabMusic": "Tónleikur",
|
||||
"TabOther": "Annað",
|
||||
"TagsValue": "Frámerkir: {0}",
|
||||
"TabMyPlugins": "Míni ískoytisforrit",
|
||||
"TabParentalControl": "Foreldra ræði",
|
||||
"Sunday": "Sunnudag",
|
||||
"SubtitleBlack": "Svart",
|
||||
"TabResponses": "Svar",
|
||||
"SubtitleBlue": "Blátt",
|
||||
"TextSent": "Tekst sent.",
|
||||
"TheseSettingsAffectSubtitlesOnThisDevice": "Hesir stillingar ávirka undirteksir á hesari eindini",
|
||||
"Thumb": "Tummil",
|
||||
"Thursday": "Hósdagur",
|
||||
"Track": "Spor",
|
||||
"Transcoding": "Umkodning",
|
||||
"TypeOptionPluralBook": "Bøkur",
|
||||
"TypeOptionPluralMovie": "Filmar",
|
||||
"TypeOptionPluralMusicArtist": "Tónleikarar",
|
||||
"TypeOptionPluralSeries": "Sjónvarpsrøðir",
|
||||
"UnknownError": "Ein ókendur feilur hendi.",
|
||||
"Unplayed": "Ikki spældur",
|
||||
"Upload": "Send upp",
|
||||
"UseEpisodeImagesInNextUp": "Brúka partamyndir í 'Komandi' og 'Hyggj víðari' teigum",
|
||||
"UserMenu": "Brúkara valmynd",
|
||||
"ValueAlbumCount": "{0} album",
|
||||
"ValueConditions": "Treytir: {0}",
|
||||
"ValueDiscNumber": "Fløga {0}",
|
||||
"ValueMusicVideoCount": "{0} tónleika sjónbond",
|
||||
"ValueOneAlbum": "1 album",
|
||||
"ValueOneSong": "1 sangur",
|
||||
"ValueSongCount": "{0} sangir"
|
||||
}
|
|
@ -1762,5 +1762,7 @@
|
|||
"LabelThrottleDelaySecondsHelp": "Tempo in secondi dopo cui il transcodificatore sarà messo in throttle. Deve essere sufficientemente grande perché il client mantenga un buon buffer. Funziona solo se il throttling è abilitato.",
|
||||
"LabelSegmentKeepSeconds": "Il tempo per cui tenere i segmenti",
|
||||
"LabelSegmentKeepSecondsHelp": "Tempo in secondi per cui i segmenti saranno tenuti prima di essere sovrascritti. Deve essere più grande di \"Throttle dopo\". Funziona solo se l'eliminazione dei segmenti è abilitata.",
|
||||
"AllowAv1Encoding": "Permetti la codifica nel formato AV1"
|
||||
"AllowAv1Encoding": "Permetti la codifica nel formato AV1",
|
||||
"GoHome": "Vai alla Home",
|
||||
"GridView": "Vista Griglia"
|
||||
}
|
||||
|
|
|
@ -1162,5 +1162,7 @@
|
|||
"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."
|
||||
"LabelKodiMetadataDateFormatHelp": "Visos datos iš NFO failų bus ištraukiamos šiuo formatu.",
|
||||
"AllowSegmentDeletion": "Ištrinti segmentus",
|
||||
"AllowSegmentDeletionHelp": "Ištrinkite senus segmentus, kai jie buvo išsiųsti klientui. Taip išvengiama viso perkoduoto failo saugojimo diske. Veiks tik su įjungtu droseliu. Išjunkite tai, jei kyla atkūrimo problemų."
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"LabelFinish": "Habis",
|
||||
"LabelYoureDone": "Kamu Selesai!",
|
||||
"ParentalRating": "Parental Rating",
|
||||
"SettingsSaved": "Seting Disimpan.",
|
||||
"SettingsSaved": "Tetapan Disimpan.",
|
||||
"Absolute": "Mutlak",
|
||||
"AccessRestrictedTryAgainLater": "Akses dihalang pada masa ini. Sila cuba sebentar lagi.",
|
||||
"Actor": "Pelakon",
|
||||
|
@ -249,5 +249,7 @@
|
|||
"AllowCollectionManagement": "Benarkan pengguna ini meguruskan koleksi",
|
||||
"AllowSegmentDeletion": "Padam segment",
|
||||
"AllowSegmentDeletionHelp": "Padam segmen lama setelah ia dihantar ke pelayan. Ini menghalang file transcode disimpan dalam disk. Ia akan berfungsi dengan penghad haju dihidupkan. Matikan tetapan ini jika anda mengalami isu dengan pemain video.",
|
||||
"LabelThrottleDelaySeconds": "Penghad laju setelah"
|
||||
"LabelThrottleDelaySeconds": "Penghad laju setelah",
|
||||
"Settings": "Tetapan",
|
||||
"SelectServer": "Pilih pelayan"
|
||||
}
|
||||
|
|
|
@ -1751,5 +1751,25 @@
|
|||
"Unknown": "Desconhecido",
|
||||
"HeaderConfirmRepositoryInstallation": "Confirme a instalação do repositório de plug-ins",
|
||||
"LabelDeveloper": "Desenvolvedor",
|
||||
"PleaseConfirmRepositoryInstallation": "Por favor, clique em OK para confirmar que você leu o acima e deseja prosseguir com a instalação do repositório de plug-ins."
|
||||
"PleaseConfirmRepositoryInstallation": "Por favor, clique em OK para confirmar que você leu o acima e deseja prosseguir com a instalação do repositório de plug-ins.",
|
||||
"LabelIsHearingImpaired": "Para deficientes auditivos (SDH)",
|
||||
"BackdropScreensaver": "Imagem de fundo do protetor de tela",
|
||||
"LogoScreensaver": "Logo da proteção de tela",
|
||||
"AllowAv1Encoding": "Permitir codificação em formato AV1",
|
||||
"HeaderGuestCast": "Estrelas Convidadas",
|
||||
"HeaderEpisodesStatus": "Situação dos Episódios",
|
||||
"GoHome": "Tela Inicial",
|
||||
"UnknownError": "Um erro desconhecido aconteceu.",
|
||||
"LabelBackdropScreensaverInterval": "Intervalo da imagem de fundo da proteção de tela",
|
||||
"LabelBackdropScreensaverIntervalHelp": "Tempo em segundos entre as diferentes imagens de fundo do protetor de tela.",
|
||||
"GridView": "Visualização em grelha",
|
||||
"ListView": "Visualização em Lista",
|
||||
"AiTranslated": "Traduzido por IA",
|
||||
"MachineTranslated": "Traduzido por Máquina",
|
||||
"AllowSegmentDeletion": "Remover segmentos",
|
||||
"AllowSegmentDeletionHelp": "Remover segmentos antigos após serem enviados ao cliente. Isso previne armazenar o arquivo transcodificado em disco. Funciona apenas com limitação habilitada. Desligue esta opção se você experenciar problemas com a reprodução.",
|
||||
"LabelThrottleDelaySeconds": "Limitar após",
|
||||
"LabelThrottleDelaySecondsHelp": "Tempo em segundos em que o transcodificador será limitado. É necessário que seja grande o suficiente para que o cliente mantenha um buffer saudável. Funciona apenas se o limitador estiver habilitado.",
|
||||
"LabelSegmentKeepSeconds": "Tempo para armazenar seguimentos",
|
||||
"LabelSegmentKeepSecondsHelp": "Tempo em segundos para que os seguimentos sejam armazenados antes de serem sobrescritos. É necessário que seja maior que \"Limitar após\". Funciona apenas se a remoção de segmentos estiver habilitada."
|
||||
}
|
||||
|
|
20
src/utils/card.ts
Normal file
20
src/utils/card.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
enum CardShape {
|
||||
Backdrop = 'backdrop',
|
||||
BackdropOverflow = 'overflowBackdrop',
|
||||
Portrait = 'portrait',
|
||||
PortraitOverflow = 'overflowPortrait',
|
||||
Square = 'square',
|
||||
SquareOverflow = 'overflowSquare'
|
||||
}
|
||||
|
||||
export function getSquareShape(enableOverflow = true) {
|
||||
return enableOverflow ? CardShape.SquareOverflow : CardShape.Square;
|
||||
}
|
||||
|
||||
export function getBackdropShape(enableOverflow = true) {
|
||||
return enableOverflow ? CardShape.BackdropOverflow : CardShape.Backdrop;
|
||||
}
|
||||
|
||||
export function getPortraitShape(enableOverflow = true) {
|
||||
return enableOverflow ? CardShape.PortraitOverflow : CardShape.Portrait;
|
||||
}
|
158
src/utils/items.ts
Normal file
158
src/utils/items.ts
Normal file
|
@ -0,0 +1,158 @@
|
|||
import { ItemFields } from '@jellyfin/sdk/lib/generated-client/models/item-fields';
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
||||
import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { EpisodeFilter, FeatureFilters, LibraryViewSettings, ParentId, VideoBasicFilter, ViewMode } from '../types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
export const getVideoBasicFilter = (libraryViewSettings: LibraryViewSettings) => {
|
||||
let isHd;
|
||||
|
||||
if (libraryViewSettings.Filters?.VideoBasicFilter?.includes(VideoBasicFilter.IsHD)) {
|
||||
isHd = true;
|
||||
}
|
||||
|
||||
if (libraryViewSettings.Filters?.VideoBasicFilter?.includes(VideoBasicFilter.IsSD)) {
|
||||
isHd = false;
|
||||
}
|
||||
|
||||
return {
|
||||
isHd,
|
||||
is4K: libraryViewSettings.Filters?.VideoBasicFilter?.includes(VideoBasicFilter.Is4K) ?
|
||||
true :
|
||||
undefined,
|
||||
is3D: libraryViewSettings.Filters?.VideoBasicFilter?.includes(VideoBasicFilter.Is3D) ?
|
||||
true :
|
||||
undefined
|
||||
};
|
||||
};
|
||||
|
||||
export const getFeatureFilters = (libraryViewSettings: LibraryViewSettings) => {
|
||||
return {
|
||||
hasSubtitles: libraryViewSettings.Filters?.Features?.includes(FeatureFilters.HasSubtitles) ?
|
||||
true :
|
||||
undefined,
|
||||
hasTrailer: libraryViewSettings.Filters?.Features?.includes(FeatureFilters.HasTrailer) ?
|
||||
true :
|
||||
undefined,
|
||||
hasSpecialFeature: libraryViewSettings.Filters?.Features?.includes(
|
||||
FeatureFilters.HasSpecialFeature
|
||||
) ?
|
||||
true :
|
||||
undefined,
|
||||
hasThemeSong: libraryViewSettings.Filters?.Features?.includes(FeatureFilters.HasThemeSong) ?
|
||||
true :
|
||||
undefined,
|
||||
hasThemeVideo: libraryViewSettings.Filters?.Features?.includes(
|
||||
FeatureFilters.HasThemeVideo
|
||||
) ?
|
||||
true :
|
||||
undefined
|
||||
};
|
||||
};
|
||||
|
||||
export const getEpisodeFilter = (
|
||||
viewType: LibraryTab,
|
||||
libraryViewSettings: LibraryViewSettings
|
||||
) => {
|
||||
return {
|
||||
parentIndexNumber: libraryViewSettings.Filters?.EpisodeFilter?.includes(
|
||||
EpisodeFilter.ParentIndexNumber
|
||||
) ?
|
||||
0 :
|
||||
undefined,
|
||||
isMissing:
|
||||
viewType === LibraryTab.Episodes ?
|
||||
!!libraryViewSettings.Filters?.EpisodeFilter?.includes(EpisodeFilter.IsMissing) :
|
||||
undefined,
|
||||
isUnaired: libraryViewSettings.Filters?.EpisodeFilter?.includes(EpisodeFilter.IsUnaired) ?
|
||||
true :
|
||||
undefined
|
||||
};
|
||||
};
|
||||
|
||||
const getItemFieldsEnum = (
|
||||
viewType: LibraryTab,
|
||||
libraryViewSettings: LibraryViewSettings
|
||||
) => {
|
||||
const itemFields: ItemFields[] = [];
|
||||
|
||||
if (viewType !== LibraryTab.Networks) {
|
||||
itemFields.push(ItemFields.BasicSyncInfo, ItemFields.MediaSourceCount);
|
||||
}
|
||||
|
||||
if (libraryViewSettings.ImageType === ImageType.Primary) {
|
||||
itemFields.push(ItemFields.PrimaryImageAspectRatio);
|
||||
}
|
||||
|
||||
if (viewType === LibraryTab.Networks) {
|
||||
itemFields.push(
|
||||
ItemFields.DateCreated,
|
||||
ItemFields.PrimaryImageAspectRatio
|
||||
);
|
||||
}
|
||||
|
||||
return itemFields;
|
||||
};
|
||||
|
||||
export const getFieldsQuery = (
|
||||
viewType: LibraryTab,
|
||||
libraryViewSettings: LibraryViewSettings
|
||||
) => {
|
||||
return {
|
||||
fields: getItemFieldsEnum(viewType, libraryViewSettings)
|
||||
};
|
||||
};
|
||||
|
||||
export const getLimitQuery = () => {
|
||||
return {
|
||||
limit: userSettings.libraryPageSize(undefined) || undefined
|
||||
};
|
||||
};
|
||||
|
||||
export const getAlphaPickerQuery = (libraryViewSettings: LibraryViewSettings) => {
|
||||
const alphabetValue = libraryViewSettings.Alphabet !== null ?
|
||||
libraryViewSettings.Alphabet : undefined;
|
||||
|
||||
return {
|
||||
nameLessThan: alphabetValue === '#' ? 'A' : undefined,
|
||||
nameStartsWith: alphabetValue === '#' ? undefined : alphabetValue
|
||||
};
|
||||
};
|
||||
|
||||
export const getFiltersQuery = (
|
||||
viewType: LibraryTab,
|
||||
libraryViewSettings: LibraryViewSettings
|
||||
) => {
|
||||
return {
|
||||
...getFeatureFilters(libraryViewSettings),
|
||||
...getEpisodeFilter(viewType, libraryViewSettings),
|
||||
...getVideoBasicFilter(libraryViewSettings),
|
||||
seriesStatus: libraryViewSettings?.Filters?.SeriesStatus,
|
||||
videoTypes: libraryViewSettings?.Filters?.VideoTypes,
|
||||
filters: libraryViewSettings?.Filters?.Status,
|
||||
genres: libraryViewSettings?.Filters?.Genres,
|
||||
officialRatings: libraryViewSettings?.Filters?.OfficialRatings,
|
||||
tags: libraryViewSettings?.Filters?.Tags,
|
||||
years: libraryViewSettings?.Filters?.Years,
|
||||
studioIds: libraryViewSettings?.Filters?.StudioIds
|
||||
};
|
||||
};
|
||||
|
||||
export const getSettingsKey = (viewType: LibraryTab, parentId: ParentId) => {
|
||||
return `${viewType} - ${parentId}`;
|
||||
};
|
||||
|
||||
export const getDefaultLibraryViewSettings = (): LibraryViewSettings => {
|
||||
return {
|
||||
ShowTitle: true,
|
||||
ShowYear: false,
|
||||
ViewMode: ViewMode.GridView,
|
||||
ImageType: ImageType.Primary,
|
||||
CardLayout: false,
|
||||
SortBy: ItemSortBy.SortName,
|
||||
SortOrder: SortOrder.Ascending,
|
||||
StartIndex: 0
|
||||
};
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue