2022-12-19 15:59:14 -08:00
|
|
|
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';
|
|
|
|
|
import { MantineProvider } from '@mantine/core';
|
2025-06-24 00:04:36 -07:00
|
|
|
import { Notifications } from '@mantine/notifications';
|
2023-08-07 21:44:39 -07:00
|
|
|
import isElectron from 'is-electron';
|
2025-05-18 14:03:18 -07:00
|
|
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
2025-06-24 00:04:36 -07:00
|
|
|
import '@mantine/core/styles.css';
|
|
|
|
|
import '@mantine/notifications/styles.css';
|
|
|
|
|
import '@mantine/dates/styles.css';
|
2025-05-18 14:03:18 -07:00
|
|
|
|
2025-06-24 14:36:14 -07:00
|
|
|
import '/@/shared/styles/global.css';
|
2025-05-18 14:03:18 -07:00
|
|
|
|
|
|
|
|
import '@ag-grid-community/styles/ag-grid.css';
|
|
|
|
|
import 'overlayscrollbars/overlayscrollbars.css';
|
|
|
|
|
|
2025-06-24 14:36:14 -07:00
|
|
|
import '/styles/overlayscrollbars.css';
|
2025-05-20 19:23:36 -07:00
|
|
|
import i18n from '/@/i18n/i18n';
|
2025-05-18 14:03:18 -07:00
|
|
|
import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc';
|
2022-12-31 19:26:58 -08:00
|
|
|
import { PlayQueueHandlerContext } from '/@/renderer/features/player';
|
2024-09-09 01:25:01 +00:00
|
|
|
import { WebAudioContext } from '/@/renderer/features/player/context/webaudio-context';
|
2025-05-18 14:03:18 -07:00
|
|
|
import { useHandlePlayQueueAdd } from '/@/renderer/features/player/hooks/use-handle-playqueue-add';
|
2024-08-25 20:02:44 -07:00
|
|
|
import { updateSong } from '/@/renderer/features/player/update-remote-song';
|
2025-05-18 14:03:18 -07:00
|
|
|
import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-settings';
|
|
|
|
|
import { useServerVersion } from '/@/renderer/hooks/use-server-version';
|
|
|
|
|
import { IsUpdatedDialog } from '/@/renderer/is-updated-dialog';
|
2025-05-20 19:23:36 -07:00
|
|
|
import { AppRouter } from '/@/renderer/router/app-router';
|
|
|
|
|
import {
|
|
|
|
|
PlayerState,
|
|
|
|
|
useCssSettings,
|
|
|
|
|
useHotkeySettings,
|
|
|
|
|
usePlaybackSettings,
|
|
|
|
|
usePlayerStore,
|
|
|
|
|
useQueueControls,
|
|
|
|
|
useRemoteSettings,
|
|
|
|
|
useSettingsStore,
|
|
|
|
|
} from '/@/renderer/store';
|
2025-06-24 00:04:36 -07:00
|
|
|
import { useAppTheme } from '/@/renderer/themes/use-app-theme';
|
2024-08-27 08:26:34 -07:00
|
|
|
import { sanitizeCss } from '/@/renderer/utils/sanitize';
|
2024-09-01 08:26:30 -07:00
|
|
|
import { setQueue } from '/@/renderer/utils/set-transcoded-queue-data';
|
2025-06-24 00:04:36 -07:00
|
|
|
import { toast } from '/@/shared/components/toast/toast';
|
|
|
|
|
import { PlaybackType, PlayerStatus, WebAudio } from '/@/shared/types/types';
|
2022-12-19 15:59:14 -08:00
|
|
|
|
|
|
|
|
ModuleRegistry.registerModules([ClientSideRowModelModule, InfiniteRowModelModule]);
|
|
|
|
|
|
2025-05-18 14:03:18 -07:00
|
|
|
const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
|
|
|
|
|
const ipc = isElectron() ? window.api.ipc : null;
|
|
|
|
|
const remote = isElectron() ? window.api.remote : null;
|
|
|
|
|
const utils = isElectron() ? window.api.utils : null;
|
2023-04-02 21:41:32 -07:00
|
|
|
|
2022-12-19 15:59:14 -08:00
|
|
|
export const App = () => {
|
2025-06-24 00:04:36 -07:00
|
|
|
const { mode, theme } = useAppTheme();
|
2023-10-30 19:22:45 -07:00
|
|
|
const language = useSettingsStore((store) => store.general.language);
|
2025-06-24 00:04:36 -07:00
|
|
|
|
2025-05-18 14:03:18 -07:00
|
|
|
const { content, enabled } = useCssSettings();
|
2023-07-01 19:10:05 -07:00
|
|
|
const { type: playbackType } = usePlaybackSettings();
|
|
|
|
|
const { bindings } = useHotkeySettings();
|
|
|
|
|
const handlePlayQueueAdd = useHandlePlayQueueAdd();
|
|
|
|
|
const { clearQueue, restoreQueue } = useQueueControls();
|
2023-07-23 12:23:18 +00:00
|
|
|
const remoteSettings = useRemoteSettings();
|
2025-05-20 19:23:36 -07:00
|
|
|
const cssRef = useRef<HTMLStyleElement | null>(null);
|
2023-10-23 06:58:39 -07:00
|
|
|
useDiscordRpc();
|
2024-02-01 08:17:31 -08:00
|
|
|
useServerVersion();
|
2023-07-01 19:10:05 -07:00
|
|
|
|
2024-09-09 01:25:01 +00:00
|
|
|
const [webAudio, setWebAudio] = useState<WebAudio>();
|
|
|
|
|
|
2024-08-27 08:26:34 -07:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (enabled && content) {
|
|
|
|
|
// Yes, CSS is sanitized here as well. Prevent a suer from changing the
|
|
|
|
|
// localStorage to bypass sanitizing.
|
|
|
|
|
const sanitized = sanitizeCss(content);
|
|
|
|
|
if (!cssRef.current) {
|
|
|
|
|
cssRef.current = document.createElement('style');
|
|
|
|
|
document.body.appendChild(cssRef.current);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cssRef.current.textContent = sanitized;
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
cssRef.current!.textContent = '';
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {};
|
|
|
|
|
}, [content, enabled]);
|
|
|
|
|
|
2023-07-23 05:31:10 -07:00
|
|
|
const providerValue = useMemo(() => {
|
|
|
|
|
return { handlePlayQueueAdd };
|
|
|
|
|
}, [handlePlayQueueAdd]);
|
|
|
|
|
|
2024-09-09 01:25:01 +00:00
|
|
|
const webAudioProvider = useMemo(() => {
|
|
|
|
|
return { setWebAudio, webAudio };
|
|
|
|
|
}, [webAudio]);
|
|
|
|
|
|
2023-07-01 19:10:05 -07:00
|
|
|
// Start the mpv instance on startup
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const initializeMpv = async () => {
|
2024-02-11 13:56:29 -08:00
|
|
|
if (playbackType === PlaybackType.LOCAL) {
|
|
|
|
|
const isRunning: boolean | undefined = await mpvPlayer?.isRunning();
|
2023-07-01 19:10:05 -07:00
|
|
|
|
2024-02-11 13:56:29 -08:00
|
|
|
mpvPlayer?.stop();
|
2023-08-06 11:41:40 -07:00
|
|
|
|
2024-02-11 13:56:29 -08:00
|
|
|
if (!isRunning) {
|
|
|
|
|
const extraParameters = useSettingsStore.getState().playback.mpvExtraParameters;
|
|
|
|
|
const properties: Record<string, any> = {
|
2024-09-29 16:16:33 -07:00
|
|
|
speed: usePlayerStore.getState().speed,
|
2024-02-11 13:56:29 -08:00
|
|
|
...getMpvProperties(useSettingsStore.getState().playback.mpvProperties),
|
|
|
|
|
};
|
2023-07-01 19:10:05 -07:00
|
|
|
|
2024-02-11 13:56:29 -08:00
|
|
|
await mpvPlayer?.initialize({
|
|
|
|
|
extraParameters,
|
|
|
|
|
properties,
|
|
|
|
|
});
|
2023-07-01 19:10:05 -07:00
|
|
|
|
2024-02-11 13:56:29 -08:00
|
|
|
mpvPlayer?.volume(properties.volume);
|
|
|
|
|
}
|
2023-07-01 19:10:05 -07:00
|
|
|
}
|
2024-02-11 13:56:29 -08:00
|
|
|
|
|
|
|
|
utils?.restoreQueue();
|
2023-06-06 10:48:47 -07:00
|
|
|
};
|
2023-05-20 19:56:17 -07:00
|
|
|
|
2024-02-11 13:56:29 -08:00
|
|
|
if (isElectron()) {
|
2023-07-01 19:10:05 -07:00
|
|
|
initializeMpv();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
clearQueue();
|
|
|
|
|
mpvPlayer?.stop();
|
|
|
|
|
mpvPlayer?.cleanup();
|
2023-05-21 09:29:58 +00:00
|
|
|
};
|
2023-07-01 19:10:05 -07:00
|
|
|
}, [clearQueue, playbackType]);
|
2023-05-21 09:29:58 +00:00
|
|
|
|
2023-07-01 19:10:05 -07:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (isElectron()) {
|
|
|
|
|
ipc?.send('set-global-shortcuts', bindings);
|
|
|
|
|
}
|
|
|
|
|
}, [bindings]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2024-02-11 13:56:29 -08:00
|
|
|
if (utils) {
|
|
|
|
|
utils.onSaveQueue(() => {
|
2023-07-01 19:10:05 -07:00
|
|
|
const { current, queue } = usePlayerStore.getState();
|
|
|
|
|
const stateToSave: Partial<Pick<PlayerState, 'current' | 'queue'>> = {
|
|
|
|
|
current: {
|
|
|
|
|
...current,
|
|
|
|
|
status: PlayerStatus.PAUSED,
|
|
|
|
|
},
|
|
|
|
|
queue,
|
|
|
|
|
};
|
2024-02-11 13:56:29 -08:00
|
|
|
utils.saveQueue(stateToSave);
|
2023-07-01 19:10:05 -07:00
|
|
|
});
|
|
|
|
|
|
2024-02-11 13:56:29 -08:00
|
|
|
utils.onRestoreQueue((_event: any, data) => {
|
2023-07-01 19:10:05 -07:00
|
|
|
const playerData = restoreQueue(data);
|
|
|
|
|
if (playbackType === PlaybackType.LOCAL) {
|
2024-09-01 08:26:30 -07:00
|
|
|
setQueue(playerData, true);
|
2023-07-01 19:10:05 -07:00
|
|
|
}
|
2024-08-25 20:02:44 -07:00
|
|
|
updateSong(playerData.current.song);
|
2023-07-01 19:10:05 -07:00
|
|
|
});
|
2023-05-21 09:29:58 +00:00
|
|
|
}
|
2023-07-01 19:10:05 -07:00
|
|
|
|
|
|
|
|
return () => {
|
2024-02-11 13:56:29 -08:00
|
|
|
ipc?.removeAllListeners('renderer-restore-queue');
|
|
|
|
|
ipc?.removeAllListeners('renderer-save-queue');
|
2023-07-01 19:10:05 -07:00
|
|
|
};
|
|
|
|
|
}, [playbackType, restoreQueue]);
|
|
|
|
|
|
2023-07-23 12:23:18 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (remote) {
|
|
|
|
|
remote
|
|
|
|
|
?.updateSetting(
|
|
|
|
|
remoteSettings.enabled,
|
|
|
|
|
remoteSettings.port,
|
|
|
|
|
remoteSettings.username,
|
|
|
|
|
remoteSettings.password,
|
|
|
|
|
)
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
toast.warn({ message: error, title: 'Failed to enable remote' });
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// We only want to fire this once
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
}, []);
|
|
|
|
|
|
2023-10-30 19:22:45 -07:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (language) {
|
|
|
|
|
i18n.changeLanguage(language);
|
|
|
|
|
}
|
|
|
|
|
}, [language]);
|
|
|
|
|
|
2023-07-01 19:10:05 -07:00
|
|
|
return (
|
2025-07-12 11:17:54 -07:00
|
|
|
<MantineProvider defaultColorScheme={mode as 'dark' | 'light'} theme={theme}>
|
2025-10-04 07:34:48 -07:00
|
|
|
<Notifications
|
|
|
|
|
containerWidth="300px"
|
|
|
|
|
position="bottom-center"
|
|
|
|
|
styles={{
|
|
|
|
|
root: {
|
|
|
|
|
marginBottom: 90,
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
zIndex={50000}
|
|
|
|
|
/>
|
2024-04-17 14:29:46 +00:00
|
|
|
<PlayQueueHandlerContext.Provider value={providerValue}>
|
2025-09-06 00:45:59 -07:00
|
|
|
<WebAudioContext.Provider value={webAudioProvider}>
|
|
|
|
|
<AppRouter />
|
|
|
|
|
</WebAudioContext.Provider>
|
2024-04-17 14:29:46 +00:00
|
|
|
</PlayQueueHandlerContext.Provider>
|
2023-08-07 21:44:39 -07:00
|
|
|
<IsUpdatedDialog />
|
2023-07-01 19:10:05 -07:00
|
|
|
</MantineProvider>
|
|
|
|
|
);
|
2022-12-19 15:59:14 -08:00
|
|
|
};
|