Add remote control (#164)

* draft add remotes

* add favorite, rating

* add basic auth
This commit is contained in:
Kendall Garner 2023-07-23 12:23:18 +00:00 committed by GitHub
parent 0a13d047bb
commit c9dbf9b5be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 2585 additions and 298 deletions

View file

@ -74,7 +74,7 @@ export const ApplicationSettings = () => {
zoomFactor: newVal,
},
});
localSettings.setZoomFactor(newVal);
localSettings!.setZoomFactor(newVal);
}}
/>
),

View file

@ -3,6 +3,7 @@ import { ApplicationSettings } from '/@/renderer/features/settings/components/ge
import { ControlSettings } from '/@/renderer/features/settings/components/general/control-settings';
import { SidebarSettings } from '/@/renderer/features/settings/components/general/sidebar-settings';
import { ThemeSettings } from '/@/renderer/features/settings/components/general/theme-settings';
import { RemoteSettings } from '/@/renderer/features/settings/components/general/remote-settings';
export const GeneralTab = () => {
return (
@ -14,6 +15,8 @@ export const GeneralTab = () => {
<ControlSettings />
<Divider />
<SidebarSettings />
<Divider />
<RemoteSettings />
</Stack>
);
};

View file

@ -0,0 +1,156 @@
import isElectron from 'is-electron';
import { SettingsSection } from '/@/renderer/features/settings/components/settings-section';
import { useRemoteSettings, useSettingsStoreActions } from '/@/renderer/store';
import { NumberInput, Switch, Text, TextInput, toast } from '/@/renderer/components';
import debounce from 'lodash/debounce';
const remote = isElectron() ? window.electron.remote : null;
export const RemoteSettings = () => {
const settings = useRemoteSettings();
const { setSettings } = useSettingsStoreActions();
const url = `http://localhost:${settings.port}`;
const debouncedEnableRemote = debounce(async (enabled: boolean) => {
const errorMsg = await remote!.setRemoteEnabled(enabled);
if (errorMsg === null) {
setSettings({
remote: {
...settings,
enabled,
},
});
} else {
toast.error({
message: errorMsg,
title: enabled ? 'Error enabling remote' : 'Error disabling remote',
});
}
}, 100);
const debouncedChangeRemotePort = debounce(async (port: number) => {
const errorMsg = await remote!.setRemotePort(port);
if (errorMsg === null) {
setSettings({
remote: {
...settings,
port,
},
});
} else {
toast.error({
message: errorMsg,
title: 'Error setting port',
});
}
});
const isHidden = !isElectron();
const controlOptions = [
{
control: (
<Switch
aria-label="Enable remote control server"
defaultChecked={settings.enabled}
onChange={async (e) => {
const enabled = e.currentTarget.checked;
await debouncedEnableRemote(enabled);
}}
/>
),
description: (
<div>
Start an HTTP server to remotely control Feishin. This will listen on{' '}
<a
href={url}
rel="noreferrer noopener"
target="_blank"
>
{url}
</a>
</div>
),
isHidden,
title: 'Enable remote control',
},
{
control: (
<NumberInput
aria-label="Set remote port"
max={65535}
value={settings.port}
onBlur={async (e) => {
if (!e) return;
const port = Number(e.currentTarget.value);
await debouncedChangeRemotePort(port);
}}
/>
),
description:
'Remote server port. Changes here only take effect when you enable the remote',
isHidden,
title: 'Remove server port',
},
{
control: (
<TextInput
aria-label="Set remote username"
defaultValue={settings.username}
onBlur={(e) => {
const username = e.currentTarget.value;
if (username === settings.username) return;
remote!.updateUsername(username);
setSettings({
remote: {
...settings,
username,
},
});
}}
/>
),
description:
'Username that must be provided to access remote. If both username and password are empty, disable authentication',
isHidden,
title: 'Remote username',
},
{
control: (
<TextInput
aria-label="Set remote password"
defaultValue={settings.password}
onBlur={(e) => {
const password = e.currentTarget.value;
if (password === settings.password) return;
remote!.updatePassword(password);
setSettings({
remote: {
...settings,
password,
},
});
}}
/>
),
description: 'Password to access remote',
isHidden,
title: 'Remote password',
},
];
return (
<>
<SettingsSection options={controlOptions} />
<Text size="lg">
<b>
NOTE: these credentials are by default transferred insecurely. Do not use a
password you care about. Changing username/password will disconnect clients and
require them to reauthenticate
</b>
</Text>
</>
);
};

View file

@ -23,12 +23,12 @@ export const WindowHotkeySettings = () => {
globalMediaHotkeys: e.currentTarget.checked,
},
});
localSettings.set('global_media_hotkeys', e.currentTarget.checked);
localSettings!.set('global_media_hotkeys', e.currentTarget.checked);
if (e.currentTarget.checked) {
localSettings.enableMediaKeys();
localSettings!.enableMediaKeys();
} else {
localSettings.disableMediaKeys();
localSettings!.disableMediaKeys();
}
}}
/>

View file

@ -56,7 +56,7 @@ export const AudioSettings = () => {
setSettings({ playback: { ...settings, type: e as PlaybackType } });
if (isElectron() && e === PlaybackType.LOCAL) {
const queueData = usePlayerStore.getState().actions.getPlayerData();
mpvPlayer.setQueue(queueData);
mpvPlayer!.setQueue(queueData);
}
}}
/>

View file

@ -36,7 +36,7 @@ export const getMpvSetting = (
case 'replayGainPreampDB':
return { 'replaygain-preamp': value || 0 };
default:
return key;
return { 'audio-format': value };
}
};
@ -66,12 +66,12 @@ export const MpvSettings = () => {
const [mpvPath, setMpvPath] = useState('');
const handleSetMpvPath = (e: File) => {
localSettings.set('mpv_path', e.path);
localSettings?.set('mpv_path', e.path);
};
useEffect(() => {
const getMpvPath = async () => {
if (!isElectron()) return setMpvPath('');
if (!localSettings) return setMpvPath('');
const mpvPath = (await localSettings.get('mpv_path')) as string;
return setMpvPath(mpvPath);
};

View file

@ -46,7 +46,7 @@ export const WindowSettings = () => {
message:
'Restart to apply changes... close the notification to restart Feishin',
onClose: () => {
window.electron.ipc.send('app-restart');
window.electron.ipc!.send('app-restart');
},
title: 'Restart required',
});