mirror of
https://github.com/jeffvli/feishin.git
synced 2024-11-20 06:27:09 +01:00
Add initial support for static server
This commit is contained in:
parent
1d2e9484d8
commit
9c355ce5bd
@ -43,6 +43,11 @@ const configuration: webpack.Configuration = {
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin({
|
||||
NODE_ENV: 'production',
|
||||
FS_SERVER_NAME: process.env.SERVER_URL ?? null,
|
||||
FS_SERVER_URL: process.env.SERVER_URL ?? null,
|
||||
FS_SERVER_TYPE: process.env.SERVER_URL ?? null,
|
||||
FS_SERVER_USERNAME: process.env.SERVER_URL ?? null,
|
||||
FS_SERVER_PASSWORD: process.env.SERVER_URL ?? null,
|
||||
}),
|
||||
],
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
"deletePlaylist": "delete $t(entity.playlist_one)",
|
||||
"deselectAll": "deselect all",
|
||||
"editPlaylist": "edit $t(entity.playlist_one)",
|
||||
"signIn": "sign in",
|
||||
"goToPage": "go to page",
|
||||
"moveToBottom": "move to bottom",
|
||||
"moveToTop": "move to top",
|
||||
@ -14,6 +15,7 @@
|
||||
"removeFromFavorites": "remove from $t(entity.favorite_other)",
|
||||
"removeFromPlaylist": "remove from $t(entity.playlist_one)",
|
||||
"removeFromQueue": "remove from queue",
|
||||
"removeServer": "remove server",
|
||||
"setRating": "set rating",
|
||||
"toggleSmartPlaylistEditor": "toggle $t(entity.smartPlaylist) editor",
|
||||
"viewPlaylists": "view $t(entity.playlist_other)"
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { ClientSideRowModelModule } from '@ag-grid-community/client-side-row-model';
|
||||
import { ModuleRegistry } from '@ag-grid-community/core';
|
||||
import { InfiniteRowModelModule } from '@ag-grid-community/infinite-row-model';
|
||||
@ -22,8 +22,19 @@ import { useHandlePlayQueueAdd } from '/@/renderer/features/player/hooks/use-han
|
||||
import { PlayQueueHandlerContext } from '/@/renderer/features/player';
|
||||
import { AddToPlaylistContextModal } from '/@/renderer/features/playlists';
|
||||
import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-settings';
|
||||
import { PlayerState, usePlayerStore, useQueueControls } from '/@/renderer/store';
|
||||
import { FontType, PlaybackType, PlayerStatus } from '/@/renderer/types';
|
||||
import {
|
||||
PlayerState,
|
||||
useAuthStoreActions,
|
||||
usePlayerStore,
|
||||
useQueueControls,
|
||||
} from '/@/renderer/store';
|
||||
import {
|
||||
FontType,
|
||||
PlaybackType,
|
||||
PlayerStatus,
|
||||
ServerListItem,
|
||||
ServerType,
|
||||
} from '/@/renderer/types';
|
||||
import '@ag-grid-community/styles/ag-grid.css';
|
||||
import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc';
|
||||
import i18n from '/@/i18n/i18n';
|
||||
@ -48,6 +59,57 @@ export const App = () => {
|
||||
const { clearQueue, restoreQueue } = useQueueControls();
|
||||
const remoteSettings = useRemoteSettings();
|
||||
const textStyleRef = useRef<HTMLStyleElement>();
|
||||
const { addServer } = useAuthStoreActions();
|
||||
|
||||
const setStaticServer = useCallback(() => {
|
||||
console.log('process.env.FS_SERVER_URL :>> ', process.env.FS_SERVER_URL);
|
||||
console.log('process.env.FS_SERVER_NAME', process.env.FS_SERVER_NAME);
|
||||
console.log('process.env.FS_SERVER_TYPE :>> ', process.env.FS_SERVER_TYPE);
|
||||
const url = process.env.FS_SERVER_URL;
|
||||
let serverType: ServerType | null = null;
|
||||
const name =
|
||||
process.env.FS_SERVER_NAME || serverType === ServerType.NAVIDROME
|
||||
? 'Navidrome'
|
||||
: 'Jellyfin';
|
||||
|
||||
switch (process.env.FS_SERVER_TYPE?.toLocaleLowerCase()) {
|
||||
case 'jellyfin':
|
||||
serverType = ServerType.JELLYFIN;
|
||||
break;
|
||||
case 'navidrome':
|
||||
serverType = ServerType.NAVIDROME;
|
||||
break;
|
||||
case 'subsonic':
|
||||
serverType = ServerType.SUBSONIC;
|
||||
break;
|
||||
default:
|
||||
serverType = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!url || !serverType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const serverItem: ServerListItem = {
|
||||
credential: undefined,
|
||||
id: 'static-server',
|
||||
name,
|
||||
ndCredential: undefined,
|
||||
static: true,
|
||||
type: serverType,
|
||||
url: url.replace(/\/$/, ''),
|
||||
userId: undefined,
|
||||
username: undefined,
|
||||
};
|
||||
|
||||
addServer(serverItem);
|
||||
}, [addServer]);
|
||||
|
||||
useEffect(() => {
|
||||
setStaticServer();
|
||||
}, [setStaticServer]);
|
||||
|
||||
useDiscordRpc();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -1,19 +1,70 @@
|
||||
import { Text } from '/@/renderer/components';
|
||||
import { openModal, closeAllModals } from '@mantine/modals';
|
||||
import isElectron from 'is-electron';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, DropdownMenu } from '/@/renderer/components';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { RiKeyFill, RiMenuFill } from 'react-icons/ri';
|
||||
import { AppMenu } from '/@/renderer/features/titlebar/components/app-menu';
|
||||
import { EditServerForm } from '/@/renderer/features/servers/components/edit-server-form';
|
||||
|
||||
const localSettings = isElectron() ? window.electron.localSettings : null;
|
||||
|
||||
export const ServerCredentialRequired = () => {
|
||||
const { t } = useTranslation();
|
||||
const currentServer = useCurrentServer();
|
||||
|
||||
const handleCredentialsModal = async () => {
|
||||
if (!currentServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const server = currentServer;
|
||||
let password: string | null = null;
|
||||
|
||||
try {
|
||||
if (localSettings && server.savePassword) {
|
||||
password = await localSettings.passwordGet(server.id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
openModal({
|
||||
children: server && (
|
||||
<EditServerForm
|
||||
isUpdate
|
||||
password={password}
|
||||
server={server}
|
||||
onCancel={closeAllModals}
|
||||
/>
|
||||
),
|
||||
size: 'sm',
|
||||
title: server.name,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text>
|
||||
The selected server '{currentServer?.name}' requires an additional login
|
||||
to access.
|
||||
</Text>
|
||||
<Text>
|
||||
Add your credentials in the 'manage servers' menu or switch to a different
|
||||
server.
|
||||
</Text>
|
||||
<Button
|
||||
leftIcon={<RiKeyFill />}
|
||||
variant="filled"
|
||||
onClick={handleCredentialsModal}
|
||||
>
|
||||
{t('action.signIn', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenu.Target>
|
||||
<Button
|
||||
leftIcon={<RiMenuFill />}
|
||||
variant="filled"
|
||||
>
|
||||
{t('common.menu', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
<AppMenu />
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiMenuFill } from 'react-icons/ri';
|
||||
import { Button, DropdownMenu, Text } from '/@/renderer/components';
|
||||
import { AppMenu } from '/@/renderer/features/titlebar/components/app-menu';
|
||||
|
||||
export const ServerRequired = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text>No server selected.</Text>
|
||||
@ -12,7 +15,7 @@ export const ServerRequired = () => {
|
||||
leftIcon={<RiMenuFill />}
|
||||
variant="filled"
|
||||
>
|
||||
Open menu
|
||||
{t('common.menu', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
|
@ -4,7 +4,7 @@ import isElectron from 'is-electron';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiCheckFill } from 'react-icons/ri';
|
||||
import { Link, Navigate } from 'react-router-dom';
|
||||
import { Button, PageHeader, Text } from '/@/renderer/components';
|
||||
import { Button, PageHeader } from '/@/renderer/components';
|
||||
import { ActionRequiredContainer } from '/@/renderer/features/action-required/components/action-required-container';
|
||||
import { MpvRequired } from '/@/renderer/features/action-required/components/mpv-required';
|
||||
import { ServerCredentialRequired } from '/@/renderer/features/action-required/components/server-credential-required';
|
||||
@ -12,15 +12,36 @@ import { ServerRequired } from '/@/renderer/features/action-required/components/
|
||||
import { AnimatedPage } from '/@/renderer/features/shared';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { ServerListItem, ServerType } from '/@/renderer/types';
|
||||
|
||||
const localSettings = isElectron() ? window.electron.localSettings : null;
|
||||
|
||||
export const getIsCredentialRequired = (currentServer: ServerListItem | null) => {
|
||||
if (currentServer === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentServer.type === ServerType.NAVIDROME) {
|
||||
return !currentServer.ndCredential || !currentServer.credential || !currentServer.username;
|
||||
}
|
||||
|
||||
if (currentServer.type === ServerType.JELLYFIN) {
|
||||
return !currentServer.credential || !currentServer.username;
|
||||
}
|
||||
|
||||
if (currentServer.type === ServerType.SUBSONIC) {
|
||||
return !currentServer.credential || !currentServer.username;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const ActionRequiredRoute = () => {
|
||||
const { t } = useTranslation();
|
||||
const currentServer = useCurrentServer();
|
||||
const [isMpvRequired, setIsMpvRequired] = useState(false);
|
||||
const isServerRequired = !currentServer;
|
||||
const isCredentialRequired = false;
|
||||
const isCredentialRequired = getIsCredentialRequired(currentServer);
|
||||
|
||||
useEffect(() => {
|
||||
const getMpvPath = async () => {
|
||||
@ -85,7 +106,6 @@ const ActionRequiredRoute = () => {
|
||||
color="var(--success-color)"
|
||||
size={30}
|
||||
/>
|
||||
<Text size="xl">No issues found</Text>
|
||||
</Group>
|
||||
<Button
|
||||
component={Link}
|
||||
@ -93,7 +113,7 @@ const ActionRequiredRoute = () => {
|
||||
to={AppRoute.HOME}
|
||||
variant="filled"
|
||||
>
|
||||
Go back
|
||||
{t('page.appMenu.goBack', { postProcess: 'sentenceCase' })}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
@ -46,7 +46,7 @@ export const EditServerForm = ({ isUpdate, password, server, onCancel }: EditSer
|
||||
savePassword: server.savePassword || false,
|
||||
type: server?.type,
|
||||
url: server?.url,
|
||||
username: server?.username,
|
||||
username: server?.username || '',
|
||||
},
|
||||
});
|
||||
|
||||
@ -125,6 +125,7 @@ export const EditServerForm = ({ isUpdate, password, server, onCancel }: EditSer
|
||||
<Stack ref={focusTrapRef}>
|
||||
<TextInput
|
||||
required
|
||||
disabled={server?.static}
|
||||
label={t('form.addServer.input', {
|
||||
context: 'name',
|
||||
postProcess: 'titleCase',
|
||||
@ -134,6 +135,7 @@ export const EditServerForm = ({ isUpdate, password, server, onCancel }: EditSer
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
disabled={server?.static}
|
||||
label={t('form.addServer.input', {
|
||||
context: 'url',
|
||||
postProcess: 'titleCase',
|
||||
@ -143,6 +145,7 @@ export const EditServerForm = ({ isUpdate, password, server, onCancel }: EditSer
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
data-autofocus={!server?.username}
|
||||
label={t('form.addServer.input', {
|
||||
context: 'username',
|
||||
postProcess: 'titleCase',
|
||||
@ -151,8 +154,8 @@ export const EditServerForm = ({ isUpdate, password, server, onCancel }: EditSer
|
||||
{...form.getInputProps('username')}
|
||||
/>
|
||||
<PasswordInput
|
||||
data-autofocus
|
||||
required
|
||||
data-autofocus={server?.username}
|
||||
label={t('form.addServer.input', {
|
||||
context: 'password',
|
||||
postProcess: 'titleCase',
|
||||
|
@ -3,6 +3,7 @@ import { Stack, Group, Divider } from '@mantine/core';
|
||||
import { Button, Text, TimeoutButton } from '/@/renderer/components';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import isElectron from 'is-electron';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiDeleteBin2Line, RiEdit2Fill } from 'react-icons/ri';
|
||||
import { EditServerForm } from '/@/renderer/features/servers/components/edit-server-form';
|
||||
import { ServerSection } from '/@/renderer/features/servers/components/server-section';
|
||||
@ -16,6 +17,7 @@ interface ServerListItemProps {
|
||||
}
|
||||
|
||||
export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [edit, editHandlers] = useDisclosure(false);
|
||||
const [savedPassword, setSavedPassword] = useState('');
|
||||
const { deleteServer } = useAuthStoreActions();
|
||||
@ -68,8 +70,18 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
<Stack>
|
||||
<Group noWrap>
|
||||
<Stack>
|
||||
<Text>URL</Text>
|
||||
<Text>Username</Text>
|
||||
<Text>
|
||||
{t('form.addServer.input', {
|
||||
context: 'url',
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Text>
|
||||
<Text>
|
||||
{t('form.addServer.input', {
|
||||
context: 'username',
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>{server.url}</Text>
|
||||
@ -83,7 +95,9 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
variant="subtle"
|
||||
onClick={() => handleEdit()}
|
||||
>
|
||||
Edit
|
||||
{t('common.edit', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
@ -95,7 +109,9 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
timeoutProps={{ callback: handleDeleteServer, duration: 1000 }}
|
||||
variant="subtle"
|
||||
>
|
||||
Remove server
|
||||
{t('action.removeServer', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</TimeoutButton>
|
||||
</Stack>
|
||||
);
|
||||
|
@ -69,14 +69,14 @@ export const AppMenu = () => {
|
||||
/>
|
||||
),
|
||||
size: 'sm',
|
||||
title: `Update session for "${server.name}"`,
|
||||
title: server.name,
|
||||
});
|
||||
};
|
||||
|
||||
const handleManageServersModal = () => {
|
||||
openModal({
|
||||
children: <ServerList />,
|
||||
title: 'Manage Servers',
|
||||
title: t('page.appMenu.manageServers', { postProcess: 'sentenceCase' }),
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@ import isElectron from 'is-electron';
|
||||
import { Navigate, Outlet } from 'react-router-dom';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { getIsCredentialRequired } from '/@/renderer/features/action-required/routes/action-required-route';
|
||||
|
||||
const localSettings = isElectron() ? window.electron.localSettings : null;
|
||||
|
||||
@ -17,9 +18,11 @@ export const AppOutlet = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const isCredentialRequired = getIsCredentialRequired(currentServer);
|
||||
|
||||
const isServerRequired = !currentServer;
|
||||
|
||||
const actions = [isServerRequired, isMpvRequired()];
|
||||
const actions = [isServerRequired, isCredentialRequired, isMpvRequired()];
|
||||
const isActionRequired = actions.some((c) => c);
|
||||
|
||||
return isActionRequired;
|
||||
|
@ -31,7 +31,15 @@ export const useAuthStore = create<AuthSlice>()(
|
||||
actions: {
|
||||
addServer: (args) => {
|
||||
set((state) => {
|
||||
if (state.serverList[args.id]) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.serverList[args.id] = args;
|
||||
|
||||
if (!state.currentServer) {
|
||||
state.currentServer = args;
|
||||
}
|
||||
});
|
||||
},
|
||||
deleteServer: (id) => {
|
||||
|
@ -61,15 +61,16 @@ export enum ServerType {
|
||||
}
|
||||
|
||||
export type ServerListItem = {
|
||||
credential: string;
|
||||
credential?: string;
|
||||
id: string;
|
||||
name: string;
|
||||
ndCredential?: string;
|
||||
savePassword?: boolean;
|
||||
static?: boolean;
|
||||
type: ServerType;
|
||||
url: string;
|
||||
userId: string | null;
|
||||
username: string;
|
||||
userId?: string | null;
|
||||
username?: string;
|
||||
};
|
||||
|
||||
export enum PlayerStatus {
|
||||
|
Loading…
Reference in New Issue
Block a user