mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 10:23:33 +00:00
Add discord rich presence (#72)
This commit is contained in:
parent
2664a80851
commit
244c00c4c6
12 changed files with 391 additions and 7 deletions
122
src/renderer/features/discord-rpc/use-discord-rpc.ts
Normal file
122
src/renderer/features/discord-rpc/use-discord-rpc.ts
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* eslint-disable consistent-return */
|
||||
import isElectron from 'is-electron';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import {
|
||||
useCurrentSong,
|
||||
useCurrentStatus,
|
||||
useDiscordSetttings,
|
||||
usePlayerStore,
|
||||
} from '/@/renderer/store';
|
||||
import { SetActivity } from '@xhayper/discord-rpc';
|
||||
import { PlayerStatus, ServerType } from '/@/renderer/types';
|
||||
|
||||
const discordRpc = isElectron() ? window.electron.discordRpc : null;
|
||||
|
||||
export const useDiscordRpc = () => {
|
||||
const intervalRef = useRef(0);
|
||||
const discordSettings = useDiscordSetttings();
|
||||
const currentSong = useCurrentSong();
|
||||
const currentStatus = useCurrentStatus();
|
||||
|
||||
const setActivity = useCallback(async () => {
|
||||
if (!discordSettings.enableIdle && currentStatus === PlayerStatus.PAUSED) {
|
||||
discordRpc?.clearActivity();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTime = usePlayerStore.getState().current.time;
|
||||
|
||||
const now = Date.now();
|
||||
const start = currentTime ? Math.round(now - currentTime * 1000) : null;
|
||||
const end =
|
||||
currentSong?.duration && start ? Math.round(start + currentSong.duration) : null;
|
||||
|
||||
const artists = currentSong?.artists.map((artist) => artist.name).join(', ');
|
||||
|
||||
const activity: SetActivity = {
|
||||
details: currentSong?.name.padEnd(2, ' ') || 'Idle',
|
||||
instance: false,
|
||||
largeImageKey: undefined,
|
||||
largeImageText: currentSong?.album || 'Unknown album',
|
||||
smallImageKey: undefined,
|
||||
smallImageText: currentStatus,
|
||||
state: artists && `By ${artists}`,
|
||||
};
|
||||
|
||||
if (currentStatus === PlayerStatus.PLAYING) {
|
||||
if (start && end) {
|
||||
activity.startTimestamp = start;
|
||||
activity.endTimestamp = end;
|
||||
}
|
||||
|
||||
activity.smallImageKey = 'playing';
|
||||
} else {
|
||||
activity.smallImageKey = 'paused';
|
||||
}
|
||||
|
||||
if (
|
||||
currentSong?.serverType === ServerType.JELLYFIN &&
|
||||
discordSettings.showServerImage &&
|
||||
currentSong?.imageUrl
|
||||
) {
|
||||
activity.largeImageKey = currentSong?.imageUrl;
|
||||
}
|
||||
|
||||
// Fall back to default icon if not set
|
||||
if (!activity.largeImageKey) {
|
||||
activity.largeImageKey = 'icon';
|
||||
}
|
||||
|
||||
discordRpc?.setActivity(activity);
|
||||
}, [currentSong, currentStatus, discordSettings.enableIdle, discordSettings.showServerImage]);
|
||||
|
||||
useEffect(() => {
|
||||
const initializeDiscordRpc = async () => {
|
||||
discordRpc?.initialize(discordSettings.clientId);
|
||||
};
|
||||
|
||||
if (discordSettings.enabled) {
|
||||
initializeDiscordRpc();
|
||||
} else {
|
||||
discordRpc?.quit();
|
||||
}
|
||||
|
||||
return () => {
|
||||
discordRpc?.quit();
|
||||
};
|
||||
}, [discordSettings.clientId, discordSettings.enabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (discordSettings.enabled) {
|
||||
let intervalSeconds = discordSettings.updateInterval;
|
||||
if (intervalSeconds < 15) {
|
||||
intervalSeconds = 15;
|
||||
}
|
||||
|
||||
intervalRef.current = window.setInterval(setActivity, intervalSeconds * 1000);
|
||||
return () => clearInterval(intervalRef.current);
|
||||
}
|
||||
|
||||
return () => {};
|
||||
}, [discordSettings.enabled, discordSettings.updateInterval, setActivity]);
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log(
|
||||
// 'currentStatus, discordSettings.enableIdle',
|
||||
// currentStatus,
|
||||
// discordSettings.enableIdle,
|
||||
// );
|
||||
|
||||
// if (discordSettings.enableIdle === false && currentStatus === PlayerStatus.PAUSED) {
|
||||
// console.log('removing activity');
|
||||
// clearActivity();
|
||||
// clearInterval(intervalRef.current);
|
||||
// }
|
||||
// }, [
|
||||
// clearActivity,
|
||||
// currentStatus,
|
||||
// discordSettings.enableIdle,
|
||||
// discordSettings.enabled,
|
||||
// setActivity,
|
||||
// ]);
|
||||
};
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import isElectron from 'is-electron';
|
||||
import { NumberInput, Switch, TextInput } from '/@/renderer/components';
|
||||
import {
|
||||
SettingOption,
|
||||
SettingsSection,
|
||||
} from '/@/renderer/features/settings/components/settings-section';
|
||||
import { useDiscordSetttings, useSettingsStoreActions } from '/@/renderer/store';
|
||||
|
||||
export const DiscordSettings = () => {
|
||||
const settings = useDiscordSetttings();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
|
||||
const discordOptions: SettingOption[] = [
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
checked={settings.enabled}
|
||||
onChange={(e) => {
|
||||
setSettings({
|
||||
discord: {
|
||||
...settings,
|
||||
enabled: e.currentTarget.checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description:
|
||||
'Enable playback status in Discord rich presence. Image keys include: "icon", "playing", and "paused"',
|
||||
isHidden: !isElectron(),
|
||||
title: 'Discord rich presence',
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<TextInput
|
||||
defaultValue={settings.clientId}
|
||||
onBlur={(e) => {
|
||||
setSettings({
|
||||
discord: {
|
||||
...settings,
|
||||
clientId: e.currentTarget.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: 'The Discord application ID (defaults to 1165957668758900787)',
|
||||
isHidden: !isElectron(),
|
||||
title: 'Discord application ID',
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<NumberInput
|
||||
value={settings.updateInterval}
|
||||
onChange={(e) => {
|
||||
let value = e ? Number(e) : 0;
|
||||
if (value < 15) {
|
||||
value = 15;
|
||||
}
|
||||
|
||||
setSettings({
|
||||
discord: {
|
||||
...settings,
|
||||
updateInterval: value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: 'The time in seconds between each update (minimum 15 seconds)',
|
||||
isHidden: !isElectron(),
|
||||
title: 'Rich presence update interval (seconds)',
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
checked={settings.enableIdle}
|
||||
onChange={(e) => {
|
||||
setSettings({
|
||||
discord: {
|
||||
...settings,
|
||||
enableIdle: e.currentTarget.checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: 'When enabled, the rich presence will update while player is idle',
|
||||
isHidden: !isElectron(),
|
||||
title: 'Show rich presence when idle',
|
||||
},
|
||||
];
|
||||
|
||||
return <SettingsSection options={discordOptions} />;
|
||||
};
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
import { Divider, Stack } from '@mantine/core';
|
||||
import { UpdateSettings } from '/@/renderer/features/settings/components/window/update-settings';
|
||||
import { WindowSettings } from '/@/renderer/features/settings/components/window/window-settings';
|
||||
import { DiscordSettings } from '/@/renderer/features/settings/components/window/discord-settings';
|
||||
|
||||
export const WindowTab = () => {
|
||||
return (
|
||||
<Stack spacing="md">
|
||||
<WindowSettings />
|
||||
<Divider />
|
||||
<DiscordSettings />
|
||||
<Divider />
|
||||
<UpdateSettings />
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue