Skip to content

Commit 3b2418f

Browse files
authored
Merge branch 'master' into armanc/add-subtitle-sync-ui
2 parents 04c5f17 + 13412e3 commit 3b2418f

156 files changed

Lines changed: 1730 additions & 787 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/apps/dashboard/components/BaseCard.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import CardActionArea from '@mui/material/CardActionArea';
1111
import Stack from '@mui/material/Stack';
1212
import { Link, To } from 'react-router-dom';
1313

14-
interface IProps {
14+
interface BaseCardProps {
1515
title?: string;
1616
secondaryTitle?: string;
1717
text?: string;
@@ -24,7 +24,7 @@ interface IProps {
2424
onActionClick?: () => void;
2525
};
2626

27-
const BaseCard = ({ title, secondaryTitle, text, image, icon, to, onClick, action, actionRef, onActionClick }: IProps) => {
27+
const BaseCard = ({ title, secondaryTitle, text, image, icon, to, onClick, action, actionRef, onActionClick }: BaseCardProps) => {
2828
return (
2929
<Card
3030
sx={{
@@ -64,7 +64,7 @@ const BaseCard = ({ title, secondaryTitle, text, image, icon, to, onClick, actio
6464
</CardActionArea>
6565
<CardHeader
6666
title={
67-
<Stack direction='row' gap={1} alignItems='center'>
67+
<Stack direction='row' spacing={1} alignItems='center'>
6868
<Typography sx={{
6969
overflow: 'hidden',
7070
whiteSpace: 'nowrap',
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import Search from '@mui/icons-material/Search';
2+
import InputBase, { type InputBaseProps } from '@mui/material/InputBase';
3+
import { alpha, styled } from '@mui/material/styles';
4+
import React, { type FC } from 'react';
5+
6+
const SearchContainer = styled('div')(({ theme }) => ({
7+
display: 'flex',
8+
position: 'relative',
9+
borderRadius: theme.shape.borderRadius,
10+
backgroundColor: alpha(theme.palette.common.white, 0.15),
11+
'&:hover': {
12+
backgroundColor: alpha(theme.palette.common.white, 0.25)
13+
},
14+
width: '100%',
15+
[theme.breakpoints.up('sm')]: {
16+
width: 'auto'
17+
}
18+
}));
19+
20+
const SearchIconWrapper = styled('div')(({ theme }) => ({
21+
padding: theme.spacing(0, 2),
22+
height: '100%',
23+
position: 'absolute',
24+
pointerEvents: 'none',
25+
display: 'flex',
26+
alignItems: 'center',
27+
justifyContent: 'center'
28+
}));
29+
30+
const StyledInputBase = styled(InputBase)(({ theme }) => ({
31+
color: 'inherit',
32+
flexGrow: 1,
33+
'& .MuiInputBase-input': {
34+
padding: theme.spacing(1, 1, 1, 0),
35+
// vertical padding + font size from searchIcon
36+
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
37+
transition: theme.transitions.create('width'),
38+
width: '100%',
39+
[theme.breakpoints.up('md')]: {
40+
width: '20ch'
41+
}
42+
}
43+
}));
44+
45+
interface SearchInputProps extends InputBaseProps {
46+
label?: string
47+
}
48+
49+
const SearchInput: FC<SearchInputProps> = ({
50+
label,
51+
...props
52+
}) => {
53+
return (
54+
<SearchContainer>
55+
<SearchIconWrapper>
56+
<Search />
57+
</SearchIconWrapper>
58+
<StyledInputBase
59+
placeholder={label}
60+
inputProps={{
61+
'aria-label': label,
62+
...props.inputProps
63+
}}
64+
{...props}
65+
/>
66+
</SearchContainer>
67+
);
68+
};
69+
70+
export default SearchInput;

src/apps/dashboard/components/drawer/sections/PluginDrawerSection.tsx

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Extension from '@mui/icons-material/Extension';
22
import Folder from '@mui/icons-material/Folder';
3-
import Public from '@mui/icons-material/Public';
43
import List from '@mui/material/List';
54
import ListItemIcon from '@mui/material/ListItemIcon';
65
import ListItemText from '@mui/material/ListItemText';
@@ -33,23 +32,16 @@ const PluginDrawerSection = () => {
3332
>
3433
<ListItemLink
3534
to='/dashboard/plugins'
36-
includePaths={[ '/configurationpage' ]}
35+
includePaths={[
36+
'/configurationpage',
37+
'/dashboard/plugins/repositories'
38+
]}
3739
excludePaths={pagesInfo?.map(p => `/${Dashboard.getPluginUrl(p.Name)}`)}
3840
>
3941
<ListItemIcon>
4042
<Extension />
4143
</ListItemIcon>
42-
<ListItemText primary={globalize.translate('TabMyPlugins')} />
43-
</ListItemLink>
44-
45-
<ListItemLink
46-
to='/dashboard/plugins/catalog'
47-
includePaths={[ '/dashboard/plugins/repositories' ]}
48-
>
49-
<ListItemIcon>
50-
<Public />
51-
</ListItemIcon>
52-
<ListItemText primary={globalize.translate('TabCatalog')} />
44+
<ListItemText primary={globalize.translate('TabPlugins')} />
5345
</ListItemLink>
5446

5547
{pagesInfo?.map(pageInfo => (

src/apps/dashboard/components/widgets/ServerInfoWidget.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ const ServerInfoWidget = ({ onScanLibrariesClick, onRestartClick, onShutdownClic
2727
padding: 2
2828
}}>
2929
<Stack direction='row'>
30-
<Stack flexGrow={1} gap={1}>
30+
<Stack flexGrow={1} spacing={1}>
3131
<Typography fontWeight='bold'>{globalize.translate('LabelServerName')}</Typography>
3232
<Typography fontWeight='bold'>{globalize.translate('LabelServerVersion')}</Typography>
3333
<Typography fontWeight='bold'>{globalize.translate('LabelWebVersion')}</Typography>
3434
<Typography fontWeight='bold'>{globalize.translate('LabelBuildVersion')}</Typography>
3535
</Stack>
36-
<Stack flexGrow={5} gap={1}>
36+
<Stack flexGrow={5} spacing={1}>
3737
{isPending ? (
3838
<>
3939
<Skeleton />
@@ -53,7 +53,7 @@ const ServerInfoWidget = ({ onScanLibrariesClick, onRestartClick, onShutdownClic
5353
</Stack>
5454
</Paper>
5555

56-
<Stack direction='row' gap={1.5} flexWrap={'wrap'}>
56+
<Stack direction='row' spacing={1.5}>
5757
<Button
5858
onClick={onScanLibrariesClick}
5959
sx={{

src/apps/dashboard/constants/helpLinks.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ export const HelpLinks = [
2222
paths: ['/dashboard/playback/transcoding'],
2323
url: 'https://jellyfin.org/docs/general/server/transcoding'
2424
}, {
25-
paths: [
26-
'/dashboard/plugins',
27-
'/dashboard/plugins/catalog'
28-
],
25+
paths: ['/dashboard/plugins'],
2926
url: 'https://jellyfin.org/docs/general/server/plugins/'
3027
}, {
3128
paths: ['/dashboard/plugins/repositories'],

src/apps/dashboard/controllers/library.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import escapeHtml from 'escape-html';
33
import taskButton from 'scripts/taskbutton';
44
import loading from 'components/loading/loading';
55
import globalize from 'lib/globalize';
6-
import dom from 'scripts/dom';
6+
import dom from 'utils/dom';
77
import imageHelper from 'utils/image';
88
import 'components/cardbuilder/card.scss';
99
import 'elements/emby-itemrefreshindicator/emby-itemrefreshindicator';

src/apps/dashboard/controllers/livetvstatus.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'jquery';
22

33
import globalize from 'lib/globalize';
44
import taskButton from 'scripts/taskbutton';
5-
import dom from 'scripts/dom';
5+
import dom from 'utils/dom';
66
import layoutManager from 'components/layoutManager';
77
import loading from 'components/loading/loading';
88
import browser from 'scripts/browser';

src/apps/dashboard/controllers/livetvtuner.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import globalize from 'lib/globalize';
22
import loading from 'components/loading/loading';
3-
import dom from 'scripts/dom';
3+
import dom from 'utils/dom';
44
import 'elements/emby-input/emby-input';
55
import 'elements/emby-button/emby-button';
66
import 'elements/emby-checkbox/emby-checkbox';

src/apps/dashboard/features/backups/components/BackupInfoDialog.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ const BackupInfoDialog: FunctionComponent<IProps> = ({ backup, open, onClose }:
4444
</DialogTitle>
4545

4646
<DialogContent>
47-
<Stack gap={2}>
47+
<Stack spacing={2}>
4848
<Box>
4949
<Stack
5050
direction='row'
51-
gap={2}
51+
spacing={2}
5252
>
5353
<Typography fontWeight='bold'>{globalize.translate('LabelPath')}</Typography>
5454
<Stack direction='row'>
@@ -60,7 +60,7 @@ const BackupInfoDialog: FunctionComponent<IProps> = ({ backup, open, onClose }:
6060
</Stack>
6161
<Stack
6262
direction='row'
63-
gap={2}
63+
spacing={2}
6464
>
6565
<Typography fontWeight='bold'>{globalize.translate('LabelVersion')}</Typography>
6666
<Typography color='text.secondary'>{backup.ServerVersion}</Typography>
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { PluginStatus } from '@jellyfin/sdk/lib/generated-client/models/plugin-status';
2+
import { useMemo } from 'react';
3+
4+
import { useApi } from 'hooks/useApi';
5+
6+
import { PluginCategory } from '../constants/pluginCategory';
7+
import type { PluginDetails } from '../types/PluginDetails';
8+
9+
import { findBestConfigurationPage } from './configurationPage';
10+
import { findBestPluginInfo } from './pluginInfo';
11+
import { useConfigurationPages } from './useConfigurationPages';
12+
import { usePackages } from './usePackages';
13+
import { usePlugins } from './usePlugins';
14+
15+
export const usePluginDetails = () => {
16+
const { api } = useApi();
17+
18+
const {
19+
data: configurationPages,
20+
isError: isConfigurationPagesError,
21+
isPending: isConfigurationPagesPending
22+
} = useConfigurationPages();
23+
24+
const {
25+
data: packages,
26+
isError: isPackagesError,
27+
isPending: isPackagesPending
28+
} = usePackages();
29+
30+
const {
31+
data: plugins,
32+
isError: isPluginsError,
33+
isPending: isPluginsPending
34+
} = usePlugins();
35+
36+
const pluginDetails = useMemo<PluginDetails[]>(() => {
37+
if (!isPackagesPending && !isPluginsPending) {
38+
const pluginIds = new Set<string>();
39+
packages?.forEach(({ guid }) => {
40+
if (guid) pluginIds.add(guid);
41+
});
42+
plugins?.forEach(({ Id }) => {
43+
if (Id) pluginIds.add(Id);
44+
});
45+
46+
return Array.from(pluginIds)
47+
.map(id => {
48+
const packageInfo = packages?.find(pkg => pkg.guid === id);
49+
const pluginInfo = findBestPluginInfo(id, plugins);
50+
51+
let version;
52+
if (pluginInfo) {
53+
// Find the installed version
54+
const repoVersion = packageInfo?.versions?.find(v => v.version === pluginInfo.Version);
55+
version = repoVersion || {
56+
version: pluginInfo.Version,
57+
VersionNumber: pluginInfo.Version
58+
};
59+
} else {
60+
// Use the latest version
61+
version = packageInfo?.versions?.[0];
62+
}
63+
64+
let imageUrl;
65+
if (pluginInfo?.HasImage) {
66+
imageUrl = api?.getUri(`/Plugins/${pluginInfo.Id}/${pluginInfo.Version}/Image`);
67+
}
68+
69+
let category = packageInfo?.category;
70+
if (!packageInfo) {
71+
switch (id) {
72+
case 'a629c0dafac54c7e931a7174223f14c8': // AudioDB
73+
case '8c95c4d2e50c4fb0a4f36c06ff0f9a1a': // MusicBrainz
74+
category = PluginCategory.Music;
75+
break;
76+
case 'a628c0dafac54c7e9d1a7134223f14c8': // OMDb
77+
case 'b8715ed16c4745289ad3f72deb539cd4': // TMDb
78+
category = PluginCategory.MoviesAndShows;
79+
break;
80+
case '872a78491171458da6fb3de3d442ad30': // Studio Images
81+
category = PluginCategory.General;
82+
}
83+
}
84+
85+
return {
86+
canUninstall: !!pluginInfo?.CanUninstall,
87+
category,
88+
description: pluginInfo?.Description || packageInfo?.description || packageInfo?.overview,
89+
id,
90+
imageUrl: imageUrl || packageInfo?.imageUrl || undefined,
91+
isEnabled: pluginInfo?.Status !== PluginStatus.Disabled,
92+
name: pluginInfo?.Name || packageInfo?.name,
93+
owner: packageInfo?.owner,
94+
status: pluginInfo?.Status,
95+
configurationPage: findBestConfigurationPage(configurationPages || [], id),
96+
version,
97+
versions: packageInfo?.versions || []
98+
};
99+
})
100+
.sort(({ name: nameA }, { name: nameB }) => (
101+
(nameA || '').localeCompare(nameB || '')
102+
));
103+
}
104+
105+
return [];
106+
}, [
107+
api,
108+
configurationPages,
109+
isPluginsPending,
110+
packages,
111+
plugins
112+
]);
113+
114+
return {
115+
data: pluginDetails,
116+
isError: isConfigurationPagesError || isPackagesError || isPluginsError,
117+
isPending: isConfigurationPagesPending || isPackagesPending || isPluginsPending
118+
};
119+
};

0 commit comments

Comments
 (0)