temporarily remove old player implementations to prevent broken app

This commit is contained in:
jeffvli
2025-11-02 01:40:20 -08:00
parent 4e3a3742a5
commit 9abda23a4a
42 changed files with 2571 additions and 2598 deletions
+3 -3
View File
@@ -3,7 +3,7 @@ import { BrowserWindow, globalShortcut, systemPreferences } from 'electron';
import { isMacOS, isWindows } from '../../../utils'; import { isMacOS, isWindows } from '../../../utils';
import { store } from '../settings'; import { store } from '../settings';
import { PlaybackType } from '/@/shared/types/types'; import { PlayerType } from '/@/shared/types/types';
export const enableMediaKeys = (window: BrowserWindow | null) => { export const enableMediaKeys = (window: BrowserWindow | null) => {
if (isMacOS()) { if (isMacOS()) {
@@ -26,9 +26,9 @@ export const enableMediaKeys = (window: BrowserWindow | null) => {
} }
const enableWindowsMediaSession = store.get('mediaSession', false) as boolean; const enableWindowsMediaSession = store.get('mediaSession', false) as boolean;
const playbackType = store.get('playbackType', PlaybackType.WEB) as PlaybackType; const playbackType = store.get('playbackType', PlayerType.WEB) as PlayerType;
if (!enableWindowsMediaSession || !isWindows() || playbackType !== PlaybackType.WEB) { if (!enableWindowsMediaSession || !isWindows() || playbackType !== PlayerType.WEB) {
globalShortcut.register('MediaStop', () => { globalShortcut.register('MediaStop', () => {
window?.webContents.send('renderer-player-stop'); window?.webContents.send('renderer-player-stop');
}); });
+3 -3
View File
@@ -38,7 +38,7 @@ import {
} from './utils'; } from './utils';
import './features'; import './features';
import { PlaybackType, TitleTheme } from '/@/shared/types/types'; import { PlayerType, TitleTheme } from '/@/shared/types/types';
export default class AppUpdater { export default class AppUpdater {
constructor() { constructor() {
@@ -549,9 +549,9 @@ async function createWindow(first = true): Promise<void> {
} }
const enableWindowsMediaSession = store.get('mediaSession', false) as boolean; const enableWindowsMediaSession = store.get('mediaSession', false) as boolean;
const playbackType = store.get('playbackType', PlaybackType.WEB) as PlaybackType; const playbackType = store.get('playbackType', PlayerType.WEB) as PlayerType;
const shouldDisableMediaFeatures = const shouldDisableMediaFeatures =
!isWindows() || !enableWindowsMediaSession || playbackType !== PlaybackType.WEB; !isWindows() || !enableWindowsMediaSession || playbackType !== PlayerType.WEB;
if (shouldDisableMediaFeatures) { if (shouldDisableMediaFeatures) {
app.commandLine.appendSwitch( app.commandLine.appendSwitch(
'disable-features', 'disable-features',
+42 -52
View File
@@ -2,44 +2,37 @@ import { ClientSideRowModelModule } from '@ag-grid-community/client-side-row-mod
import { ModuleRegistry } from '@ag-grid-community/core'; import { ModuleRegistry } from '@ag-grid-community/core';
import { InfiniteRowModelModule } from '@ag-grid-community/infinite-row-model'; import { InfiniteRowModelModule } from '@ag-grid-community/infinite-row-model';
import { MantineProvider } from '@mantine/core'; import { MantineProvider } from '@mantine/core';
import '@mantine/core/styles.css';
import '@mantine/dates/styles.css';
import { Notifications } from '@mantine/notifications'; import { Notifications } from '@mantine/notifications';
import '@mantine/notifications/styles.css';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import { useEffect, useMemo, useRef, useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
import '@mantine/dates/styles.css';
import '/@/shared/styles/global.css'; import '/@/shared/styles/global.css';
import '@ag-grid-community/styles/ag-grid.css'; import '@ag-grid-community/styles/ag-grid.css';
import 'overlayscrollbars/overlayscrollbars.css'; import 'overlayscrollbars/overlayscrollbars.css';
import '/styles/overlayscrollbars.css';
import i18n from '/@/i18n/i18n'; import i18n from '/@/i18n/i18n';
import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc'; import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc';
import { PlayQueueHandlerContext } from '/@/renderer/features/player/context/play-queue-handler-context';
import { WebAudioContext } from '/@/renderer/features/player/context/webaudio-context'; import { WebAudioContext } from '/@/renderer/features/player/context/webaudio-context';
import { useHandlePlayQueueAdd } from '/@/renderer/features/player/hooks/use-handle-playqueue-add';
import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-settings'; import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-settings';
import { useServerVersion } from '/@/renderer/hooks/use-server-version'; import { useServerVersion } from '/@/renderer/hooks/use-server-version';
import { IsUpdatedDialog } from '/@/renderer/is-updated-dialog'; import { IsUpdatedDialog } from '/@/renderer/is-updated-dialog';
import { AppRouter } from '/@/renderer/router/app-router'; import { AppRouter } from '/@/renderer/router/app-router';
import { import {
PlayerState,
useCssSettings, useCssSettings,
useHotkeySettings, useHotkeySettings,
usePlaybackSettings, usePlaybackSettings,
usePlayerStore,
useQueueControls,
useRemoteSettings, useRemoteSettings,
useSettingsStore, useSettingsStore,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { useAppTheme } from '/@/renderer/themes/use-app-theme'; import { useAppTheme } from '/@/renderer/themes/use-app-theme';
import { sanitizeCss } from '/@/renderer/utils/sanitize'; import { sanitizeCss } from '/@/renderer/utils/sanitize';
import { setQueue } from '/@/renderer/utils/set-transcoded-queue-data';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { PlaybackType, PlayerStatus, WebAudio } from '/@/shared/types/types'; import { PlayerType, WebAudio } from '/@/shared/types/types';
import '/styles/overlayscrollbars.css';
ModuleRegistry.registerModules([ClientSideRowModelModule, InfiniteRowModelModule]); ModuleRegistry.registerModules([ClientSideRowModelModule, InfiniteRowModelModule]);
@@ -55,8 +48,8 @@ export const App = () => {
const { content, enabled } = useCssSettings(); const { content, enabled } = useCssSettings();
const { type: playbackType } = usePlaybackSettings(); const { type: playbackType } = usePlaybackSettings();
const { bindings } = useHotkeySettings(); const { bindings } = useHotkeySettings();
const handlePlayQueueAdd = useHandlePlayQueueAdd(); // const handlePlayQueueAdd = useHandlePlayQueueAdd();
const { clearQueue, restoreQueue } = useQueueControls(); // const { clearQueue, restoreQueue } = useQueueControls();
const remoteSettings = useRemoteSettings(); const remoteSettings = useRemoteSettings();
const cssRef = useRef<HTMLStyleElement | null>(null); const cssRef = useRef<HTMLStyleElement | null>(null);
useDiscordRpc(); useDiscordRpc();
@@ -84,9 +77,9 @@ export const App = () => {
return () => {}; return () => {};
}, [content, enabled]); }, [content, enabled]);
const providerValue = useMemo(() => { // const providerValue = useMemo(() => {
return { handlePlayQueueAdd }; // return { handlePlayQueueAdd };
}, [handlePlayQueueAdd]); // }, [handlePlayQueueAdd]);
const webAudioProvider = useMemo(() => { const webAudioProvider = useMemo(() => {
return { setWebAudio, webAudio }; return { setWebAudio, webAudio };
@@ -95,7 +88,7 @@ export const App = () => {
// Start the mpv instance on startup // Start the mpv instance on startup
useEffect(() => { useEffect(() => {
const initializeMpv = async () => { const initializeMpv = async () => {
if (playbackType === PlaybackType.LOCAL) { if (playbackType === PlayerType.LOCAL) {
const isRunning: boolean | undefined = await mpvPlayer?.isRunning(); const isRunning: boolean | undefined = await mpvPlayer?.isRunning();
mpvPlayer?.stop(); mpvPlayer?.stop();
@@ -103,7 +96,7 @@ export const App = () => {
if (!isRunning) { if (!isRunning) {
const extraParameters = useSettingsStore.getState().playback.mpvExtraParameters; const extraParameters = useSettingsStore.getState().playback.mpvExtraParameters;
const properties: Record<string, any> = { const properties: Record<string, any> = {
speed: usePlayerStore.getState().speed, // speed: usePlayerStore.getState().speed,
...getMpvProperties(useSettingsStore.getState().playback.mpvProperties), ...getMpvProperties(useSettingsStore.getState().playback.mpvProperties),
}; };
@@ -124,11 +117,10 @@ export const App = () => {
} }
return () => { return () => {
clearQueue();
mpvPlayer?.stop(); mpvPlayer?.stop();
mpvPlayer?.cleanup(); mpvPlayer?.cleanup();
}; };
}, [clearQueue, playbackType]); }, [playbackType]);
useEffect(() => { useEffect(() => {
if (isElectron()) { if (isElectron()) {
@@ -136,34 +128,34 @@ export const App = () => {
} }
}, [bindings]); }, [bindings]);
useEffect(() => { // useEffect(() => {
if (utils) { // if (utils) {
utils.onSaveQueue(() => { // utils.onSaveQueue(() => {
const { current, queue } = usePlayerStore.getState(); // const { current, queue } = usePlayerStore.getState();
const stateToSave: Partial<Pick<PlayerState, 'current' | 'queue'>> = { // const stateToSave: Partial<Pick<PlayerState, 'current' | 'queue'>> = {
current: { // current: {
...current, // ...current,
status: PlayerStatus.PAUSED, // status: PlayerStatus.PAUSED,
}, // },
queue, // queue,
}; // };
utils.saveQueue(stateToSave); // utils.saveQueue(stateToSave);
}); // });
utils.onRestoreQueue((_event: any, data) => { // utils.onRestoreQueue((_event: any, data) => {
const playerData = restoreQueue(data); // const playerData = restoreQueue(data);
if (playbackType === PlaybackType.LOCAL) { // if (playbackType === PlaybackType.LOCAL) {
setQueue(playerData, true); // setQueue(playerData, true);
} // }
updateSong(playerData.current.song); // updateSong(playerData.current.song);
}); // });
} // }
return () => { // return () => {
ipc?.removeAllListeners('renderer-restore-queue'); // ipc?.removeAllListeners('renderer-restore-queue');
ipc?.removeAllListeners('renderer-save-queue'); // ipc?.removeAllListeners('renderer-save-queue');
}; // };
}, [playbackType, restoreQueue]); // }, [playbackType, restoreQueue]);
useEffect(() => { useEffect(() => {
if (remote) { if (remote) {
@@ -200,11 +192,9 @@ export const App = () => {
}} }}
zIndex={50000} zIndex={50000}
/> />
<PlayQueueHandlerContext.Provider value={providerValue}> <WebAudioContext.Provider value={webAudioProvider}>
<WebAudioContext.Provider value={webAudioProvider}> <AppRouter />
<AppRouter /> </WebAudioContext.Provider>
</WebAudioContext.Provider>
</PlayQueueHandlerContext.Provider>
<IsUpdatedDialog /> <IsUpdatedDialog />
</MantineProvider> </MantineProvider>
); );
+12 -7
View File
@@ -20,10 +20,15 @@ import {
gaplessHandler, gaplessHandler,
} from '/@/renderer/components/audio-player/utils/list-handlers'; } from '/@/renderer/components/audio-player/utils/list-handlers';
import { useWebAudio } from '/@/renderer/features/player/hooks/use-webaudio'; import { useWebAudio } from '/@/renderer/features/player/hooks/use-webaudio';
import { TranscodingConfig, usePlaybackSettings, useSpeed } from '/@/renderer/store'; import {
import { useSettingsStore, useSettingsStoreActions } from '/@/renderer/store/settings.store'; TranscodingConfig,
usePlaybackSettings,
usePlayerSpeed,
useSettingsStore,
useSettingsStoreActions,
} from '/@/renderer/store';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { PlaybackStyle, PlayerStatus } from '/@/shared/types/types'; import { PlayerStatus, PlayerStyle } from '/@/shared/types/types';
export type AudioPlayerProgress = { export type AudioPlayerProgress = {
loaded: number; loaded: number;
@@ -38,7 +43,7 @@ interface AudioPlayerProps extends ReactPlayerProps {
crossfadeStyle: CrossfadeStyle; crossfadeStyle: CrossfadeStyle;
currentPlayer: 1 | 2; currentPlayer: 1 | 2;
muted: boolean; muted: boolean;
playbackStyle: PlaybackStyle; playbackStyle: PlayerStyle;
player1?: Song; player1?: Song;
player2?: Song; player2?: Song;
status: PlayerStatus; status: PlayerStatus;
@@ -122,7 +127,7 @@ export const AudioPlayer = forwardRef<AudioPlayerRef, AudioPlayerProps>((props,
const shouldUseWebAudio = useSettingsStore((state) => state.playback.webAudio); const shouldUseWebAudio = useSettingsStore((state) => state.playback.webAudio);
const preservesPitch = useSettingsStore((state) => state.playback.preservePitch); const preservesPitch = useSettingsStore((state) => state.playback.preservePitch);
const { resetSampleRate } = useSettingsStoreActions(); const { resetSampleRate } = useSettingsStoreActions();
const playbackSpeed = useSpeed(); const playbackSpeed = usePlayerSpeed();
const { transcode } = usePlaybackSettings(); const { transcode } = usePlaybackSettings();
const stream1 = useSongUrl(transcode, currentPlayer === 1, player1); const stream1 = useSongUrl(transcode, currentPlayer === 1, player1);
@@ -448,7 +453,7 @@ export const AudioPlayer = forwardRef<AudioPlayerRef, AudioPlayerProps>((props,
onEnded={stream1 ? handleOnEnded : undefined} onEnded={stream1 ? handleOnEnded : undefined}
onError={handleOnError(player1Ref)} onError={handleOnError(player1Ref)}
onProgress={ onProgress={
playbackStyle === PlaybackStyle.GAPLESS ? handleGapless1 : handleCrossfade1 playbackStyle === PlayerStyle.GAPLESS ? handleGapless1 : handleCrossfade1
} }
onReady={handlePlayer1Start} onReady={handlePlayer1Start}
playbackRate={playbackSpeed} playbackRate={playbackSpeed}
@@ -468,7 +473,7 @@ export const AudioPlayer = forwardRef<AudioPlayerRef, AudioPlayerProps>((props,
onEnded={stream2 ? handleOnEnded : undefined} onEnded={stream2 ? handleOnEnded : undefined}
onError={handleOnError(player2Ref)} onError={handleOnError(player2Ref)}
onProgress={ onProgress={
playbackStyle === PlaybackStyle.GAPLESS ? handleGapless2 : handleCrossfade2 playbackStyle === PlayerStyle.GAPLESS ? handleGapless2 : handleCrossfade2
} }
onReady={handlePlayer2Start} onReady={handlePlayer2Start}
playbackRate={playbackSpeed} playbackRate={playbackSpeed}
@@ -4,7 +4,7 @@ import { RowClassRules, RowNode } from '@ag-grid-community/core';
import { MutableRefObject, useEffect, useMemo, useRef } from 'react'; import { MutableRefObject, useEffect, useMemo, useRef } from 'react';
import { useAppFocus } from '/@/renderer/hooks'; import { useAppFocus } from '/@/renderer/hooks';
import { useCurrentSong, usePlayerStore } from '/@/renderer/store'; import { usePlayerSong, usePlayerStore } from '/@/renderer/store';
import { Song } from '/@/shared/types/domain-types'; import { Song } from '/@/shared/types/domain-types';
import { PlayerStatus } from '/@/shared/types/types'; import { PlayerStatus } from '/@/shared/types/types';
@@ -13,7 +13,7 @@ interface UseCurrentSongRowStylesProps {
} }
export const useCurrentSongRowStyles = ({ tableRef }: UseCurrentSongRowStylesProps) => { export const useCurrentSongRowStyles = ({ tableRef }: UseCurrentSongRowStylesProps) => {
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const isFocused = useAppFocus(); const isFocused = useAppFocus();
const isFocusedRef = useRef<boolean>(isFocused); const isFocusedRef = useRef<boolean>(isFocused);
@@ -36,7 +36,7 @@ import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-
import { useAppFocus, useContainerQuery } from '/@/renderer/hooks'; import { useAppFocus, useContainerQuery } from '/@/renderer/hooks';
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route'; import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer, useCurrentSong, useCurrentStatus } from '/@/renderer/store'; import { useCurrentServer, usePlayerSong, usePlayerStatus } from '/@/renderer/store';
import { import {
PersistedTableColumn, PersistedTableColumn,
useGeneralSettings, useGeneralSettings,
@@ -85,9 +85,9 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
const handlePlayQueueAdd = usePlayQueueAdd(); const handlePlayQueueAdd = usePlayQueueAdd();
const tableConfig = useTableSettings('albumDetail'); const tableConfig = useTableSettings('albumDetail');
const { setTable } = useSettingsStoreActions(); const { setTable } = useSettingsStoreActions();
const status = useCurrentStatus(); const status = usePlayerStatus();
const isFocused = useAppFocus(); const isFocused = useAppFocus();
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const { externalLinks, lastFM, musicBrainz } = useGeneralSettings(); const { externalLinks, lastFM, musicBrainz } = useGeneralSettings();
const genreRoute = useGenreRoute(); const genreRoute = useGenreRoute();
File diff suppressed because it is too large Load Diff
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { openLyricSearchModal } from '/@/renderer/features/lyrics/components/lyrics-search-form'; import { openLyricSearchModal } from '/@/renderer/features/lyrics/components/lyrics-search-form';
import { import {
useCurrentSong, usePlayerSong,
useLyricsSettings, useLyricsSettings,
useSettingsStore, useSettingsStore,
useSettingsStoreActions, useSettingsStoreActions,
@@ -38,7 +38,7 @@ export const LyricsActions = ({
setIndex, setIndex,
}: LyricsActionsProps) => { }: LyricsActionsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const { setSettings } = useSettingsStoreActions(); const { setSettings } = useSettingsStoreActions();
const { delayMs, sources } = useLyricsSettings(); const { delayMs, sources } = useLyricsSettings();
+2 -2
View File
@@ -20,7 +20,7 @@ import {
UnsynchronizedLyricsProps, UnsynchronizedLyricsProps,
} from '/@/renderer/features/lyrics/unsynchronized-lyrics'; } from '/@/renderer/features/lyrics/unsynchronized-lyrics';
import { queryClient } from '/@/renderer/lib/react-query'; import { queryClient } from '/@/renderer/lib/react-query';
import { useCurrentSong, useLyricsSettings, usePlayerStore } from '/@/renderer/store'; import { usePlayerSong, useLyricsSettings, usePlayerStore } from '/@/renderer/store';
import { Center } from '/@/shared/components/center/center'; import { Center } from '/@/shared/components/center/center';
import { Group } from '/@/shared/components/group/group'; import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon'; import { Icon } from '/@/shared/components/icon/icon';
@@ -29,7 +29,7 @@ import { Text } from '/@/shared/components/text/text';
import { FullLyricsMetadata, LyricSource, LyricsOverride } from '/@/shared/types/domain-types'; import { FullLyricsMetadata, LyricSource, LyricsOverride } from '/@/shared/types/domain-types';
export const Lyrics = () => { export const Lyrics = () => {
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const { const {
enableAutoTranslation, enableAutoTranslation,
translationApiKey, translationApiKey,
@@ -8,17 +8,16 @@ import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
import { useScrobble } from '/@/renderer/features/player/hooks/use-scrobble'; import { useScrobble } from '/@/renderer/features/player/hooks/use-scrobble';
import { PlayersRef } from '/@/renderer/features/player/ref/players-ref'; import { PlayersRef } from '/@/renderer/features/player/ref/players-ref';
import { import {
useCurrentPlayer,
useCurrentStatus,
useCurrentTime,
useLyricsSettings, useLyricsSettings,
usePlaybackType, usePlaybackType,
usePlayerActions,
usePlayerData, usePlayerData,
useSeeked, usePlayerNum,
useSetCurrentTime, usePlayerStatus,
usePlayerTimestamp,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { FullLyricsMetadata, SynchronizedLyricsArray } from '/@/shared/types/domain-types'; import { FullLyricsMetadata, SynchronizedLyricsArray } from '/@/shared/types/domain-types';
import { PlaybackType, PlayerStatus } from '/@/shared/types/types'; import { PlayerStatus, PlayerType } from '/@/shared/types/types';
const mpvPlayer = isElectron() ? window.api.mpvPlayer : null; const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
const utils = isElectron() ? window.api.utils : null; const utils = isElectron() ? window.api.utils : null;
@@ -38,33 +37,33 @@ export const SynchronizedLyrics = ({
translatedLyrics, translatedLyrics,
}: SynchronizedLyricsProps) => { }: SynchronizedLyricsProps) => {
const playersRef = PlayersRef; const playersRef = PlayersRef;
const status = useCurrentStatus(); const status = usePlayerStatus();
const playbackType = usePlaybackType(); const playbackType = usePlaybackType();
const playerData = usePlayerData(); const playerData = usePlayerData();
const now = useCurrentTime(); const now = usePlayerTimestamp();
const settings = useLyricsSettings(); const settings = useLyricsSettings();
const currentPlayer = useCurrentPlayer(); const currentPlayer = usePlayerNum();
const currentPlayerRef = const currentPlayerRef =
currentPlayer === 1 ? playersRef.current?.player1 : playersRef.current?.player2; currentPlayer === 1 ? playersRef.current?.player1 : playersRef.current?.player2;
const setCurrentTime = useSetCurrentTime();
const { handleScrobbleFromSeek } = useScrobble(); const { handleScrobbleFromSeek } = useScrobble();
const handleSeek = useCallback( const handleSeek = useCallback(
(time: number) => { (time: number) => {
if (playbackType === PlaybackType.LOCAL && mpvPlayer) { if (playbackType === PlayerType.LOCAL && mpvPlayer) {
mpvPlayer.seekTo(time); mpvPlayer.seekTo(time);
setCurrentTime(time, true); // setCurrentTime(time, true);
} else { } else {
setCurrentTime(time, true); // setCurrentTime(time, true);
handleScrobbleFromSeek(time); handleScrobbleFromSeek(time);
mpris?.updateSeek(time); mpris?.updateSeek(time);
currentPlayerRef?.seekTo(time); currentPlayerRef?.seekTo(time);
} }
}, },
[currentPlayerRef, handleScrobbleFromSeek, playbackType, setCurrentTime], [currentPlayerRef, handleScrobbleFromSeek, playbackType],
); );
const seeked = useSeeked(); // const seeked = useSeeked();
// A reference to the timeout handler // A reference to the timeout handler
const lyricTimer = useRef<null | ReturnType<typeof setTimeout>>(null); const lyricTimer = useRef<null | ReturnType<typeof setTimeout>>(null);
@@ -97,7 +96,7 @@ export const SynchronizedLyrics = ({
}; };
const getCurrentTime = useCallback(async () => { const getCurrentTime = useCallback(async () => {
if (isElectron() && playbackType !== PlaybackType.WEB) { if (isElectron() && playbackType !== PlayerType.WEB) {
if (mpvPlayer) { if (mpvPlayer) {
return mpvPlayer.getCurrentTime(); return mpvPlayer.getCurrentTime();
} }
@@ -109,7 +108,7 @@ export const SynchronizedLyrics = ({
} }
const player = const player =
playerData.current.player === 1 playerData.player.playerNum === 1
? playersRef.current.player1 ? playersRef.current.player1
: playersRef.current.player2; : playersRef.current.player2;
const underlying = player?.getInternalPlayer(); const underlying = player?.getInternalPlayer();
@@ -282,16 +281,16 @@ export const SynchronizedLyrics = ({
// we may be playing the same track (repeat one). In this case, we also // we may be playing the same track (repeat one). In this case, we also
// need to restart playback // need to restart playback
const restarted = status === PlayerStatus.PLAYING && now === 0; const restarted = status === PlayerStatus.PLAYING && now === 0;
if (!seeked && !restarted) { // if (!seeked && !restarted) {
return; // return;
} // }
if (lyricTimer.current) { if (lyricTimer.current) {
clearTimeout(lyricTimer.current); clearTimeout(lyricTimer.current);
} }
setCurrentLyric(now * 1000 - delayMsRef.current); setCurrentLyric(now * 1000 - delayMsRef.current);
}, [now, seeked, setCurrentLyric, status]); }, [now, setCurrentLyric, status]);
useEffect(() => { useEffect(() => {
// Guaranteed cleanup; stop the timer, and just in case also increment // Guaranteed cleanup; stop the timer, and just in case also increment
@@ -7,15 +7,12 @@ import { useTranslation } from 'react-i18next';
import { TableConfigDropdown } from '/@/renderer/components/virtual-table'; import { TableConfigDropdown } from '/@/renderer/components/virtual-table';
import { updateSong } from '/@/renderer/features/player/update-remote-song'; import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { SearchInput } from '/@/renderer/features/shared/components/search-input'; import { SearchInput } from '/@/renderer/features/shared/components/search-input';
import { usePlayerControls, useQueueControls } from '/@/renderer/store';
import { usePlayerStore, useSetCurrentTime } from '/@/renderer/store/player.store';
import { usePlaybackType } from '/@/renderer/store/settings.store'; import { usePlaybackType } from '/@/renderer/store/settings.store';
import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Group } from '/@/shared/components/group/group'; import { Group } from '/@/shared/components/group/group';
import { Popover } from '/@/shared/components/popover/popover'; import { Popover } from '/@/shared/components/popover/popover';
import { Song } from '/@/shared/types/domain-types'; import { Song } from '/@/shared/types/domain-types';
import { PlaybackType, TableType } from '/@/shared/types/types'; import { TableType } from '/@/shared/types/types';
const mpvPlayer = isElectron() ? window.api.mpvPlayer : null; const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
@@ -33,30 +30,30 @@ export const PlayQueueListControls = ({
type, type,
}: PlayQueueListOptionsProps) => { }: PlayQueueListOptionsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { // const {
clearQueue, // clearQueue,
moveToBottomOfQueue, // moveToBottomOfQueue,
moveToNextOfQueue, // moveToNextOfQueue,
moveToTopOfQueue, // moveToTopOfQueue,
removeFromQueue, // removeFromQueue,
shuffleQueue, // shuffleQueue,
} = useQueueControls(); // } = useQueueControls();
const { pause } = usePlayerControls(); // const { pause } = usePlayerControls();
const playbackType = usePlaybackType(); const playbackType = usePlaybackType();
const setCurrentTime = useSetCurrentTime(); // const setCurrentTime = useSetCurrentTime();
const handleMoveToNext = () => { const handleMoveToNext = () => {
const selectedRows = tableRef?.current?.grid.api.getSelectedRows(); const selectedRows = tableRef?.current?.grid.api.getSelectedRows();
const uniqueIds = selectedRows?.map((row) => row.uniqueId); const uniqueIds = selectedRows?.map((row) => row.uniqueId);
if (!uniqueIds?.length) return; if (!uniqueIds?.length) return;
const playerData = moveToNextOfQueue(uniqueIds); // const playerData = moveToNextOfQueue(uniqueIds);
if (playbackType === PlaybackType.LOCAL) { // if (playbackType === PlaybackType.LOCAL) {
setQueueNext(playerData); // setQueueNext(playerData);
} // }
}; };
const handleMoveToBottom = () => { const handleMoveToBottom = () => {
@@ -64,11 +61,11 @@ export const PlayQueueListControls = ({
const uniqueIds = selectedRows?.map((row) => row.uniqueId); const uniqueIds = selectedRows?.map((row) => row.uniqueId);
if (!uniqueIds?.length) return; if (!uniqueIds?.length) return;
const playerData = moveToBottomOfQueue(uniqueIds); // const playerData = moveToBottomOfQueue(uniqueIds);
if (playbackType === PlaybackType.LOCAL) { // if (playbackType === PlaybackType.LOCAL) {
setQueueNext(playerData); // setQueueNext(playerData);
} // }
}; };
const handleMoveToTop = () => { const handleMoveToTop = () => {
@@ -76,11 +73,11 @@ export const PlayQueueListControls = ({
const uniqueIds = selectedRows?.map((row) => row.uniqueId); const uniqueIds = selectedRows?.map((row) => row.uniqueId);
if (!uniqueIds?.length) return; if (!uniqueIds?.length) return;
const playerData = moveToTopOfQueue(uniqueIds); // const playerData = moveToTopOfQueue(uniqueIds);
if (playbackType === PlaybackType.LOCAL) { // if (playbackType === PlaybackType.LOCAL) {
setQueueNext(playerData); // setQueueNext(playerData);
} // }
}; };
const handleRemoveSelected = () => { const handleRemoveSelected = () => {
@@ -88,43 +85,42 @@ export const PlayQueueListControls = ({
const uniqueIds = selectedRows?.map((row) => row.uniqueId); const uniqueIds = selectedRows?.map((row) => row.uniqueId);
if (!uniqueIds?.length) return; if (!uniqueIds?.length) return;
const currentSong = usePlayerStore.getState().current.song; // const currentSong = usePlayerStore.getState().current.song;
const playerData = removeFromQueue(uniqueIds); // const playerData = removeFromQueue(uniqueIds);
const isCurrentSongRemoved = currentSong && uniqueIds.includes(currentSong.uniqueId); // const isCurrentSongRemoved = currentSong && uniqueIds.includes(currentSong.uniqueId);
if (playbackType === PlaybackType.LOCAL) { // if (playbackType === PlaybackType.LOCAL) {
if (isCurrentSongRemoved) { // if (isCurrentSongRemoved) {
setQueue(playerData); // setQueue(playerData);
} else { // } else {
setQueueNext(playerData); // setQueueNext(playerData);
} // }
} // }
if (isCurrentSongRemoved) { // if (isCurrentSongRemoved) {
updateSong(playerData.current.song); // updateSong(playerData.current.song);
} // }
}; };
const handleClearQueue = () => { const handleClearQueue = () => {
const playerData = clearQueue(); // const playerData = clearQueue();
if (playbackType === PlaybackType.LOCAL) { // if (playbackType === PlaybackType.LOCAL) {
setQueue(playerData); // setQueue(playerData);
mpvPlayer!.pause(); // mpvPlayer!.pause();
} // }
updateSong(undefined); updateSong(undefined);
setCurrentTime(0); // setCurrentTime(0);
pause(); // pause();
}; };
const handleShuffleQueue = () => { const handleShuffleQueue = () => {
const playerData = shuffleQueue(); // const playerData = shuffleQueue();
// if (playbackType === PlaybackType.LOCAL) {
if (playbackType === PlaybackType.LOCAL) { // setQueueNext(playerData);
setQueueNext(playerData); // }
}
}; };
const handleSearchTerm = useCallback( const handleSearchTerm = useCallback(
@@ -24,13 +24,10 @@ import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { useAppFocus } from '/@/renderer/hooks'; import { useAppFocus } from '/@/renderer/hooks';
import { import {
useAppStoreActions, useAppStoreActions,
useCurrentSong, usePlayerQueue,
useCurrentStatus, usePlayerSong,
useDefaultQueue, usePlayerStatus,
usePlayerControls, usePlayerVolume,
usePreviousSong,
useQueueControls,
useVolume,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { import {
PersistedTableColumn, PersistedTableColumn,
@@ -42,7 +39,7 @@ import {
import { searchSongs } from '/@/renderer/utils/search-songs'; import { searchSongs } from '/@/renderer/utils/search-songs';
import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data'; import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data';
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types'; import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
import { PlaybackType, TableType } from '/@/shared/types/types'; import { PlayerType, TableType } from '/@/shared/types/types';
const mpvPlayer = isElectron() ? window.api.mpvPlayer : null; const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
@@ -54,18 +51,18 @@ type QueueProps = {
export const PlayQueue = forwardRef(({ searchTerm, type }: QueueProps, ref: Ref<any>) => { export const PlayQueue = forwardRef(({ searchTerm, type }: QueueProps, ref: Ref<any>) => {
const tableRef = useRef<AgGridReactType | null>(null); const tableRef = useRef<AgGridReactType | null>(null);
const mergedRef = useMergedRef(ref, tableRef); const mergedRef = useMergedRef(ref, tableRef);
const queue = useDefaultQueue(); const queue = usePlayerQueue();
const { reorderQueue, setCurrentTrack } = useQueueControls(); // const { reorderQueue, setCurrentTrack } = useQueueControls();
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const previousSong = usePreviousSong(); // const previousSong = usePreviousSong();
const status = useCurrentStatus(); const status = usePlayerStatus();
const { setSettings } = useSettingsStoreActions(); const { setSettings } = useSettingsStoreActions();
const { setAppStore } = useAppStoreActions(); const { setAppStore } = useAppStoreActions();
const tableConfig = useTableSettings(type); const tableConfig = useTableSettings(type);
const [gridApi, setGridApi] = useState<AgGridReactType | undefined>(); const [gridApi, setGridApi] = useState<AgGridReactType | undefined>();
const playbackType = usePlaybackType(); const playbackType = usePlaybackType();
const { play } = usePlayerControls(); // const { play } = usePlayerControls();
const volume = useVolume(); const volume = usePlayerVolume();
const isFocused = useAppFocus(); const isFocused = useAppFocus();
const isFocusedRef = useRef<boolean>(isFocused); const isFocusedRef = useRef<boolean>(isFocused);
@@ -94,26 +91,26 @@ export const PlayQueue = forwardRef(({ searchTerm, type }: QueueProps, ref: Ref<
[tableConfig.columns], [tableConfig.columns],
); );
const handleDoubleClick = (e: CellDoubleClickedEvent) => { // const handleDoubleClick = (e: CellDoubleClickedEvent) => {
const playerData = setCurrentTrack(e.data.uniqueId); // const playerData = setCurrentTrack(e.data.uniqueId);
updateSong(playerData.current.song); // updateSong(playerData.current.song);
if (playbackType === PlaybackType.LOCAL) { // if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.volume(volume); // mpvPlayer!.volume(volume);
setQueue(playerData, false); // setQueue(playerData, false);
} else { // } else {
const player = // const player =
playerData.current.player === 1 // playerData.current.player === 1
? PlayersRef.current?.player1 // ? PlayersRef.current?.player1
: PlayersRef.current?.player2; // : PlayersRef.current?.player2;
const underlying = player?.getInternalPlayer(); // const underlying = player?.getInternalPlayer();
if (underlying) { // if (underlying) {
underlying.currentTime = 0; // underlying.currentTime = 0;
} // }
} // }
play(); // play();
}; // };
const handleDragStart = () => { const handleDragStart = () => {
if (type === 'sideDrawerQueue') { if (type === 'sideDrawerQueue') {
@@ -128,11 +125,11 @@ export const PlayQueue = forwardRef(({ searchTerm, type }: QueueProps, ref: Ref<
.map((node) => node.data?.uniqueId) .map((node) => node.data?.uniqueId)
.filter((e) => e !== undefined); .filter((e) => e !== undefined);
const playerData = reorderQueue(selectedUniqueIds as string[], e.overNode?.data?.uniqueId); // const playerData = reorderQueue(selectedUniqueIds as string[], e.overNode?.data?.uniqueId);
if (playbackType === PlaybackType.LOCAL) { // if (playbackType === PlaybackType.LOCAL) {
setQueueNext(playerData); // setQueueNext(playerData);
} // }
if (type === 'sideDrawerQueue') { if (type === 'sideDrawerQueue') {
setAppStore({ isReorderingQueue: false }); setAppStore({ isReorderingQueue: false });
@@ -204,6 +201,14 @@ export const PlayQueue = forwardRef(({ searchTerm, type }: QueueProps, ref: Ref<
}; };
}, [currentSong?.uniqueId]); }, [currentSong?.uniqueId]);
const previousSongRef = useRef<QueueSong | undefined>(undefined);
useEffect(() => {
if (currentSong) {
previousSongRef.current = currentSong;
}
}, [currentSong]);
// Redraw the current song row when the previous song changes // Redraw the current song row when the previous song changes
useEffect(() => { useEffect(() => {
if (tableRef?.current) { if (tableRef?.current) {
@@ -215,8 +220,8 @@ export const PlayQueue = forwardRef(({ searchTerm, type }: QueueProps, ref: Ref<
const currentNode = currentSong?.uniqueId const currentNode = currentSong?.uniqueId
? api.getRowNode(currentSong.uniqueId) ? api.getRowNode(currentSong.uniqueId)
: undefined; : undefined;
const previousNode = previousSong?.uniqueId const previousNode = previousSongRef.current?.uniqueId
? api.getRowNode(previousSong?.uniqueId) ? api.getRowNode(previousSongRef.current?.uniqueId)
: undefined; : undefined;
const rowNodes = [currentNode, previousNode].filter( const rowNodes = [currentNode, previousNode].filter(
@@ -231,7 +236,7 @@ export const PlayQueue = forwardRef(({ searchTerm, type }: QueueProps, ref: Ref<
} }
} }
} }
}, [currentSong, previousSong, tableConfig.followCurrentSong, status]); }, [currentSong, previousSongRef, tableConfig.followCurrentSong, status]);
// As a separate rule, update the current row when focus changes. This is // As a separate rule, update the current row when focus changes. This is
// to prevent queue scrolling when the application loses and then gains focus. // to prevent queue scrolling when the application loses and then gains focus.
@@ -266,7 +271,7 @@ export const PlayQueue = forwardRef(({ searchTerm, type }: QueueProps, ref: Ref<
columnDefs={columnDefs} columnDefs={columnDefs}
context={{ context={{
currentSong, currentSong,
handleDoubleClick, // handleDoubleClick,
isFocused, isFocused,
isQueue: true, isQueue: true,
itemType: LibraryItem.SONG, itemType: LibraryItem.SONG,
@@ -276,7 +281,7 @@ export const PlayQueue = forwardRef(({ searchTerm, type }: QueueProps, ref: Ref<
deselectOnClickOutside={type === 'fullScreen'} deselectOnClickOutside={type === 'fullScreen'}
getRowId={(data) => data.data.uniqueId} getRowId={(data) => data.data.uniqueId}
onCellContextMenu={onCellContextMenu} onCellContextMenu={onCellContextMenu}
onCellDoubleClicked={handleDoubleClick} // onCellDoubleClicked={handleDoubleClick}
onColumnMoved={handleColumnChange} onColumnMoved={handleColumnChange}
onColumnResized={debouncedColumnChange} onColumnResized={debouncedColumnChange}
onDragStarted={handleDragStart} onDragStarted={handleDragStart}
@@ -1,4 +1,3 @@
import { useHotkeys } from '@mantine/hooks';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import formatDuration from 'format-duration'; import formatDuration from 'format-duration';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
@@ -10,27 +9,24 @@ import styles from './center-controls.module.css';
import { PlayButton, PlayerButton } from '/@/renderer/features/player/components/player-button'; import { PlayButton, PlayerButton } from '/@/renderer/features/player/components/player-button';
import { PlayerbarSlider } from '/@/renderer/features/player/components/playerbar-slider'; import { PlayerbarSlider } from '/@/renderer/features/player/components/playerbar-slider';
import { openShuffleAllModal } from '/@/renderer/features/player/components/shuffle-all-modal'; import { openShuffleAllModal } from '/@/renderer/features/player/components/shuffle-all-modal';
import { useCenterControls } from '/@/renderer/features/player/hooks/use-center-controls';
import { useMediaSession } from '/@/renderer/features/player/hooks/use-media-session';
import { usePlayQueueAdd } from '/@/renderer/features/player/hooks/use-playqueue-add'; import { usePlayQueueAdd } from '/@/renderer/features/player/hooks/use-playqueue-add';
import { import {
useAppStore, useAppStore,
useAppStoreActions, useAppStoreActions,
useCurrentPlayer,
useCurrentSong,
useCurrentStatus,
useCurrentTime,
useHotkeySettings, useHotkeySettings,
usePlaybackType, usePlaybackType,
useRepeatStatus, usePlayerNum,
useSetCurrentTime, usePlayerRepeat,
usePlayerShuffle,
usePlayerSong,
usePlayerStatus,
usePlayerTimestamp,
useSettingsStore, useSettingsStore,
useShuffleStatus,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { Icon } from '/@/shared/components/icon/icon'; import { Icon } from '/@/shared/components/icon/icon';
import { Text } from '/@/shared/components/text/text'; import { Text } from '/@/shared/components/text/text';
import { PlaybackSelectors } from '/@/shared/constants/playback-selectors'; import { PlaybackSelectors } from '/@/shared/constants/playback-selectors';
import { PlaybackType, PlayerRepeat, PlayerShuffle, PlayerStatus } from '/@/shared/types/types'; import { PlayerRepeat, PlayerShuffle, PlayerStatus, PlayerType } from '/@/shared/types/types';
interface CenterControlsProps { interface CenterControlsProps {
playersRef: any; playersRef: any;
@@ -40,38 +36,38 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [isSeeking, setIsSeeking] = useState(false); const [isSeeking, setIsSeeking] = useState(false);
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const skip = useSettingsStore((state) => state.general.skipButtons); const skip = useSettingsStore((state) => state.general.skipButtons);
const buttonSize = useSettingsStore((state) => state.general.buttonSize); const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const playbackType = usePlaybackType(); const playbackType = usePlaybackType();
const player1 = playersRef?.current?.player1; const player1 = playersRef?.current?.player1;
const player2 = playersRef?.current?.player2; const player2 = playersRef?.current?.player2;
const status = useCurrentStatus(); const status = usePlayerStatus();
const player = useCurrentPlayer(); const player = usePlayerNum();
const setCurrentTime = useSetCurrentTime(); // const setCurrentTime = useSetCurrentTime();
const repeat = useRepeatStatus(); const repeat = usePlayerRepeat();
const shuffle = useShuffleStatus(); const shuffle = usePlayerShuffle();
const { bindings } = useHotkeySettings(); const { bindings } = useHotkeySettings();
const { showTimeRemaining } = useAppStore(); const { showTimeRemaining } = useAppStore();
const { setShowTimeRemaining } = useAppStoreActions(); const { setShowTimeRemaining } = useAppStoreActions();
const { // const {
handleNextTrack, // handleNextTrack,
handlePause, // handlePause,
handlePlay, // handlePlay,
handlePlayPause, // handlePlayPause,
handlePrevTrack, // handlePrevTrack,
handleSeekSlider, // handleSeekSlider,
handleSkipBackward, // handleSkipBackward,
handleSkipForward, // handleSkipForward,
handleStop, // handleStop,
handleToggleRepeat, // handleToggleRepeat,
handleToggleShuffle, // handleToggleShuffle,
} = useCenterControls({ playersRef }); // } = useCenterControls({ playersRef });
const handlePlayQueueAdd = usePlayQueueAdd(); const handlePlayQueueAdd = usePlayQueueAdd();
const songDuration = currentSong?.duration ? currentSong.duration / 1000 : 0; const songDuration = currentSong?.duration ? currentSong.duration / 1000 : 0;
const currentTime = useCurrentTime(); const currentTime = usePlayerTimestamp();
const currentPlayerRef = player === 1 ? player1 : player2; const currentPlayerRef = player === 1 ? player1 : player2;
const formattedDuration = formatDuration(songDuration * 1000 || 0); const formattedDuration = formatDuration(songDuration * 1000 || 0);
const formattedTimeRemaining = formatDuration((currentTime - songDuration) * 1000 || 0); const formattedTimeRemaining = formatDuration((currentTime - songDuration) * 1000 || 0);
@@ -81,50 +77,50 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
let interval: ReturnType<typeof setInterval>; let interval: ReturnType<typeof setInterval>;
if (status === PlayerStatus.PLAYING && !isSeeking) { if (status === PlayerStatus.PLAYING && !isSeeking) {
if (!isElectron() || playbackType === PlaybackType.WEB) { if (!isElectron() || playbackType === PlayerType.WEB) {
// Update twice a second for slightly better performance // Update twice a second for slightly better performance
interval = setInterval(() => { interval = setInterval(() => {
if (currentPlayerRef) { if (currentPlayerRef) {
setCurrentTime(currentPlayerRef.getCurrentTime()); // setCurrentTime(currentPlayerRef.getCurrentTime());
} }
}, 500); }, 500);
} }
} }
return () => clearInterval(interval); return () => clearInterval(interval);
}, [currentPlayerRef, isSeeking, setCurrentTime, playbackType, status]); }, [currentPlayerRef, isSeeking, playbackType, status]);
const [seekValue, setSeekValue] = useState(0); const [seekValue, setSeekValue] = useState(0);
useHotkeys([ // useHotkeys([
[bindings.playPause.isGlobal ? '' : bindings.playPause.hotkey, handlePlayPause], // [bindings.playPause.isGlobal ? '' : bindings.playPause.hotkey, handlePlayPause],
[bindings.play.isGlobal ? '' : bindings.play.hotkey, handlePlay], // [bindings.play.isGlobal ? '' : bindings.play.hotkey, handlePlay],
[bindings.pause.isGlobal ? '' : bindings.pause.hotkey, handlePause], // [bindings.pause.isGlobal ? '' : bindings.pause.hotkey, handlePause],
[bindings.stop.isGlobal ? '' : bindings.stop.hotkey, handleStop], // [bindings.stop.isGlobal ? '' : bindings.stop.hotkey, handleStop],
[bindings.next.isGlobal ? '' : bindings.next.hotkey, handleNextTrack], // [bindings.next.isGlobal ? '' : bindings.next.hotkey, handleNextTrack],
[bindings.previous.isGlobal ? '' : bindings.previous.hotkey, handlePrevTrack], // [bindings.previous.isGlobal ? '' : bindings.previous.hotkey, handlePrevTrack],
[bindings.toggleRepeat.isGlobal ? '' : bindings.toggleRepeat.hotkey, handleToggleRepeat], // [bindings.toggleRepeat.isGlobal ? '' : bindings.toggleRepeat.hotkey, handleToggleRepeat],
[bindings.toggleShuffle.isGlobal ? '' : bindings.toggleShuffle.hotkey, handleToggleShuffle], // [bindings.toggleShuffle.isGlobal ? '' : bindings.toggleShuffle.hotkey, handleToggleShuffle],
[ // [
bindings.skipBackward.isGlobal ? '' : bindings.skipBackward.hotkey, // bindings.skipBackward.isGlobal ? '' : bindings.skipBackward.hotkey,
() => handleSkipBackward(skip?.skipBackwardSeconds || 5), // () => handleSkipBackward(skip?.skipBackwardSeconds || 5),
], // ],
[ // [
bindings.skipForward.isGlobal ? '' : bindings.skipForward.hotkey, // bindings.skipForward.isGlobal ? '' : bindings.skipForward.hotkey,
() => handleSkipForward(skip?.skipForwardSeconds || 5), // () => handleSkipForward(skip?.skipForwardSeconds || 5),
], // ],
]); // ]);
useMediaSession({ // useMediaSession({
handleNextTrack, // handleNextTrack,
handlePause, // handlePause,
handlePlay, // handlePlay,
handlePrevTrack, // handlePrevTrack,
handleSeekSlider, // handleSeekSlider,
handleSkipBackward, // handleSkipBackward,
handleSkipForward, // handleSkipForward,
handleStop, // handleStop,
}); // });
return ( return (
<> <>
@@ -132,7 +128,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
<div className={styles.buttonsContainer}> <div className={styles.buttonsContainer}>
<PlayerButton <PlayerButton
icon={<Icon fill="default" icon="mediaStop" size={buttonSize - 2} />} icon={<Icon fill="default" icon="mediaStop" size={buttonSize - 2} />}
onClick={handleStop} // onClick={handleStop}
tooltip={{ tooltip={{
label: t('player.stop', { postProcess: 'sentenceCase' }), label: t('player.stop', { postProcess: 'sentenceCase' }),
openDelay: 0, openDelay: 0,
@@ -148,7 +144,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
/> />
} }
isActive={shuffle !== PlayerShuffle.NONE} isActive={shuffle !== PlayerShuffle.NONE}
onClick={handleToggleShuffle} // onClick={handleToggleShuffle}
tooltip={{ tooltip={{
label: label:
shuffle === PlayerShuffle.NONE shuffle === PlayerShuffle.NONE
@@ -163,7 +159,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
/> />
<PlayerButton <PlayerButton
icon={<Icon fill="default" icon="mediaPrevious" size={buttonSize} />} icon={<Icon fill="default" icon="mediaPrevious" size={buttonSize} />}
onClick={handlePrevTrack} // onClick={handlePrevTrack}
tooltip={{ tooltip={{
label: t('player.previous', { postProcess: 'sentenceCase' }), label: t('player.previous', { postProcess: 'sentenceCase' }),
openDelay: 0, openDelay: 0,
@@ -175,7 +171,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
icon={ icon={
<Icon fill="default" icon="mediaStepBackward" size={buttonSize} /> <Icon fill="default" icon="mediaStepBackward" size={buttonSize} />
} }
onClick={() => handleSkipBackward(skip?.skipBackwardSeconds)} // onClick={() => handleSkipBackward(skip?.skipBackwardSeconds)}
tooltip={{ tooltip={{
label: t('player.skip', { label: t('player.skip', {
context: 'back', context: 'back',
@@ -190,12 +186,12 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
<PlayButton <PlayButton
disabled={currentSong?.id === undefined} disabled={currentSong?.id === undefined}
isPaused={status === PlayerStatus.PAUSED} isPaused={status === PlayerStatus.PAUSED}
onClick={handlePlayPause} // onClick={handlePlayPause}
/> />
{skip?.enabled && ( {skip?.enabled && (
<PlayerButton <PlayerButton
icon={<Icon fill="default" icon="mediaStepForward" size={buttonSize} />} icon={<Icon fill="default" icon="mediaStepForward" size={buttonSize} />}
onClick={() => handleSkipForward(skip?.skipForwardSeconds)} // onClick={() => handleSkipForward(skip?.skipForwardSeconds)}
tooltip={{ tooltip={{
label: t('player.skip', { label: t('player.skip', {
context: 'forward', context: 'forward',
@@ -209,7 +205,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
)} )}
<PlayerButton <PlayerButton
icon={<Icon fill="default" icon="mediaNext" size={buttonSize} />} icon={<Icon fill="default" icon="mediaNext" size={buttonSize} />}
onClick={handleNextTrack} // onClick={handleNextTrack}
tooltip={{ tooltip={{
label: t('player.next', { postProcess: 'sentenceCase' }), label: t('player.next', { postProcess: 'sentenceCase' }),
openDelay: 0, openDelay: 0,
@@ -229,7 +225,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
) )
} }
isActive={repeat !== PlayerRepeat.NONE} isActive={repeat !== PlayerRepeat.NONE}
onClick={handleToggleRepeat} // onClick={handleToggleRepeat}
tooltip={{ tooltip={{
label: `${ label: `${
repeat === PlayerRepeat.NONE repeat === PlayerRepeat.NONE
@@ -294,7 +290,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
// event fires before onChange. Add a small delay to force // event fires before onChange. Add a small delay to force
// onChangeEnd to happen after onCHange // onChangeEnd to happen after onCHange
setTimeout(() => { setTimeout(() => {
handleSeekSlider(e); // handleSeekSlider(e);
setIsSeeking(false); setIsSeeking(false);
}, 50); }, 50);
}} }}
@@ -15,7 +15,7 @@ import {
} from '/@/renderer/store/full-screen-player.store'; } from '/@/renderer/store/full-screen-player.store';
import { Button } from '/@/shared/components/button/button'; import { Button } from '/@/shared/components/button/button';
import { Group } from '/@/shared/components/group/group'; import { Group } from '/@/shared/components/group/group';
import { PlaybackType } from '/@/shared/types/types'; import { PlayerType } from '/@/shared/types/types';
const Visualizer = lazy(() => const Visualizer = lazy(() =>
import('/@/renderer/features/player/components/visualizer').then((module) => ({ import('/@/renderer/features/player/components/visualizer').then((module) => ({
@@ -48,7 +48,7 @@ export const FullScreenPlayerQueue = () => {
}, },
]; ];
if (type === PlaybackType.WEB && webAudio) { if (type === PlayerType.WEB && webAudio) {
items.push({ items.push({
active: activeTab === 'visualizer', active: activeTab === 'visualizer',
label: t('page.fullscreenPlayer.visualizer', { postProcess: 'titleCase' }), label: t('page.fullscreenPlayer.visualizer', { postProcess: 'titleCase' }),
@@ -107,7 +107,7 @@ export const FullScreenPlayerQueue = () => {
</div> </div>
) : activeTab === 'lyrics' ? ( ) : activeTab === 'lyrics' ? (
<Lyrics /> <Lyrics />
) : activeTab === 'visualizer' && type === PlaybackType.WEB && webAudio ? ( ) : activeTab === 'visualizer' && type === PlayerType.WEB && webAudio ? (
<Suspense fallback={<></>}> <Suspense fallback={<></>}>
<Visualizer /> <Visualizer />
</Suspense> </Suspense>
@@ -11,7 +11,7 @@ import { FullScreenPlayerImage } from '/@/renderer/features/player/components/fu
import { FullScreenPlayerQueue } from '/@/renderer/features/player/components/full-screen-player-queue'; import { FullScreenPlayerQueue } from '/@/renderer/features/player/components/full-screen-player-queue';
import { useFastAverageColor } from '/@/renderer/hooks'; import { useFastAverageColor } from '/@/renderer/hooks';
import { import {
useCurrentSong, usePlayerSong,
useFullScreenPlayerStore, useFullScreenPlayerStore,
useFullScreenPlayerStoreActions, useFullScreenPlayerStoreActions,
useLyricsSettings, useLyricsSettings,
@@ -421,7 +421,7 @@ export const FullScreenPlayer = () => {
isOpenedRef.current = true; isOpenedRef.current = true;
}, [location, setStore]); }, [location, setStore]);
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const { background } = useFastAverageColor({ const { background } = useFastAverageColor({
algorithm: 'dominant', algorithm: 'dominant',
src: currentSong?.imageUrl, src: currentSong?.imageUrl,
@@ -1,8 +1,8 @@
import { SimilarSongsList } from '/@/renderer/features/similar-songs/components/similar-songs-list'; import { SimilarSongsList } from '/@/renderer/features/similar-songs/components/similar-songs-list';
import { useCurrentSong } from '/@/renderer/store'; import { usePlayerSong } from '/@/renderer/store';
export const FullScreenSimilarSongs = () => { export const FullScreenSimilarSongs = () => {
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
return currentSong?.id ? <SimilarSongsList fullScreen song={currentSong} /> : null; return currentSong?.id ? <SimilarSongsList fullScreen song={currentSong} /> : null;
}; };
@@ -12,7 +12,7 @@ import { useHandleGeneralContextMenu } from '/@/renderer/features/context-menu/h
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { import {
useAppStoreActions, useAppStoreActions,
useCurrentSong, usePlayerSong,
useFullScreenPlayerStore, useFullScreenPlayerStore,
useHotkeySettings, useHotkeySettings,
useSetFullScreenPlayerStore, useSetFullScreenPlayerStore,
@@ -34,7 +34,7 @@ export const LeftControls = () => {
const setFullScreenPlayerStore = useSetFullScreenPlayerStore(); const setFullScreenPlayerStore = useSetFullScreenPlayerStore();
const { collapsed, image } = useSidebarStore(); const { collapsed, image } = useSidebarStore();
const hideImage = image && !collapsed; const hideImage = image && !collapsed;
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const title = currentSong?.name; const title = currentSong?.name;
const artists = currentSong?.artists; const artists = currentSong?.artists;
const { bindings } = useHotkeySettings(); const { bindings } = useHotkeySettings();
@@ -1,5 +1,5 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { MouseEvent, useCallback } from 'react'; import { MouseEvent } from 'react';
import styles from './playerbar.module.css'; import styles from './playerbar.module.css';
@@ -9,17 +9,17 @@ import { LeftControls } from '/@/renderer/features/player/components/left-contro
import { RightControls } from '/@/renderer/features/player/components/right-controls'; import { RightControls } from '/@/renderer/features/player/components/right-controls';
import { usePowerSaveBlocker } from '/@/renderer/features/player/hooks/use-power-save-blocker'; import { usePowerSaveBlocker } from '/@/renderer/features/player/hooks/use-power-save-blocker';
import { PlayersRef } from '/@/renderer/features/player/ref/players-ref'; import { PlayersRef } from '/@/renderer/features/player/ref/players-ref';
import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { import {
useCurrentPlayer,
useCurrentStatus,
useFullScreenPlayerStore, useFullScreenPlayerStore,
useMuted, usePlayerData,
usePlayer1Data, // usePlayer1Data,
usePlayer2Data, // usePlayer2Data,
usePlayerControls, // usePlayerControls,
usePlayerMuted,
usePlayerNum,
usePlayerStatus,
usePlayerVolume,
useSetFullScreenPlayerStore, useSetFullScreenPlayerStore,
useVolume,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { import {
useGeneralSettings, useGeneralSettings,
@@ -27,20 +27,20 @@ import {
useSettingsStore, useSettingsStore,
} from '/@/renderer/store/settings.store'; } from '/@/renderer/store/settings.store';
import { PlaybackSelectors } from '/@/shared/constants/playback-selectors'; import { PlaybackSelectors } from '/@/shared/constants/playback-selectors';
import { PlaybackType } from '/@/shared/types/types'; import { PlayerType } from '/@/shared/types/types';
export const Playerbar = () => { export const Playerbar = () => {
const playersRef = PlayersRef; const playersRef = PlayersRef;
const settings = useSettingsStore((state) => state.playback); const settings = useSettingsStore((state) => state.playback);
const { playerbarOpenDrawer } = useGeneralSettings(); const { playerbarOpenDrawer } = useGeneralSettings();
const playbackType = usePlaybackType(); const playbackType = usePlaybackType();
const volume = useVolume(); const volume = usePlayerVolume();
const player1 = usePlayer1Data(); // const player1 = usePlayer1Data();
const player2 = usePlayer2Data(); // const player2 = usePlayer2Data();
const status = useCurrentStatus(); const status = usePlayerStatus();
const player = useCurrentPlayer(); const player = usePlayerNum();
const muted = useMuted(); const muted = usePlayerMuted();
const { autoNext } = usePlayerControls(); // const { autoNext } = usePlayerControls();
const { expanded: isFullScreenPlayerExpanded } = useFullScreenPlayerStore(); const { expanded: isFullScreenPlayerExpanded } = useFullScreenPlayerStore();
const setFullScreenPlayerStore = useSetFullScreenPlayerStore(); const setFullScreenPlayerStore = useSetFullScreenPlayerStore();
@@ -51,10 +51,12 @@ export const Playerbar = () => {
setFullScreenPlayerStore({ expanded: !isFullScreenPlayerExpanded }); setFullScreenPlayerStore({ expanded: !isFullScreenPlayerExpanded });
}; };
const autoNextFn = useCallback(() => { const { player1, player2 } = usePlayerData();
const playerData = autoNext();
updateSong(playerData.current.song); // const autoNextFn = useCallback(() => {
}, [autoNext]); // const playerData = autoNext();
// updateSong(playerData.current.song);
// }, [autoNext]);
return ( return (
<div <div
@@ -72,9 +74,9 @@ export const Playerbar = () => {
<RightControls /> <RightControls />
</div> </div>
</div> </div>
{playbackType === PlaybackType.WEB && ( {playbackType === PlayerType.WEB && (
<AudioPlayer <AudioPlayer
autoNext={autoNextFn} // autoNext={autoNextFn}
crossfadeDuration={settings.crossfadeDuration} crossfadeDuration={settings.crossfadeDuration}
crossfadeStyle={settings.crossfadeStyle} crossfadeStyle={settings.crossfadeStyle}
currentPlayer={player} currentPlayer={player}
@@ -11,17 +11,16 @@ import { useSetRating } from '/@/renderer/features/shared/mutations/set-rating-m
import { import {
useAppStoreActions, useAppStoreActions,
useCurrentServer, useCurrentServer,
useCurrentSong,
useHotkeySettings, useHotkeySettings,
useMuted,
usePlaybackSettings, usePlaybackSettings,
usePlaybackType, usePlaybackType,
usePreviousSong, usePlayerMuted,
usePlayerSong,
usePlayerSpeed,
usePlayerVolume,
useSettingsStore, useSettingsStore,
useSettingsStoreActions, useSettingsStoreActions,
useSidebarStore, useSidebarStore,
useSpeed,
useVolume,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu'; import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
@@ -40,11 +39,11 @@ const remote = isElectron() ? window.api.remote : null;
export const RightControls = () => { export const RightControls = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const isMinWidth = useMediaQuery('(max-width: 480px)'); const isMinWidth = useMediaQuery('(max-width: 480px)');
const volume = useVolume(); const volume = usePlayerVolume();
const muted = useMuted(); const muted = usePlayerMuted();
const server = useCurrentServer(); const server = useCurrentServer();
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const previousSong = usePreviousSong(); // const previousSong = usePreviousSong();
const { setSideBar } = useAppStoreActions(); const { setSideBar } = useAppStoreActions();
const { rightExpanded: isQueueExpanded } = useSidebarStore(); const { rightExpanded: isQueueExpanded } = useSidebarStore();
const { bindings } = useHotkeySettings(); const { bindings } = useHotkeySettings();
@@ -60,7 +59,7 @@ export const RightControls = () => {
const playbackSettings = usePlaybackSettings(); const playbackSettings = usePlaybackSettings();
const playbackType = usePlaybackType(); const playbackType = usePlaybackType();
const speed = useSpeed(); const speed = usePlayerSpeed();
const volumeWidth = useSettingsStore((state) => state.general.volumeWidth); const volumeWidth = useSettingsStore((state) => state.general.volumeWidth);
const speedPreservePitch = useSettingsStore((state) => state.playback.preservePitch); const speedPreservePitch = useSettingsStore((state) => state.playback.preservePitch);
@@ -148,18 +147,18 @@ export const RightControls = () => {
bindings.favoriteCurrentToggle.isGlobal ? '' : bindings.favoriteCurrentToggle.hotkey, bindings.favoriteCurrentToggle.isGlobal ? '' : bindings.favoriteCurrentToggle.hotkey,
() => handleToggleFavorite(currentSong), () => handleToggleFavorite(currentSong),
], ],
[ // [
bindings.favoritePreviousAdd.isGlobal ? '' : bindings.favoritePreviousAdd.hotkey, // bindings.favoritePreviousAdd.isGlobal ? '' : bindings.favoritePreviousAdd.hotkey,
() => handleAddToFavorites(previousSong), // () => handleAddToFavorites(previousSong),
], // ],
[ // [
bindings.favoritePreviousRemove.isGlobal ? '' : bindings.favoritePreviousRemove.hotkey, // bindings.favoritePreviousRemove.isGlobal ? '' : bindings.favoritePreviousRemove.hotkey,
() => handleRemoveFromFavorites(previousSong), // () => handleRemoveFromFavorites(previousSong),
], // ],
[ // [
bindings.favoritePreviousToggle.isGlobal ? '' : bindings.favoritePreviousToggle.hotkey, // bindings.favoritePreviousToggle.isGlobal ? '' : bindings.favoritePreviousToggle.hotkey,
() => handleToggleFavorite(previousSong), // () => handleToggleFavorite(previousSong),
], // ],
[bindings.rate0.isGlobal ? '' : bindings.rate0.hotkey, () => handleUpdateRating(0)], [bindings.rate0.isGlobal ? '' : bindings.rate0.hotkey, () => handleUpdateRating(0)],
[bindings.rate1.isGlobal ? '' : bindings.rate1.hotkey, () => handleUpdateRating(1)], [bindings.rate1.isGlobal ? '' : bindings.rate1.hotkey, () => handleUpdateRating(1)],
[bindings.rate2.isGlobal ? '' : bindings.rate2.hotkey, () => handleUpdateRating(2)], [bindings.rate2.isGlobal ? '' : bindings.rate2.hotkey, () => handleUpdateRating(2)],
File diff suppressed because it is too large Load Diff
@@ -1,226 +1,226 @@
import { useQueryClient } from '@tanstack/react-query'; // import { useQueryClient } from '@tanstack/react-query';
import isElectron from 'is-electron'; // import isElectron from 'is-electron';
import { nanoid } from 'nanoid/non-secure'; // import { nanoid } from 'nanoid/non-secure';
import { useCallback, useRef } from 'react'; // import { useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next'; // import { useTranslation } from 'react-i18next';
import { queryKeys } from '/@/renderer/api/query-keys'; // import { queryKeys } from '/@/renderer/api/query-keys';
import { PlayersRef } from '/@/renderer/features/player/ref/players-ref'; // import { PlayersRef } from '/@/renderer/features/player/ref/players-ref';
import { updateSong } from '/@/renderer/features/player/update-remote-song'; // import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { // import {
getAlbumArtistSongsById, // getAlbumArtistSongsById,
getAlbumSongsById, // getAlbumSongsById,
getArtistSongsById, // getArtistSongsById,
getGenreSongsById, // getGenreSongsById,
getPlaylistSongsById, // getPlaylistSongsById,
getSongById, // getSongById,
getSongsByQuery, // getSongsByQuery,
} from '/@/renderer/features/player/utils'; // } from '/@/renderer/features/player/utils';
import { useCurrentServer, usePlayerControls, usePlayerStore } from '/@/renderer/store'; // import { useCurrentServer, usePlayerStore } from '/@/renderer/store';
import { useGeneralSettings, usePlaybackType } from '/@/renderer/store/settings.store'; // import { useGeneralSettings, usePlaybackType } from '/@/renderer/store/settings.store';
import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data'; // import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data';
import { toast } from '/@/shared/components/toast/toast'; // import { toast } from '/@/shared/components/toast/toast';
import { // import {
instanceOfCancellationError, // instanceOfCancellationError,
LibraryItem, // LibraryItem,
QueueSong, // QueueSong,
Song, // Song,
SongListResponse, // SongListResponse,
} from '/@/shared/types/domain-types'; // } from '/@/shared/types/domain-types';
import { Play, PlaybackType, PlayQueueAddOptions } from '/@/shared/types/types'; // import { Play, PlaybackType, PlayQueueAddOptions } from '/@/shared/types/types';
const getRootQueryKey = (itemType: LibraryItem, serverId: string) => { // const getRootQueryKey = (itemType: LibraryItem, serverId: string) => {
let queryKey; // let queryKey;
switch (itemType) { // switch (itemType) {
case LibraryItem.ALBUM: // case LibraryItem.ALBUM:
queryKey = queryKeys.songs.list(serverId); // queryKey = queryKeys.songs.list(serverId);
break; // break;
case LibraryItem.ALBUM_ARTIST: // case LibraryItem.ALBUM_ARTIST:
queryKey = queryKeys.songs.list(serverId); // queryKey = queryKeys.songs.list(serverId);
break; // break;
case LibraryItem.GENRE: // case LibraryItem.GENRE:
queryKey = queryKeys.songs.list(serverId); // queryKey = queryKeys.songs.list(serverId);
break; // break;
case LibraryItem.PLAYLIST: // case LibraryItem.PLAYLIST:
queryKey = queryKeys.playlists.songList(serverId); // queryKey = queryKeys.playlists.songList(serverId);
break; // break;
case LibraryItem.SONG: // case LibraryItem.SONG:
queryKey = queryKeys.songs.list(serverId); // queryKey = queryKeys.songs.list(serverId);
break; // break;
default: // default:
queryKey = queryKeys.songs.list(serverId); // queryKey = queryKeys.songs.list(serverId);
break; // break;
} // }
return queryKey; // return queryKey;
}; // };
const mpvPlayer = isElectron() ? window.api.mpvPlayer : null; // const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
const addToQueue = usePlayerStore.getState().actions.addToQueue; // const addToQueue = usePlayerStore.getState().actions.addToQueue;
export const useHandlePlayQueueAdd = () => { // export const useHandlePlayQueueAdd = () => {
const { t } = useTranslation(); // const { t } = useTranslation();
const queryClient = useQueryClient(); // const queryClient = useQueryClient();
const playbackType = usePlaybackType(); // const playbackType = usePlaybackType();
const server = useCurrentServer(); // const server = useCurrentServer();
const { play } = usePlayerControls(); // const { play } = usePlayerControls();
const timeoutIds = useRef<null | Record<string, ReturnType<typeof setTimeout>>>({}); // const timeoutIds = useRef<null | Record<string, ReturnType<typeof setTimeout>>>({});
const { doubleClickQueueAll } = useGeneralSettings(); // const { doubleClickQueueAll } = useGeneralSettings();
const handlePlayQueueAdd = useCallback( // const handlePlayQueueAdd = useCallback(
async (options: PlayQueueAddOptions) => { // async (options: PlayQueueAddOptions) => {
if (!server) return toast.error({ message: 'No server selected', type: 'error' }); // if (!server) return toast.error({ message: 'No server selected', type: 'error' });
const { byData, byItemType, initialIndex, initialSongId, playType, query } = options; // const { byData, byItemType, initialIndex, initialSongId, playType, query } = options;
let songs: null | QueueSong[] = null; // let songs: null | QueueSong[] = null;
// Allow this to be undefined for "play shuffled". If undefined, default to 0, // // Allow this to be undefined for "play shuffled". If undefined, default to 0,
// otherwise, choose the selected item in the queue // // otherwise, choose the selected item in the queue
let initialSongIndex: number | undefined; // let initialSongIndex: number | undefined;
let toastId: null | string = null; // let toastId: null | string = null;
if (byItemType) { // if (byItemType) {
let songList: SongListResponse | undefined; // let songList: SongListResponse | undefined;
const { id, type: itemType } = byItemType; // const { id, type: itemType } = byItemType;
const fetchId = nanoid(); // const fetchId = nanoid();
timeoutIds.current = { // timeoutIds.current = {
...timeoutIds.current, // ...timeoutIds.current,
[fetchId]: setTimeout(() => { // [fetchId]: setTimeout(() => {
toastId = toast.info({ // toastId = toast.info({
autoClose: false, // autoClose: false,
message: t('player.playbackFetchCancel', { // message: t('player.playbackFetchCancel', {
postProcess: 'sentenceCase', // postProcess: 'sentenceCase',
}), // }),
onClose: () => { // onClose: () => {
queryClient.cancelQueries({ // queryClient.cancelQueries({
exact: false, // exact: false,
queryKey: getRootQueryKey(itemType, server?.id), // queryKey: getRootQueryKey(itemType, server?.id),
}); // });
}, // },
title: t('player.playbackFetchInProgress', { // title: t('player.playbackFetchInProgress', {
postProcess: 'sentenceCase', // postProcess: 'sentenceCase',
}), // }),
}); // });
}, 2000), // }, 2000),
}; // };
try { // try {
if (itemType === LibraryItem.PLAYLIST) { // if (itemType === LibraryItem.PLAYLIST) {
songList = await getPlaylistSongsById({ // songList = await getPlaylistSongsById({
id: id?.[0], // id: id?.[0],
query, // query,
queryClient, // queryClient,
server, // server,
}); // });
} else if (itemType === LibraryItem.ALBUM) { // } else if (itemType === LibraryItem.ALBUM) {
songList = await getAlbumSongsById({ id, query, queryClient, server }); // songList = await getAlbumSongsById({ id, query, queryClient, server });
} else if (itemType === LibraryItem.ALBUM_ARTIST) { // } else if (itemType === LibraryItem.ALBUM_ARTIST) {
songList = await getAlbumArtistSongsById({ // songList = await getAlbumArtistSongsById({
id, // id,
query, // query,
queryClient, // queryClient,
server, // server,
}); // });
} else if (itemType === LibraryItem.ARTIST) { // } else if (itemType === LibraryItem.ARTIST) {
songList = await getArtistSongsById({ // songList = await getArtistSongsById({
id, // id,
query, // query,
queryClient, // queryClient,
server, // server,
}); // });
} else if (itemType === LibraryItem.GENRE) { // } else if (itemType === LibraryItem.GENRE) {
songList = await getGenreSongsById({ id, query, queryClient, server }); // songList = await getGenreSongsById({ id, query, queryClient, server });
} else if (itemType === LibraryItem.SONG) { // } else if (itemType === LibraryItem.SONG) {
if (id?.length === 1) { // if (id?.length === 1) {
songList = await getSongById({ id: id?.[0], queryClient, server }); // songList = await getSongById({ id: id?.[0], queryClient, server });
} else if (!doubleClickQueueAll && initialSongId) { // } else if (!doubleClickQueueAll && initialSongId) {
songList = await getSongById({ // songList = await getSongById({
id: initialSongId, // id: initialSongId,
queryClient, // queryClient,
server, // server,
}); // });
} else { // } else {
songList = await getSongsByQuery({ query, queryClient, server }); // songList = await getSongsByQuery({ query, queryClient, server });
} // }
} // }
clearTimeout(timeoutIds.current[fetchId] as ReturnType<typeof setTimeout>); // clearTimeout(timeoutIds.current[fetchId] as ReturnType<typeof setTimeout>);
delete timeoutIds.current[fetchId]; // delete timeoutIds.current[fetchId];
if (toastId) { // if (toastId) {
toast.hide(toastId); // toast.hide(toastId);
} // }
} catch (err: any) { // } catch (err: any) {
if (instanceOfCancellationError(err)) { // if (instanceOfCancellationError(err)) {
return null; // return null;
} // }
clearTimeout(timeoutIds.current[fetchId] as ReturnType<typeof setTimeout>); // clearTimeout(timeoutIds.current[fetchId] as ReturnType<typeof setTimeout>);
delete timeoutIds.current[fetchId]; // delete timeoutIds.current[fetchId];
toast.hide(fetchId); // toast.hide(fetchId);
return toast.error({ // return toast.error({
message: err.message, // message: err.message,
title: t('error.genericError', { postProcess: 'sentenceCase' }) as string, // title: t('error.genericError', { postProcess: 'sentenceCase' }) as string,
}); // });
} // }
songs = // songs =
songList?.items?.map((song: Song) => ({ ...song, uniqueId: nanoid() })) || null; // songList?.items?.map((song: Song) => ({ ...song, uniqueId: nanoid() })) || null;
} else if (byData) { // } else if (byData) {
songs = byData.map((song) => ({ ...song, uniqueId: nanoid() })) || null; // songs = byData.map((song) => ({ ...song, uniqueId: nanoid() })) || null;
} // }
if (!songs || songs?.length === 0) // if (!songs || songs?.length === 0)
return toast.warn({ // return toast.warn({
message: t('common.noResultsFromQuery', { postProcess: 'sentenceCase' }), // message: t('common.noResultsFromQuery', { postProcess: 'sentenceCase' }),
title: t('player.playbackFetchNoResults', { postProcess: 'sentenceCase' }), // title: t('player.playbackFetchNoResults', { postProcess: 'sentenceCase' }),
}); // });
if (initialIndex) { // if (initialIndex) {
initialSongIndex = initialIndex; // initialSongIndex = initialIndex;
} else if (initialSongId) { // } else if (initialSongId) {
initialSongIndex = songs.findIndex((song) => song.id === initialSongId); // initialSongIndex = songs.findIndex((song) => song.id === initialSongId);
} // }
const hadSong = usePlayerStore.getState().queue.default.length > 0; // const hadSong = usePlayerStore.getState().queue.default.length > 0;
const playerData = addToQueue({ initialIndex: initialSongIndex, playType, songs }); // const playerData = addToQueue({ initialIndex: initialSongIndex, playType, songs });
updateSong(playerData.current.song); // updateSong(playerData.current.song);
const replacesQueue = playType === Play.NOW || playType === Play.SHUFFLE; // const replacesQueue = playType === Play.NOW || playType === Play.SHUFFLE;
if (playbackType === PlaybackType.LOCAL) { // if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.volume(usePlayerStore.getState().volume); // mpvPlayer!.volume(usePlayerStore.getState().volume);
if (replacesQueue || !hadSong) { // if (replacesQueue || !hadSong) {
mpvPlayer!.pause(); // mpvPlayer!.pause();
setQueue(playerData, false); // setQueue(playerData, false);
} else { // } else {
setQueueNext(playerData); // setQueueNext(playerData);
} // }
} else { // } else {
const player = // const player =
playerData.current.player === 1 // playerData.current.player === 1
? PlayersRef.current?.player1 // ? PlayersRef.current?.player1
: PlayersRef.current?.player2; // : PlayersRef.current?.player2;
const underlying = player?.getInternalPlayer(); // const underlying = player?.getInternalPlayer();
if (underlying && replacesQueue) { // if (underlying && replacesQueue) {
underlying.currentTime = 0; // underlying.currentTime = 0;
} // }
} // }
// We should only play if the queue was empty, or we are doing play NOW // // We should only play if the queue was empty, or we are doing play NOW
// (override the queue). // // (override the queue).
if (replacesQueue || !hadSong) { // if (replacesQueue || !hadSong) {
play(); // play();
} // }
return null; // return null;
}, // },
[doubleClickQueueAll, play, playbackType, queryClient, server, t], // [doubleClickQueueAll, play, playbackType, queryClient, server, t],
); // );
return handlePlayQueueAdd; // return handlePlayQueueAdd;
}; // };
@@ -1,8 +1,8 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { import {
useCurrentSong, usePlayerSong,
useCurrentStatus, usePlayerStatus,
usePlaybackSettings, usePlaybackSettings,
useSettingsStore, useSettingsStore,
} from '/@/renderer/store'; } from '/@/renderer/store';
@@ -28,8 +28,8 @@ export const useMediaSession = ({
handleStop: () => void; handleStop: () => void;
}) => { }) => {
const { mediaSession: mediaSessionEnabled } = usePlaybackSettings(); const { mediaSession: mediaSessionEnabled } = usePlaybackSettings();
const playerStatus = useCurrentStatus(); const playerStatus = usePlayerStatus();
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const mediaSession = navigator.mediaSession; const mediaSession = navigator.mediaSession;
const skip = useSettingsStore((state) => state.general.skipButtons); const skip = useSettingsStore((state) => state.general.skipButtons);
@@ -1,14 +1,14 @@
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect } from 'react';
import { useCurrentStatus } from '/@/renderer/store'; import { usePlayerStatus } from '/@/renderer/store';
import { useWindowSettings } from '/@/renderer/store'; import { useWindowSettings } from '/@/renderer/store';
import { PlayerStatus } from '/@/shared/types/types'; import { PlayerStatus } from '/@/shared/types/types';
const ipc = isElectron() ? window.api.ipc : null; const ipc = isElectron() ? window.api.ipc : null;
export const usePowerSaveBlocker = () => { export const usePowerSaveBlocker = () => {
const status = useCurrentStatus(); const status = usePlayerStatus();
const { preventSleepOnPlayback } = useWindowSettings(); const { preventSleepOnPlayback } = useWindowSettings();
const startPowerSaveBlocker = useCallback(async () => { const startPowerSaveBlocker = useCallback(async () => {
@@ -1,13 +1,7 @@
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import { useCallback, useEffect, WheelEvent } from 'react'; import { useCallback, useEffect, WheelEvent } from 'react';
import { import { usePlayerMuted, usePlayerSpeed, usePlayerVolume } from '/@/renderer/store';
useMuted,
usePlayerControls,
useSetCurrentSpeed,
useSpeed,
useVolume,
} from '/@/renderer/store';
import { useGeneralSettings } from '/@/renderer/store/settings.store'; import { useGeneralSettings } from '/@/renderer/store/settings.store';
const mpvPlayer = isElectron() ? window.api.mpvPlayer : null; const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
@@ -40,12 +34,12 @@ const calculateVolumeDown = (volume: number, volumeWheelStep: number) => {
}; };
export const useRightControls = () => { export const useRightControls = () => {
const { setMuted, setVolume } = usePlayerControls(); // const { setMuted, setVolume } = usePlayerControls();
const volume = useVolume(); const volume = usePlayerVolume();
const muted = useMuted(); const muted = usePlayerMuted();
const { volumeWheelStep } = useGeneralSettings(); const { volumeWheelStep } = useGeneralSettings();
const speed = useSpeed(); const speed = usePlayerSpeed();
const setCurrentSpeed = useSetCurrentSpeed(); // const setCurrentSpeed = useSetCurrentSpeed();
// Ensure that the mpv player volume is set on startup // Ensure that the mpv player volume is set on startup
useEffect(() => { useEffect(() => {
@@ -63,40 +57,37 @@ export const useRightControls = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const handleSpeed = useCallback( const handleSpeed = useCallback((e: number) => {
(e: number) => { // setCurrentSpeed(e);
setCurrentSpeed(e); if (mpvPlayer) {
if (mpvPlayer) { mpvPlayer?.setProperties({ speed: e });
mpvPlayer?.setProperties({ speed: e }); }
} }, []);
},
[setCurrentSpeed],
);
const handleVolumeSlider = (e: number) => { const handleVolumeSlider = (e: number) => {
mpvPlayer?.volume(e); mpvPlayer?.volume(e);
remote?.updateVolume(e); remote?.updateVolume(e);
setVolume(e); // setVolume(e);
}; };
const handleVolumeSliderState = (e: number) => { const handleVolumeSliderState = (e: number) => {
remote?.updateVolume(e); remote?.updateVolume(e);
setVolume(e); // setVolume(e);
}; };
const handleVolumeDown = useCallback(() => { const handleVolumeDown = useCallback(() => {
const volumeToSet = calculateVolumeDown(volume, volumeWheelStep); const volumeToSet = calculateVolumeDown(volume, volumeWheelStep);
mpvPlayer?.volume(volumeToSet); mpvPlayer?.volume(volumeToSet);
remote?.updateVolume(volumeToSet); remote?.updateVolume(volumeToSet);
setVolume(volumeToSet); // setVolume(volumeToSet);
}, [setVolume, volume, volumeWheelStep]); }, [volume, volumeWheelStep]);
const handleVolumeUp = useCallback(() => { const handleVolumeUp = useCallback(() => {
const volumeToSet = calculateVolumeUp(volume, volumeWheelStep); const volumeToSet = calculateVolumeUp(volume, volumeWheelStep);
mpvPlayer?.volume(volumeToSet); mpvPlayer?.volume(volumeToSet);
remote?.updateVolume(volumeToSet); remote?.updateVolume(volumeToSet);
setVolume(volumeToSet); // setVolume(volumeToSet);
}, [setVolume, volume, volumeWheelStep]); }, [volume, volumeWheelStep]);
const handleVolumeWheel = useCallback( const handleVolumeWheel = useCallback(
(e: WheelEvent<HTMLButtonElement | HTMLDivElement>) => { (e: WheelEvent<HTMLButtonElement | HTMLDivElement>) => {
@@ -109,15 +100,15 @@ export const useRightControls = () => {
mpvPlayer?.volume(volumeToSet); mpvPlayer?.volume(volumeToSet);
remote?.updateVolume(volumeToSet); remote?.updateVolume(volumeToSet);
setVolume(volumeToSet); // setVolume(volumeToSet);
}, },
[setVolume, volume, volumeWheelStep], [volume, volumeWheelStep],
); );
const handleMute = useCallback(() => { const handleMute = useCallback(() => {
setMuted(!muted); // setMuted(!muted);
mpvPlayer?.mute(!muted); mpvPlayer?.mute(!muted);
}, [muted, setMuted]); }, [muted]);
useEffect(() => { useEffect(() => {
if (isElectron()) { if (isElectron()) {
@@ -3,12 +3,11 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { MutationOptions } from '/@/renderer/lib/react-query'; import { MutationOptions } from '/@/renderer/lib/react-query';
import { useIncrementQueuePlayCount } from '/@/renderer/store';
import { usePlayEvent } from '/@/renderer/store/event.store'; import { usePlayEvent } from '/@/renderer/store/event.store';
import { ScrobbleArgs, ScrobbleResponse } from '/@/shared/types/domain-types'; import { ScrobbleArgs, ScrobbleResponse } from '/@/shared/types/domain-types';
export const useSendScrobble = (options?: MutationOptions) => { export const useSendScrobble = (options?: MutationOptions) => {
const incrementPlayCount = useIncrementQueuePlayCount(); // const incrementPlayCount = useIncrementQueuePlayCount();
const sendPlayEvent = usePlayEvent(); const sendPlayEvent = usePlayEvent();
return useMutation<ScrobbleResponse, AxiosError, ScrobbleArgs, null>({ return useMutation<ScrobbleResponse, AxiosError, ScrobbleArgs, null>({
@@ -21,7 +20,7 @@ export const useSendScrobble = (options?: MutationOptions) => {
onSuccess: (_data, variables) => { onSuccess: (_data, variables) => {
// Manually increment the play count for the song in the queue if scrobble was submitted // Manually increment the play count for the song in the queue if scrobble was submitted
if (variables.query.submission) { if (variables.query.submission) {
incrementPlayCount([variables.query.id]); // incrementPlayCount([variables.query.id]);
sendPlayEvent(variables.query.id); sendPlayEvent(variables.query.id);
} }
}, },
@@ -29,8 +29,8 @@ import { playlistsQueries } from '/@/renderer/features/playlists/api/playlists-a
import { useAppFocus } from '/@/renderer/hooks'; import { useAppFocus } from '/@/renderer/hooks';
import { import {
useCurrentServer, useCurrentServer,
useCurrentSong, usePlayerSong,
useCurrentStatus, usePlayerStatus,
usePlaylistDetailStore, usePlaylistDetailStore,
usePlaylistDetailTablePagination, usePlaylistDetailTablePagination,
useSetPlaylistDetailTable, useSetPlaylistDetailTable,
@@ -58,9 +58,9 @@ interface PlaylistDetailContentProps {
export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetailContentProps) => { export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetailContentProps) => {
const { playlistId } = useParams() as { playlistId: string }; const { playlistId } = useParams() as { playlistId: string };
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const status = useCurrentStatus(); const status = usePlayerStatus();
const isFocused = useAppFocus(); const isFocused = useAppFocus();
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const server = useCurrentServer(); const server = useCurrentServer();
const page = usePlaylistDetailStore(); const page = usePlaylistDetailStore();
const filters: PlaylistSongListQueryClientSide = useMemo(() => { const filters: PlaylistSongListQueryClientSide = useMemo(() => {
@@ -7,7 +7,7 @@ import {
} from '/@/renderer/features/settings/components/settings-section'; } from '/@/renderer/features/settings/components/settings-section';
import { useHotkeySettings, usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store'; import { useHotkeySettings, usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store';
import { Switch } from '/@/shared/components/switch/switch'; import { Switch } from '/@/shared/components/switch/switch';
import { PlaybackType } from '/@/shared/types/types'; import { PlayerType } from '/@/shared/types/types';
const localSettings = isElectron() ? window.api.localSettings : null; const localSettings = isElectron() ? window.api.localSettings : null;
const isWindows = isElectron() ? window.api.utils.isWindows() : false; const isWindows = isElectron() ? window.api.utils.isWindows() : false;
@@ -25,9 +25,7 @@ export const WindowHotkeySettings = () => {
defaultChecked={settings.globalMediaHotkeys} defaultChecked={settings.globalMediaHotkeys}
disabled={ disabled={
!isElectron() || !isElectron() ||
(enableWindowsMediaSession && (enableWindowsMediaSession && isWindows && playbackType === PlayerType.WEB)
isWindows &&
playbackType === PlaybackType.WEB)
} }
onChange={(e) => { onChange={(e) => {
setSettings({ setSettings({
@@ -6,14 +6,14 @@ import {
SettingOption, SettingOption,
SettingsSection, SettingsSection,
} from '/@/renderer/features/settings/components/settings-section'; } from '/@/renderer/features/settings/components/settings-section';
import { useCurrentStatus, usePlayerStore } from '/@/renderer/store'; import { usePlayerStatus, usePlayerStore } from '/@/renderer/store';
import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store'; import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import { setQueue } from '/@/renderer/utils/set-transcoded-queue-data'; import { setQueue } from '/@/renderer/utils/set-transcoded-queue-data';
import { Select } from '/@/shared/components/select/select'; import { Select } from '/@/shared/components/select/select';
import { Slider } from '/@/shared/components/slider/slider'; import { Slider } from '/@/shared/components/slider/slider';
import { Switch } from '/@/shared/components/switch/switch'; import { Switch } from '/@/shared/components/switch/switch';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { CrossfadeStyle, PlaybackStyle, PlaybackType, PlayerStatus } from '/@/shared/types/types'; import { CrossfadeStyle, PlayerStyle, PlayerType, PlayerStatus } from '/@/shared/types/types';
const ipc = isElectron() ? window.api.ipc : null; const ipc = isElectron() ? window.api.ipc : null;
@@ -26,7 +26,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
const { t } = useTranslation(); const { t } = useTranslation();
const settings = usePlaybackSettings(); const settings = usePlaybackSettings();
const { setSettings } = useSettingsStoreActions(); const { setSettings } = useSettingsStoreActions();
const status = useCurrentStatus(); const status = usePlayerStatus();
const [audioDevices, setAudioDevices] = useState<{ label: string; value: string }[]>([]); const [audioDevices, setAudioDevices] = useState<{ label: string; value: string }[]>([]);
@@ -43,7 +43,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
); );
}; };
if (settings.type === PlaybackType.WEB) { if (settings.type === PlayerType.WEB) {
getAudioDevices(); getAudioDevices();
} }
}, [settings.type, t]); }, [settings.type, t]);
@@ -56,16 +56,16 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
{ {
disabled: !isElectron(), disabled: !isElectron(),
label: 'MPV', label: 'MPV',
value: PlaybackType.LOCAL, value: PlayerType.LOCAL,
}, },
{ label: 'Web', value: PlaybackType.WEB }, { label: 'Web', value: PlayerType.WEB },
]} ]}
defaultValue={settings.type} defaultValue={settings.type}
disabled={status === PlayerStatus.PLAYING} disabled={status === PlayerStatus.PLAYING}
onChange={(e) => { onChange={(e) => {
setSettings({ playback: { ...settings, type: e as PlaybackType } }); setSettings({ playback: { ...settings, type: e as PlayerType } });
ipc?.send('settings-set', { property: 'playbackType', value: e }); ipc?.send('settings-set', { property: 'playbackType', value: e });
if (isElectron() && e === PlaybackType.LOCAL) { if (isElectron() && e === PlayerType.LOCAL) {
const queueData = usePlayerStore.getState().actions.getPlayerData(); const queueData = usePlayerStore.getState().actions.getPlayerData();
setQueue(queueData); setQueue(queueData);
} }
@@ -89,7 +89,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
clearable clearable
data={audioDevices} data={audioDevices}
defaultValue={settings.audioDeviceId} defaultValue={settings.audioDeviceId}
disabled={settings.type !== PlaybackType.WEB} disabled={settings.type !== PlayerType.WEB}
onChange={(e) => setSettings({ playback: { ...settings, audioDeviceId: e } })} onChange={(e) => setSettings({ playback: { ...settings, audioDeviceId: e } })}
/> />
), ),
@@ -97,7 +97,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: !isElectron() || settings.type !== PlaybackType.WEB, isHidden: !isElectron() || settings.type !== PlayerType.WEB,
title: t('setting.audioDevice', { postProcess: 'sentenceCase' }), title: t('setting.audioDevice', { postProcess: 'sentenceCase' }),
}, },
{ {
@@ -109,20 +109,20 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
context: 'optionNormal', context: 'optionNormal',
postProcess: 'titleCase', postProcess: 'titleCase',
}), }),
value: PlaybackStyle.GAPLESS, value: PlayerStyle.GAPLESS,
}, },
{ {
label: t('setting.playbackStyle', { label: t('setting.playbackStyle', {
context: 'optionCrossFade', context: 'optionCrossFade',
postProcess: 'titleCase', postProcess: 'titleCase',
}), }),
value: PlaybackStyle.CROSSFADE, value: PlayerStyle.CROSSFADE,
}, },
]} ]}
defaultValue={settings.style} defaultValue={settings.style}
disabled={settings.type !== PlaybackType.WEB || status === PlayerStatus.PLAYING} disabled={settings.type !== PlayerType.WEB || status === PlayerStatus.PLAYING}
onChange={(e) => onChange={(e) =>
setSettings({ playback: { ...settings, style: e as PlaybackStyle } }) setSettings({ playback: { ...settings, style: e as PlayerStyle } })
} }
/> />
), ),
@@ -130,7 +130,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: settings.type !== PlaybackType.WEB, isHidden: settings.type !== PlayerType.WEB,
note: status === PlayerStatus.PLAYING ? 'Player must be paused' : undefined, note: status === PlayerStatus.PLAYING ? 'Player must be paused' : undefined,
title: t('setting.playbackStyle', { title: t('setting.playbackStyle', {
context: 'description', context: 'description',
@@ -152,7 +152,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: settings.type !== PlaybackType.WEB, isHidden: settings.type !== PlayerType.WEB,
note: t('common.restartRequired', { postProcess: 'sentenceCase' }), note: t('common.restartRequired', { postProcess: 'sentenceCase' }),
title: t('setting.webAudio', { title: t('setting.webAudio', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
@@ -173,7 +173,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: settings.type !== PlaybackType.WEB, isHidden: settings.type !== PlayerType.WEB,
title: t('setting.preservePitch', { title: t('setting.preservePitch', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
@@ -183,8 +183,8 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
<Slider <Slider
defaultValue={settings.crossfadeDuration} defaultValue={settings.crossfadeDuration}
disabled={ disabled={
settings.type !== PlaybackType.WEB || settings.type !== PlayerType.WEB ||
settings.style !== PlaybackStyle.CROSSFADE || settings.style !== PlayerStyle.CROSSFADE ||
status === PlayerStatus.PLAYING status === PlayerStatus.PLAYING
} }
max={15} max={15}
@@ -199,7 +199,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: settings.type !== PlaybackType.WEB, isHidden: settings.type !== PlayerType.WEB,
note: status === PlayerStatus.PLAYING ? 'Player must be paused' : undefined, note: status === PlayerStatus.PLAYING ? 'Player must be paused' : undefined,
title: t('setting.crossfadeDuration', { title: t('setting.crossfadeDuration', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
@@ -224,8 +224,8 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
]} ]}
defaultValue={settings.crossfadeStyle} defaultValue={settings.crossfadeStyle}
disabled={ disabled={
settings.type !== PlaybackType.WEB || settings.type !== PlayerType.WEB ||
settings.style !== PlaybackStyle.CROSSFADE || settings.style !== PlayerStyle.CROSSFADE ||
status === PlayerStatus.PLAYING status === PlayerStatus.PLAYING
} }
onChange={(e) => { onChange={(e) => {
@@ -241,7 +241,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: settings.type !== PlaybackType.WEB, isHidden: settings.type !== PlayerType.WEB,
note: status === PlayerStatus.PLAYING ? 'Player must be paused' : undefined, note: status === PlayerStatus.PLAYING ? 'Player must be paused' : undefined,
title: t('setting.crossfadeStyle', { postProcess: 'sentenceCase' }), title: t('setting.crossfadeStyle', { postProcess: 'sentenceCase' }),
}, },
@@ -7,7 +7,7 @@ import {
} from '/@/renderer/features/settings/components/settings-section'; } from '/@/renderer/features/settings/components/settings-section';
import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store'; import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import { Switch } from '/@/shared/components/switch/switch'; import { Switch } from '/@/shared/components/switch/switch';
import { PlaybackType } from '/@/shared/types/types'; import { PlayerType } from '/@/shared/types/types';
const isWindows = isElectron() ? window.api.utils.isWindows() : null; const isWindows = isElectron() ? window.api.utils.isWindows() : null;
const isDesktop = isElectron(); const isDesktop = isElectron();
@@ -30,7 +30,7 @@ export const MediaSessionSettings = () => {
<Switch <Switch
aria-label="Toggle media Session" aria-label="Toggle media Session"
defaultChecked={mediaSession} defaultChecked={mediaSession}
disabled={!isWindows || !isDesktop || playbackType !== PlaybackType.WEB} disabled={!isWindows || !isDesktop || playbackType !== PlayerType.WEB}
onChange={handleMediaSessionChange} onChange={handleMediaSessionChange}
/> />
), ),
@@ -6,11 +6,9 @@ import {
SettingOption, SettingOption,
SettingsSection, SettingsSection,
} from '/@/renderer/features/settings/components/settings-section'; } from '/@/renderer/features/settings/components/settings-section';
import { usePlayerControls, usePlayerStore, useQueueControls } from '/@/renderer/store';
import { import {
SettingsState, SettingsState,
usePlaybackSettings, usePlaybackSettings,
useSettingsStore,
useSettingsStoreActions, useSettingsStoreActions,
} from '/@/renderer/store/settings.store'; } from '/@/renderer/store/settings.store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
@@ -22,7 +20,7 @@ import { Switch } from '/@/shared/components/switch/switch';
import { TextInput } from '/@/shared/components/text-input/text-input'; import { TextInput } from '/@/shared/components/text-input/text-input';
import { Text } from '/@/shared/components/text/text'; import { Text } from '/@/shared/components/text/text';
import { Textarea } from '/@/shared/components/textarea/textarea'; import { Textarea } from '/@/shared/components/textarea/textarea';
import { PlaybackType } from '/@/shared/types/types'; import { PlayerType } from '/@/shared/types/types';
const localSettings = isElectron() ? window.api.localSettings : null; const localSettings = isElectron() ? window.api.localSettings : null;
const mpvPlayer = isElectron() ? window.api.mpvPlayer : null; const mpvPlayer = isElectron() ? window.api.mpvPlayer : null;
@@ -74,8 +72,8 @@ export const MpvSettings = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const settings = usePlaybackSettings(); const settings = usePlaybackSettings();
const { setSettings } = useSettingsStoreActions(); const { setSettings } = useSettingsStoreActions();
const { pause } = usePlayerControls(); // const { pause } = usePlayerControls();
const { clearQueue } = useQueueControls(); // const { clearQueue } = useQueueControls();
const [mpvPath, setMpvPath] = useState( const [mpvPath, setMpvPath] = useState(
(localSettings?.get('mpv_path') as string | undefined) || '', (localSettings?.get('mpv_path') as string | undefined) || '',
@@ -129,21 +127,21 @@ export const MpvSettings = () => {
mpvPlayer?.setProperties(mpvSetting); mpvPlayer?.setProperties(mpvSetting);
}; };
const handleReloadMpv = () => { // const handleReloadMpv = () => {
pause(); // pause();
clearQueue(); // clearQueue();
const extraParameters = useSettingsStore.getState().playback.mpvExtraParameters; // const extraParameters = useSettingsStore.getState().playback.mpvExtraParameters;
const properties: Record<string, any> = { // const properties: Record<string, any> = {
speed: usePlayerStore.getState().speed, // speed: usePlayerStore.getState().speed,
...getMpvProperties(useSettingsStore.getState().playback.mpvProperties), // ...getMpvProperties(useSettingsStore.getState().playback.mpvProperties),
}; // };
mpvPlayer?.restart({ // mpvPlayer?.restart({
binaryPath: mpvPath || undefined, // binaryPath: mpvPath || undefined,
extraParameters, // extraParameters,
properties, // properties,
}); // });
}; // };
const handleSetExtraParameters = (data: string[]) => { const handleSetExtraParameters = (data: string[]) => {
setSettings({ setSettings({
@@ -160,7 +158,7 @@ export const MpvSettings = () => {
<Group gap="sm"> <Group gap="sm">
<ActionIcon <ActionIcon
icon="refresh" icon="refresh"
onClick={handleReloadMpv} // onClick={handleReloadMpv}
tooltip={{ tooltip={{
label: t('common.reload', { postProcess: 'titleCase' }), label: t('common.reload', { postProcess: 'titleCase' }),
openDelay: 0, openDelay: 0,
@@ -194,7 +192,7 @@ export const MpvSettings = () => {
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: settings.type !== PlaybackType.LOCAL, isHidden: settings.type !== PlayerType.LOCAL,
note: 'Restart required', note: 'Restart required',
title: t('setting.mpvExecutablePath', { postProcess: 'sentenceCase' }), title: t('setting.mpvExecutablePath', { postProcess: 'sentenceCase' }),
}, },
@@ -235,7 +233,7 @@ export const MpvSettings = () => {
</Text> </Text>
</Stack> </Stack>
), ),
isHidden: settings.type !== PlaybackType.LOCAL, isHidden: settings.type !== PlayerType.LOCAL,
note: t('common.restartRequired', { note: t('common.restartRequired', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
@@ -268,7 +266,7 @@ export const MpvSettings = () => {
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: settings.type !== PlaybackType.LOCAL, isHidden: settings.type !== PlayerType.LOCAL,
title: t('setting.gaplessAudio', { postProcess: 'sentenceCase' }), title: t('setting.gaplessAudio', { postProcess: 'sentenceCase' }),
}, },
{ {
@@ -311,7 +309,7 @@ export const MpvSettings = () => {
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: settings.type !== PlaybackType.LOCAL, isHidden: settings.type !== PlayerType.LOCAL,
title: t('setting.audioExclusiveMode', { postProcess: 'sentenceCase' }), title: t('setting.audioExclusiveMode', { postProcess: 'sentenceCase' }),
}, },
]; ];
@@ -8,7 +8,7 @@ import { ScrobbleSettings } from '/@/renderer/features/settings/components/playb
import { TranscodeSettings } from '/@/renderer/features/settings/components/playback/transcode-settings'; import { TranscodeSettings } from '/@/renderer/features/settings/components/playback/transcode-settings';
import { useSettingsStore } from '/@/renderer/store'; import { useSettingsStore } from '/@/renderer/store';
import { Stack } from '/@/shared/components/stack/stack'; import { Stack } from '/@/shared/components/stack/stack';
import { PlaybackType } from '/@/shared/types/types'; import { PlayerType } from '/@/shared/types/types';
const MpvSettings = lazy(() => const MpvSettings = lazy(() =>
import('/@/renderer/features/settings/components/playback/mpv-settings').then((module) => { import('/@/renderer/features/settings/components/playback/mpv-settings').then((module) => {
@@ -22,7 +22,7 @@ export const PlaybackTab = () => {
const hasFancyAudio = useMemo(() => { const hasFancyAudio = useMemo(() => {
return ( return (
(isElectron() && audioType === PlaybackType.LOCAL) || (isElectron() && audioType === PlayerType.LOCAL) ||
(useWebAudio && 'AudioContext' in window) (useWebAudio && 'AudioContext' in window)
); );
}, [audioType, useWebAudio]); }, [audioType, useWebAudio]);
@@ -5,7 +5,7 @@ import isElectron from 'is-electron';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys'; import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { useSetAlbumListItemDataById, useSetQueueFavorite } from '/@/renderer/store'; import { useSetAlbumListItemDataById } from '/@/renderer/store';
import { useFavoriteEvent } from '/@/renderer/store/event.store'; import { useFavoriteEvent } from '/@/renderer/store/event.store';
import { import {
AlbumArtistDetailResponse, AlbumArtistDetailResponse,
@@ -21,7 +21,7 @@ export const useCreateFavorite = (args: MutationHookArgs) => {
const { options } = args || {}; const { options } = args || {};
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const setAlbumListData = useSetAlbumListItemDataById(); const setAlbumListData = useSetAlbumListItemDataById();
const setQueueFavorite = useSetQueueFavorite(); // const setQueueFavorite = useSetQueueFavorite();
const setFavoriteEvent = useFavoriteEvent(); const setFavoriteEvent = useFavoriteEvent();
return useMutation<FavoriteResponse, AxiosError, FavoriteArgs, null>({ return useMutation<FavoriteResponse, AxiosError, FavoriteArgs, null>({
@@ -46,7 +46,7 @@ export const useCreateFavorite = (args: MutationHookArgs) => {
if (variables.query.type === LibraryItem.SONG) { if (variables.query.type === LibraryItem.SONG) {
remote?.updateFavorite(true, serverId, variables.query.id); remote?.updateFavorite(true, serverId, variables.query.id);
setQueueFavorite(variables.query.id, true); // setQueueFavorite(variables.query.id, true);
setFavoriteEvent(variables.query.id, true); setFavoriteEvent(variables.query.id, true);
} }
@@ -5,7 +5,7 @@ import isElectron from 'is-electron';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys'; import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { useSetAlbumListItemDataById, useSetQueueFavorite } from '/@/renderer/store'; import { useSetAlbumListItemDataById } from '/@/renderer/store';
import { useFavoriteEvent } from '/@/renderer/store/event.store'; import { useFavoriteEvent } from '/@/renderer/store/event.store';
import { import {
AlbumArtistDetailResponse, AlbumArtistDetailResponse,
@@ -21,7 +21,7 @@ export const useDeleteFavorite = (args: MutationHookArgs) => {
const { options } = args || {}; const { options } = args || {};
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const setAlbumListData = useSetAlbumListItemDataById(); const setAlbumListData = useSetAlbumListItemDataById();
const setQueueFavorite = useSetQueueFavorite(); // const setQueueFavorite = useSetQueueFavorite();
const setFavoriteEvent = useFavoriteEvent(); const setFavoriteEvent = useFavoriteEvent();
return useMutation<FavoriteResponse, AxiosError, FavoriteArgs, null>({ return useMutation<FavoriteResponse, AxiosError, FavoriteArgs, null>({
@@ -44,7 +44,7 @@ export const useDeleteFavorite = (args: MutationHookArgs) => {
if (variables.query.type === LibraryItem.SONG) { if (variables.query.type === LibraryItem.SONG) {
remote?.updateFavorite(false, serverId, variables.query.id); remote?.updateFavorite(false, serverId, variables.query.id);
setQueueFavorite(variables.query.id, false); // setQueueFavorite(variables.query.id, false);
setFavoriteEvent(variables.query.id, false); setFavoriteEvent(variables.query.id, false);
} }
@@ -5,7 +5,7 @@ import isElectron from 'is-electron';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys'; import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { useSetAlbumListItemDataById, useSetQueueRating } from '/@/renderer/store'; import { useSetAlbumListItemDataById } from '/@/renderer/store';
import { useRatingEvent } from '/@/renderer/store/event.store'; import { useRatingEvent } from '/@/renderer/store/event.store';
import { import {
Album, Album,
@@ -24,7 +24,7 @@ export const useSetRating = (args: MutationHookArgs) => {
const { options } = args || {}; const { options } = args || {};
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const setAlbumListData = useSetAlbumListItemDataById(); const setAlbumListData = useSetAlbumListItemDataById();
const setQueueRating = useSetQueueRating(); // const setQueueRating = useSetQueueRating();
const setRatingEvent = useRatingEvent(); const setRatingEvent = useRatingEvent();
return useMutation< return useMutation<
@@ -46,7 +46,7 @@ export const useSetRating = (args: MutationHookArgs) => {
setAlbumListData(item.id, { userRating: item.userRating }); setAlbumListData(item.id, { userRating: item.userRating });
break; break;
case LibraryItem.SONG: case LibraryItem.SONG:
setQueueRating([item.id], item.userRating); // setQueueRating([item.id], item.userRating);
setRatingEvent([item.id], item.userRating); setRatingEvent([item.id], item.userRating);
break; break;
} }
@@ -67,7 +67,7 @@ export const useSetRating = (args: MutationHookArgs) => {
} }
if (songIds.length > 0) { if (songIds.length > 0) {
setQueueRating(songIds, variables.query.rating); // setQueueRating(songIds, variables.query.rating);
setRatingEvent(songIds, variables.query.rating); setRatingEvent(songIds, variables.query.rating);
} }
@@ -17,7 +17,7 @@ import {
} from '/@/renderer/features/sidebar/components/sidebar-playlist-list'; } from '/@/renderer/features/sidebar/components/sidebar-playlist-list';
import { import {
useAppStoreActions, useAppStoreActions,
useCurrentSong, usePlayerSong,
useFullScreenPlayerStore, useFullScreenPlayerStore,
useSetFullScreenPlayerStore, useSetFullScreenPlayerStore,
useSidebarStore, useSidebarStore,
@@ -42,7 +42,7 @@ export const Sidebar = () => {
const sidebar = useSidebarStore(); const sidebar = useSidebarStore();
const { setSideBar } = useAppStoreActions(); const { setSideBar } = useAppStoreActions();
const { sidebarPlaylistList } = useGeneralSettings(); const { sidebarPlaylistList } = useGeneralSettings();
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const translatedSidebarItemMap = useMemo( const translatedSidebarItemMap = useMemo(
() => ({ () => ({
@@ -9,7 +9,6 @@ import { getColumnDefs, VirtualTable } from '/@/renderer/components/virtual-tabl
import { ErrorFallback } from '/@/renderer/features/action-required/components/error-fallback'; import { ErrorFallback } from '/@/renderer/features/action-required/components/error-fallback';
import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items'; import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
import { useHandleTableContextMenu } from '/@/renderer/features/context-menu/hooks/use-handle-context-menu'; import { useHandleTableContextMenu } from '/@/renderer/features/context-menu/hooks/use-handle-context-menu';
import { useHandlePlayQueueAdd } from '/@/renderer/features/player/hooks/use-handle-playqueue-add';
import { songsQueries } from '/@/renderer/features/songs/api/songs-api'; import { songsQueries } from '/@/renderer/features/songs/api/songs-api';
import { usePlayButtonBehavior, useTableSettings } from '/@/renderer/store'; import { usePlayButtonBehavior, useTableSettings } from '/@/renderer/store';
import { Spinner } from '/@/shared/components/spinner/spinner'; import { Spinner } from '/@/shared/components/spinner/spinner';
@@ -24,8 +23,6 @@ export type SimilarSongsListProps = {
export const SimilarSongsList = ({ count, fullScreen, song }: SimilarSongsListProps) => { export const SimilarSongsList = ({ count, fullScreen, song }: SimilarSongsListProps) => {
const tableRef = useRef<AgGridReact<Song> | null>(null); const tableRef = useRef<AgGridReact<Song> | null>(null);
const tableConfig = useTableSettings(fullScreen ? 'fullScreen' : 'songs'); const tableConfig = useTableSettings(fullScreen ? 'fullScreen' : 'songs');
const handlePlayQueueAdd = useHandlePlayQueueAdd();
const playButtonBehavior = usePlayButtonBehavior();
const songQuery = useQuery( const songQuery = useQuery(
songsQueries.similar({ songsQueries.similar({
@@ -51,11 +48,11 @@ export const SimilarSongsList = ({ count, fullScreen, song }: SimilarSongsListPr
const handleRowDoubleClick = (e: RowDoubleClickedEvent<Song>) => { const handleRowDoubleClick = (e: RowDoubleClickedEvent<Song>) => {
if (!e.data || !songQuery.data) return; if (!e.data || !songQuery.data) return;
handlePlayQueueAdd?.({ // handlePlayQueueAdd?.({
byData: songQuery.data, // byData: songQuery.data,
initialSongId: e.data.id, // initialSongId: e.data.id,
playType: playButtonBehavior, // playType: playButtonBehavior,
}); // });
}; };
return songQuery.isLoading ? ( return songQuery.isLoading ? (
@@ -12,8 +12,8 @@ import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/conte
import { useAppFocus } from '/@/renderer/hooks'; import { useAppFocus } from '/@/renderer/hooks';
import { import {
useCurrentServer, useCurrentServer,
useCurrentSong, usePlayerSong,
useCurrentStatus, usePlayerStatus,
usePlayButtonBehavior, usePlayButtonBehavior,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { LibraryItem, QueueSong, SongListQuery } from '/@/shared/types/domain-types'; import { LibraryItem, QueueSong, SongListQuery } from '/@/shared/types/domain-types';
@@ -27,8 +27,8 @@ export const SongListTableView = ({ itemCount, tableRef }: SongListTableViewProp
const server = useCurrentServer(); const server = useCurrentServer();
const { customFilters, handlePlay, id, pageKey } = useListContext(); const { customFilters, handlePlay, id, pageKey } = useListContext();
const isFocused = useAppFocus(); const isFocused = useAppFocus();
const currentSong = useCurrentSong(); const currentSong = usePlayerSong();
const status = useCurrentStatus(); const status = usePlayerStatus();
const { rowClassRules } = useCurrentSongRowStyles({ tableRef }); const { rowClassRules } = useCurrentSongRowStyles({ tableRef });
+20 -27
View File
@@ -1,22 +1,16 @@
import { HotkeyItem, useHotkeys } from '@mantine/hooks'; import { HotkeyItem, useHotkeys } from '@mantine/hooks';
import clsx from 'clsx'; import clsx from 'clsx';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import { lazy, useMemo } from 'react'; import { lazy } from 'react';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import styles from './default-layout.module.css'; import styles from './default-layout.module.css';
import { ContextMenuProvider } from '/@/renderer/features/context-menu/context-menu-provider';
import { CommandPalette } from '/@/renderer/features/search/components/command-palette'; import { CommandPalette } from '/@/renderer/features/search/components/command-palette';
import { MainContent } from '/@/renderer/layouts/default-layout/main-content'; import { MainContent } from '/@/renderer/layouts/default-layout/main-content';
import { PlayerBar } from '/@/renderer/layouts/default-layout/player-bar'; import { PlayerBar } from '/@/renderer/layouts/default-layout/player-bar';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { import { useAppStore, useCommandPalette } from '/@/renderer/store';
useAppStore,
useCommandPalette,
useCurrentStatus,
useQueueStatus,
} from '/@/renderer/store';
import { import {
useGeneralSettings, useGeneralSettings,
useHotkeySettings, useHotkeySettings,
@@ -24,13 +18,13 @@ import {
useSettingsStoreActions, useSettingsStoreActions,
useWindowSettings, useWindowSettings,
} from '/@/renderer/store/settings.store'; } from '/@/renderer/store/settings.store';
import { Platform, PlaybackType, PlayerStatus } from '/@/shared/types/types'; import { Platform, PlayerType } from '/@/shared/types/types';
if (!isElectron()) { if (!isElectron()) {
useSettingsStore.getState().actions.setSettings({ useSettingsStore.getState().actions.setSettings({
playback: { playback: {
...useSettingsStore.getState().playback, ...useSettingsStore.getState().playback,
type: PlaybackType.WEB, type: PlayerType.WEB,
}, },
}); });
} }
@@ -53,8 +47,7 @@ export const DefaultLayout = ({ shell }: DefaultLayoutProps) => {
const localSettings = isElectron() ? window.api.localSettings : null; const localSettings = isElectron() ? window.api.localSettings : null;
const settings = useGeneralSettings(); const settings = useGeneralSettings();
const { setSettings } = useSettingsStoreActions(); const { setSettings } = useSettingsStoreActions();
const playerStatus = useCurrentStatus();
const { currentSong, index, length } = useQueueStatus();
const { privateMode } = useAppStore(); const { privateMode } = useAppStore();
const updateZoom = (increase: number) => { const updateZoom = (increase: number) => {
@@ -83,21 +76,21 @@ export const DefaultLayout = ({ shell }: DefaultLayoutProps) => {
...(isElectron() ? zoomHotkeys : []), ...(isElectron() ? zoomHotkeys : []),
]); ]);
const title = useMemo(() => { // const title = useMemo(() => {
const statusString = playerStatus === PlayerStatus.PAUSED ? '(Paused) ' : ''; // const statusString = playerStatus === PlayerStatus.PAUSED ? '(Paused) ' : '';
const queueString = length ? `(${index + 1} / ${length}) ` : ''; // const queueString = length ? `(${index + 1} / ${length}) ` : '';
const privateModeString = privateMode ? '(Private mode)' : ''; // const privateModeString = privateMode ? '(Private mode)' : '';
const title = `${ // const title = `${
length // length
? `${statusString}${queueString}${currentSong?.name}${currentSong?.artistName ? `${currentSong?.artistName} — Feishin` : ''}` // ? `${statusString}${queueString}${currentSong?.name}${currentSong?.artistName ? ` — ${currentSong?.artistName} — Feishin` : ''}`
: 'Feishin' // : 'Feishin'
}${privateMode ? ` ${privateModeString}` : ''}`; // }${privateMode ? ` ${privateModeString}` : ''}`;
document.title = title; // document.title = title;
return title; // return title;
}, [currentSong?.artistName, currentSong?.name, index, length, playerStatus, privateMode]); // }, [currentSong?.artistName, currentSong?.name, index, length, playerStatus, privateMode]);
return ( return (
<ContextMenuProvider> <>
<div <div
className={clsx(styles.layout, { className={clsx(styles.layout, {
[styles.macos]: windowBarStyle === Platform.MACOS, [styles.macos]: windowBarStyle === Platform.MACOS,
@@ -105,11 +98,11 @@ export const DefaultLayout = ({ shell }: DefaultLayoutProps) => {
})} })}
id="default-layout" id="default-layout"
> >
{windowBarStyle !== Platform.WEB && <WindowBar title={title} />} {windowBarStyle !== Platform.WEB && <WindowBar title="" />}
<MainContent shell={shell} /> <MainContent shell={shell} />
<PlayerBar /> <PlayerBar />
</div> </div>
<CommandPalette modalProps={{ handlers, opened }} /> <CommandPalette modalProps={{ handlers, opened }} />
</ContextMenuProvider> </>
); );
}; };
+14 -14
View File
@@ -4,7 +4,7 @@ import { Navigate, Outlet } from 'react-router-dom';
import { useServerAuthenticated } from '/@/renderer/hooks/use-server-authenticated'; import { useServerAuthenticated } from '/@/renderer/hooks/use-server-authenticated';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer, useSetPlayerFallback } from '/@/renderer/store'; import { useCurrentServer } from '/@/renderer/store';
import { Center } from '/@/shared/components/center/center'; import { Center } from '/@/shared/components/center/center';
import { Spinner } from '/@/shared/components/spinner/spinner'; import { Spinner } from '/@/shared/components/spinner/spinner';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
@@ -16,7 +16,7 @@ const mpvPlayerListener = isElectron() ? window.api.mpvPlayerListener : null;
export const AppOutlet = () => { export const AppOutlet = () => {
const currentServer = useCurrentServer(); const currentServer = useCurrentServer();
const setFallback = useSetPlayerFallback(); // const setFallback = useSetPlayerFallback();
const authState = useServerAuthenticated(); const authState = useServerAuthenticated();
const isActionsRequired = useMemo(() => { const isActionsRequired = useMemo(() => {
@@ -28,20 +28,20 @@ export const AppOutlet = () => {
return isActionRequired; return isActionRequired;
}, [currentServer]); }, [currentServer]);
useEffect(() => { // useEffect(() => {
utils?.mainMessageListener((_event, data) => { // utils?.mainMessageListener((_event, data) => {
toast.show(data); // toast.show(data);
}); // });
mpvPlayerListener?.rendererPlayerFallback((_event, data) => { // mpvPlayerListener?.rendererPlayerFallback((_event, data) => {
setFallback(data); // setFallback(data);
}); // });
return () => { // return () => {
ipc?.removeAllListeners('toast-from-main'); // ipc?.removeAllListeners('toast-from-main');
ipc?.removeAllListeners('renderer-player-fallback'); // ipc?.removeAllListeners('renderer-player-fallback');
}; // };
}, [setFallback]); // }, [setFallback]);
if (authState === AuthState.LOADING) { if (authState === AuthState.LOADING) {
return ( return (
+85 -86
View File
@@ -10,40 +10,14 @@ import { useShallow } from 'zustand/react/shallow';
import { createSelectors } from '/@/renderer/lib/zustand'; import { createSelectors } from '/@/renderer/lib/zustand';
import { shuffleInPlace } from '/@/renderer/utils/shuffle'; import { shuffleInPlace } from '/@/renderer/utils/shuffle';
import { QueueSong, Song } from '/@/shared/types/domain-types'; import { QueueSong, Song } from '/@/shared/types/domain-types';
import {
export enum PlayerRepeat { Play,
ALBUM = 'album', PlayerQueueType,
ALL = 'all', PlayerRepeat,
OFF = 'off', PlayerShuffle,
ONE = 'one', PlayerStatus,
} PlayerStyle,
} from '/@/shared/types/types';
export enum PlayerShuffle {
OFF = 'off',
TRACK = 'track',
}
export enum PlayerStatus {
PAUSED = 'paused',
PLAYING = 'playing',
}
export enum PlayerTransition {
CROSSFADE = 'crossfade',
GAPLESS = 'gapless',
}
export enum PlayType {
INDEX = 'index',
LAST = 'last',
NEXT = 'next',
NOW = 'now',
}
export enum QueueType {
DEFAULT = 'default',
PRIORITY = 'priority',
}
export interface PlayerData { export interface PlayerData {
currentTrack: QueueSong | undefined; currentTrack: QueueSong | undefined;
@@ -56,7 +30,7 @@ export interface PlayerData {
shuffle: PlayerShuffle; shuffle: PlayerShuffle;
speed: number; speed: number;
status: PlayerStatus; status: PlayerStatus;
transitionType: PlayerTransition; transitionType: PlayerStyle;
volume: number; volume: number;
}; };
player1: QueueSong | undefined; player1: QueueSong | undefined;
@@ -75,7 +49,7 @@ export interface QueueData {
export type QueueGroupingProperty = keyof QueueSong; export type QueueGroupingProperty = keyof QueueSong;
interface Actions { interface Actions {
addToQueueByType: (items: Song[], playType: PlayType) => void; addToQueueByType: (items: Song[], playType: Play) => void;
addToQueueByUniqueId: (items: Song[], uniqueId: string, edge: 'bottom' | 'top') => void; addToQueueByUniqueId: (items: Song[], uniqueId: string, edge: 'bottom' | 'top') => void;
clearQueue: () => void; clearQueue: () => void;
clearSelected: (items: QueueSong[]) => void; clearSelected: (items: QueueSong[]) => void;
@@ -103,11 +77,11 @@ interface Actions {
moveSelectedToTop: (items: QueueSong[]) => void; moveSelectedToTop: (items: QueueSong[]) => void;
setCrossfadeDuration: (duration: number) => void; setCrossfadeDuration: (duration: number) => void;
setProgress: (timestamp: number) => void; setProgress: (timestamp: number) => void;
setQueueType: (queueType: QueueType) => void; setQueueType: (queueType: PlayerQueueType) => void;
setRepeat: (repeat: PlayerRepeat) => void; setRepeat: (repeat: PlayerRepeat) => void;
setShuffle: (shuffle: PlayerShuffle) => void; setShuffle: (shuffle: PlayerShuffle) => void;
setSpeed: (speed: number) => void; setSpeed: (speed: number) => void;
setTransitionType: (transitionType: PlayerTransition) => void; setTransitionType: (transitionType: PlayerStyle) => void;
setVolume: (volume: number) => void; setVolume: (volume: number) => void;
shuffle: () => void; shuffle: () => void;
shuffleAll: () => void; shuffleAll: () => void;
@@ -125,7 +99,7 @@ interface State {
index: number; index: number;
muted: boolean; muted: boolean;
playerNum: 1 | 2; playerNum: 1 | 2;
queueType: QueueType; queueType: PlayerQueueType;
repeat: PlayerRepeat; repeat: PlayerRepeat;
seekToTimestamp: string; seekToTimestamp: string;
shuffle: PlayerShuffle; shuffle: PlayerShuffle;
@@ -134,7 +108,7 @@ interface State {
stepBackward: number; stepBackward: number;
stepForward: number; stepForward: number;
timestamp: number; timestamp: number;
transitionType: PlayerTransition; transitionType: PlayerStyle;
volume: number; volume: number;
}; };
queue: QueueData; queue: QueueData;
@@ -150,9 +124,9 @@ export const usePlayerStoreBase = create<PlayerState>()(
const queueType = getQueueType(); const queueType = getQueueType();
switch (queueType) { switch (queueType) {
case QueueType.DEFAULT: { case PlayerQueueType.DEFAULT: {
switch (playType) { switch (playType) {
case PlayType.LAST: { case Play.LAST: {
set((state) => { set((state) => {
const currentIndex = state.player.index; const currentIndex = state.player.index;
@@ -171,7 +145,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
}); });
break; break;
} }
case PlayType.NEXT: { case Play.NEXT: {
set((state) => { set((state) => {
const currentIndex = state.player.index; const currentIndex = state.player.index;
@@ -194,7 +168,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
}); });
break; break;
} }
case PlayType.NOW: { case Play.NOW: {
set((state) => { set((state) => {
state.queue.default = []; state.queue.default = [];
state.player.index = 0; state.player.index = 0;
@@ -214,9 +188,9 @@ export const usePlayerStoreBase = create<PlayerState>()(
} }
break; break;
} }
case QueueType.PRIORITY: { case PlayerQueueType.PRIORITY: {
switch (playType) { switch (playType) {
case PlayType.LAST: { case Play.LAST: {
set((state) => { set((state) => {
state.queue.priority = [ state.queue.priority = [
...state.queue.priority, ...state.queue.priority,
@@ -230,7 +204,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
}); });
break; break;
} }
case PlayType.NEXT: { case Play.NEXT: {
set((state) => { set((state) => {
const currentIndex = state.player.index; const currentIndex = state.player.index;
const isInPriority = const isInPriority =
@@ -260,7 +234,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
}); });
break; break;
} }
case PlayType.NOW: { case Play.NOW: {
set((state) => { set((state) => {
state.queue.default = []; state.queue.default = [];
state.player.status = PlayerStatus.PLAYING; state.player.status = PlayerStatus.PLAYING;
@@ -337,7 +311,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
const queueType = getQueueType(); const queueType = getQueueType();
set((state) => { set((state) => {
if (queueType === QueueType.DEFAULT) { if (queueType === PlayerQueueType.DEFAULT) {
const index = state.queue.default.findIndex( const index = state.queue.default.findIndex(
(item) => item.uniqueId === uniqueId, (item) => item.uniqueId === uniqueId,
); );
@@ -445,7 +419,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
const queue = get().getQueueOrder(); const queue = get().getQueueOrder();
const queueType = getQueueType(); const queueType = getQueueType();
if (!groupBy || queueType === QueueType.PRIORITY) { if (!groupBy || queueType === PlayerQueueType.PRIORITY) {
return queue; return queue;
} }
@@ -483,7 +457,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
getQueueOrder: () => { getQueueOrder: () => {
const queueType = getQueueType(); const queueType = getQueueType();
if (queueType === QueueType.PRIORITY) { if (queueType === PlayerQueueType.PRIORITY) {
const defaultQueue = get().queue.default; const defaultQueue = get().queue.default;
const priorityQueue = get().queue.priority; const priorityQueue = get().queue.priority;
@@ -645,7 +619,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
set((state) => { set((state) => {
const uniqueIdMap = new Map(items.map((item) => [item.uniqueId, item])); const uniqueIdMap = new Map(items.map((item) => [item.uniqueId, item]));
if (queueType == QueueType.DEFAULT) { if (queueType == PlayerQueueType.DEFAULT) {
// Find the index of the drop target // Find the index of the drop target
const index = state.queue.default.findIndex( const index = state.queue.default.findIndex(
(item) => item.uniqueId === uniqueId, (item) => item.uniqueId === uniqueId,
@@ -738,7 +712,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
set((state) => { set((state) => {
const uniqueIds = items.map((item) => item.uniqueId); const uniqueIds = items.map((item) => item.uniqueId);
if (state.player.queueType === QueueType.PRIORITY) { if (state.player.queueType === PlayerQueueType.PRIORITY) {
const priorityFiltered = state.queue.priority.filter( const priorityFiltered = state.queue.priority.filter(
(item) => !uniqueIds.includes(item.uniqueId), (item) => !uniqueIds.includes(item.uniqueId),
); );
@@ -779,7 +753,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
const uniqueIds = items.map((item) => item.uniqueId); const uniqueIds = items.map((item) => item.uniqueId);
if (queueType === QueueType.DEFAULT) { if (queueType === PlayerQueueType.DEFAULT) {
const currentIndex = state.player.index; const currentIndex = state.player.index;
const filtered = state.queue.default.filter( const filtered = state.queue.default.filter(
(item) => !uniqueIds.includes(item.uniqueId), (item) => !uniqueIds.includes(item.uniqueId),
@@ -861,7 +835,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
set((state) => { set((state) => {
const uniqueIds = items.map((item) => item.uniqueId); const uniqueIds = items.map((item) => item.uniqueId);
if (state.player.queueType === QueueType.PRIORITY) { if (state.player.queueType === PlayerQueueType.PRIORITY) {
const priorityFiltered = state.queue.priority.filter( const priorityFiltered = state.queue.priority.filter(
(item) => !uniqueIds.includes(item.uniqueId), (item) => !uniqueIds.includes(item.uniqueId),
); );
@@ -896,16 +870,16 @@ export const usePlayerStoreBase = create<PlayerState>()(
index: -1, index: -1,
muted: false, muted: false,
playerNum: 1, playerNum: 1,
queueType: QueueType.DEFAULT, queueType: PlayerQueueType.DEFAULT,
repeat: PlayerRepeat.OFF, repeat: PlayerRepeat.NONE,
seekToTimestamp: uniqueSeekToTimestamp(0), seekToTimestamp: uniqueSeekToTimestamp(0),
shuffle: PlayerShuffle.OFF, shuffle: PlayerShuffle.NONE,
speed: 1, speed: 1,
status: PlayerStatus.PAUSED, status: PlayerStatus.PAUSED,
stepBackward: 10, stepBackward: 10,
stepForward: 10, stepForward: 10,
timestamp: 0, timestamp: 0,
transitionType: PlayerTransition.GAPLESS, transitionType: PlayerStyle.GAPLESS,
volume: 30, volume: 30,
}, },
queue: { queue: {
@@ -924,10 +898,10 @@ export const usePlayerStoreBase = create<PlayerState>()(
state.player.timestamp = timestamp; state.player.timestamp = timestamp;
}); });
}, },
setQueueType: (queueType: QueueType) => { setQueueType: (queueType: PlayerQueueType) => {
set((state) => { set((state) => {
// From default -> priority, move all items from default to priority // From default -> priority, move all items from default to priority
if (queueType === QueueType.PRIORITY) { if (queueType === PlayerQueueType.PRIORITY) {
state.queue.priority = [ state.queue.priority = [
...state.queue.default, ...state.queue.default,
...state.queue.priority, ...state.queue.priority,
@@ -960,7 +934,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
state.player.speed = normalizedSpeed; state.player.speed = normalizedSpeed;
}); });
}, },
setTransitionType: (transitionType: PlayerTransition) => { setTransitionType: (transitionType: PlayerStyle) => {
set((state) => { set((state) => {
state.player.transitionType = transitionType; state.player.transitionType = transitionType;
}); });
@@ -1053,7 +1027,7 @@ export const usePlayerActions = () => {
); );
}; };
export type AddToQueueByPlayType = PlayType; export type AddToQueueByPlayType = Play;
export type AddToQueueByUniqueId = { export type AddToQueueByUniqueId = {
edge: 'bottom' | 'left' | 'right' | 'top' | null; edge: 'bottom' | 'left' | 'right' | 'top' | null;
@@ -1254,16 +1228,6 @@ export const usePlayerProperties = () => {
); );
}; };
export const useCurrentTrack = () => {
return usePlayerStoreBase(
useShallow((state) => {
const queue = state.getQueue();
const index = state.player.index;
return { index, length: queue.items.length, track: queue.items[index] };
}),
);
};
export const usePlayerProgress = () => { export const usePlayerProgress = () => {
return usePlayerStoreBase((state) => state.player.timestamp); return usePlayerStoreBase((state) => state.player.timestamp);
}; };
@@ -1321,32 +1285,67 @@ export const updateQueueFavorites = (ids: string[], favorite: boolean) => {
}); });
}; };
export function usePlayerMuted() { export const usePlayerMuted = () => {
return usePlayerStoreBase((state) => state.player.muted); return usePlayerStoreBase((state) => state.player.muted);
} };
export function usePlayerQueueType() { export const usePlayerQueueType = () => {
return usePlayerStoreBase((state) => state.player.queueType); return usePlayerStoreBase((state) => state.player.queueType);
} };
export function usePlayerRepeat() { export const usePlayerRepeat = () => {
return usePlayerStoreBase((state) => state.player.repeat); return usePlayerStoreBase((state) => state.player.repeat);
} };
export function usePlayerShuffle() { export const usePlayerShuffle = () => {
return usePlayerStoreBase((state) => state.player.shuffle); return usePlayerStoreBase((state) => state.player.shuffle);
} };
export function usePlayerStatus() { export const usePlayerStatus = () => {
return usePlayerStoreBase((state) => state.player.status); return usePlayerStoreBase((state) => state.player.status);
} };
export function usePlayerVolume() { export const usePlayerVolume = () => {
return usePlayerStoreBase((state) => state.player.volume); return usePlayerStoreBase((state) => state.player.volume);
} };
export const usePlayerSpeed = () => {
return usePlayerStoreBase((state) => state.player.speed);
};
export const usePlayerTimestamp = () => {
return usePlayerStoreBase((state) => state.player.timestamp);
};
export const usePlayerSong = () => {
return usePlayerStoreBase((state) => {
const queue = state.getQueue();
const index = state.player.index;
return queue.items[index];
});
};
export const usePlayerNum = () => {
return usePlayerStoreBase((state) => state.player.playerNum);
};
export const usePlayerQueue = () => {
return usePlayerStoreBase((state) => {
const queueType = state.player.queueType;
switch (queueType) {
case PlayerQueueType.DEFAULT:
return state.queue.default;
case PlayerQueueType.PRIORITY:
return state.queue.priority;
default:
return state.queue.default;
}
});
};
function getQueueType() { function getQueueType() {
const queueType: QueueType = usePlayerStore.getState().player.queueType; const queueType: PlayerQueueType = usePlayerStore.getState().player.queueType;
return queueType; return queueType;
} }
+8 -8
View File
@@ -21,8 +21,8 @@ import {
FontType, FontType,
Platform, Platform,
Play, Play,
PlaybackStyle, PlayerStyle,
PlaybackType, PlayerType,
TableColumn, TableColumn,
TableType, TableType,
} from '/@/shared/types/types'; } from '/@/shared/types/types';
@@ -258,9 +258,9 @@ const PlaybackSettingsSchema = z.object({
muted: z.boolean(), muted: z.boolean(),
preservePitch: z.boolean(), preservePitch: z.boolean(),
scrobble: ScrobbleSettingsSchema, scrobble: ScrobbleSettingsSchema,
style: z.nativeEnum(PlaybackStyle), style: z.nativeEnum(PlayerStyle),
transcode: TranscodingConfigSchema, transcode: TranscodingConfigSchema,
type: z.nativeEnum(PlaybackType), type: z.nativeEnum(PlayerType),
webAudio: z.boolean(), webAudio: z.boolean(),
}); });
@@ -652,11 +652,11 @@ const initialState: SettingsState = {
scrobbleAtDuration: 240, scrobbleAtDuration: 240,
scrobbleAtPercentage: 75, scrobbleAtPercentage: 75,
}, },
style: PlaybackStyle.GAPLESS, style: PlayerStyle.GAPLESS,
transcode: { transcode: {
enabled: false, enabled: false,
}, },
type: PlaybackType.WEB, type: PlayerType.WEB,
webAudio: true, webAudio: true,
}, },
remote: { remote: {
@@ -843,7 +843,7 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
...initialState, ...initialState,
playback: { playback: {
...initialState.playback, ...initialState.playback,
type: PlaybackType.WEB, type: PlayerType.WEB,
}, },
}); });
} else { } else {
@@ -970,7 +970,7 @@ export const usePlaybackType = () =>
const isFallback = usePlayerStore.getState().fallback; const isFallback = usePlayerStore.getState().fallback;
if (isFallback) { if (isFallback) {
return PlaybackType.WEB; return PlayerType.WEB;
} }
return state.playback.type; return state.playback.type;
+14 -8
View File
@@ -101,20 +101,16 @@ export enum FontType {
} }
export enum Play { export enum Play {
INDEX = 'index',
LAST = 'last', LAST = 'last',
NEXT = 'next', NEXT = 'next',
NOW = 'now', NOW = 'now',
SHUFFLE = 'shuffle', SHUFFLE = 'shuffle',
} }
export enum PlaybackStyle { export enum PlayerQueueType {
CROSSFADE = 'crossfade', DEFAULT = 'default',
GAPLESS = 'gapless', PRIORITY = 'priority',
}
export enum PlaybackType {
LOCAL = 'local',
WEB = 'web',
} }
export enum PlayerRepeat { export enum PlayerRepeat {
@@ -134,6 +130,16 @@ export enum PlayerStatus {
PLAYING = 'playing', PLAYING = 'playing',
} }
export enum PlayerStyle {
CROSSFADE = 'crossfade',
GAPLESS = 'gapless',
}
export enum PlayerType {
LOCAL = 'local',
WEB = 'web',
}
export enum TableColumn { export enum TableColumn {
ACTIONS = 'actions', ACTIONS = 'actions',
ALBUM = 'album', ALBUM = 'album',