mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
Handle shuffle/repeat playback for mpv
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { PlaybackType, PlayerStatus } from '@/renderer/types';
|
import {
|
||||||
|
PlaybackType,
|
||||||
|
PlayerRepeat,
|
||||||
|
PlayerShuffle,
|
||||||
|
PlayerStatus,
|
||||||
|
} from '@/renderer/types';
|
||||||
import { usePlayerStore } from '../../../store';
|
import { usePlayerStore } from '../../../store';
|
||||||
import { useSettingsStore } from '../../../store/settings.store';
|
import { useSettingsStore } from '../../../store/settings.store';
|
||||||
import { mpvPlayer } from '../utils/mpv-player';
|
import { mpvPlayer } from '../utils/mpv-player';
|
||||||
@@ -11,14 +16,21 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
const { playersRef } = args;
|
const { playersRef } = args;
|
||||||
|
|
||||||
const settings = useSettingsStore((state) => state.player);
|
const settings = useSettingsStore((state) => state.player);
|
||||||
|
const setShuffle = usePlayerStore((state) => state.setShuffle);
|
||||||
|
const setRepeat = usePlayerStore((state) => state.setRepeat);
|
||||||
const play = usePlayerStore((state) => state.play);
|
const play = usePlayerStore((state) => state.play);
|
||||||
const pause = usePlayerStore((state) => state.pause);
|
const pause = usePlayerStore((state) => state.pause);
|
||||||
const prev = usePlayerStore((state) => state.prev);
|
const prev = usePlayerStore((state) => state.prev);
|
||||||
const next = usePlayerStore((state) => state.next);
|
const next = usePlayerStore((state) => state.next);
|
||||||
|
const setCurrentIndex = usePlayerStore((state) => state.setCurrentIndex);
|
||||||
|
const autoNext = usePlayerStore((state) => state.autoNext);
|
||||||
const queue = usePlayerStore((state) => state.queue.default);
|
const queue = usePlayerStore((state) => state.queue.default);
|
||||||
const playerStatus = usePlayerStore((state) => state.current.status);
|
const playerStatus = usePlayerStore((state) => state.current.status);
|
||||||
const currentPlayer = usePlayerStore((state) => state.current.player);
|
const currentPlayer = usePlayerStore((state) => state.current.player);
|
||||||
const currentTime = usePlayerStore((state) => state.current.time);
|
const repeat = usePlayerStore((state) => state.repeat);
|
||||||
|
const shuffle = usePlayerStore((state) => state.shuffle);
|
||||||
|
const playerType = useSettingsStore((state) => state.player.type);
|
||||||
|
|
||||||
const setCurrentTime = usePlayerStore((state) => state.setCurrentTime);
|
const setCurrentTime = usePlayerStore((state) => state.setCurrentTime);
|
||||||
|
|
||||||
const player1Ref = playersRef?.current?.player1;
|
const player1Ref = playersRef?.current?.player1;
|
||||||
@@ -39,11 +51,11 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
nextPlayerRef.getInternalPlayer().pause();
|
nextPlayerRef.getInternalPlayer().pause();
|
||||||
}, [currentPlayerRef, nextPlayerRef]);
|
}, [currentPlayerRef, nextPlayerRef]);
|
||||||
|
|
||||||
const stopPlayback = () => {
|
const stopPlayback = useCallback(() => {
|
||||||
player1Ref.getInternalPlayer().pause();
|
player1Ref.getInternalPlayer().pause();
|
||||||
player2Ref.getInternalPlayer().pause();
|
player2Ref.getInternalPlayer().pause();
|
||||||
resetPlayers();
|
resetPlayers();
|
||||||
};
|
}, [player1Ref, player2Ref, resetPlayers]);
|
||||||
|
|
||||||
const isMpvPlayer = isElectron() && settings.type === PlaybackType.LOCAL;
|
const isMpvPlayer = isElectron() && settings.type === PlaybackType.LOCAL;
|
||||||
|
|
||||||
@@ -65,7 +77,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
pause();
|
pause();
|
||||||
}, [isMpvPlayer, pause]);
|
}, [isMpvPlayer, pause]);
|
||||||
|
|
||||||
const handleStop = () => {
|
const handleStop = useCallback(() => {
|
||||||
if (isMpvPlayer) {
|
if (isMpvPlayer) {
|
||||||
mpvPlayer.stop();
|
mpvPlayer.stop();
|
||||||
} else {
|
} else {
|
||||||
@@ -74,33 +86,335 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
|
|
||||||
setCurrentTime(0);
|
setCurrentTime(0);
|
||||||
pause();
|
pause();
|
||||||
};
|
}, [isMpvPlayer, pause, setCurrentTime, stopPlayback]);
|
||||||
|
|
||||||
|
const handleToggleShuffle = useCallback(() => {
|
||||||
|
if (shuffle === PlayerShuffle.NONE) {
|
||||||
|
const playerData = setShuffle(PlayerShuffle.TRACK);
|
||||||
|
return mpvPlayer.setQueueNext(playerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
const playerData = setShuffle(PlayerShuffle.NONE);
|
||||||
|
return mpvPlayer.setQueueNext(playerData);
|
||||||
|
}, [setShuffle, shuffle]);
|
||||||
|
|
||||||
|
const handleToggleRepeat = useCallback(() => {
|
||||||
|
if (repeat === PlayerRepeat.NONE) {
|
||||||
|
const playerData = setRepeat(PlayerRepeat.ALL);
|
||||||
|
return mpvPlayer.setQueueNext(playerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeat === PlayerRepeat.ALL) {
|
||||||
|
const playerData = setRepeat(PlayerRepeat.ONE);
|
||||||
|
return mpvPlayer.setQueueNext(playerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return setRepeat(PlayerRepeat.NONE);
|
||||||
|
}, [repeat, setRepeat]);
|
||||||
|
|
||||||
|
const checkIsLastTrack = useCallback(() => {
|
||||||
|
const currentIndex =
|
||||||
|
shuffle === PlayerShuffle.NONE
|
||||||
|
? usePlayerStore.getState().current.index
|
||||||
|
: usePlayerStore.getState().current.shuffledIndex;
|
||||||
|
|
||||||
|
const queueLength = queue.length;
|
||||||
|
|
||||||
|
return currentIndex >= queueLength - 1;
|
||||||
|
}, [queue.length, shuffle]);
|
||||||
|
|
||||||
|
const checkIsFirstTrack = useCallback(() => {
|
||||||
|
const currentIndex =
|
||||||
|
shuffle === PlayerShuffle.NONE
|
||||||
|
? usePlayerStore.getState().current.index
|
||||||
|
: usePlayerStore.getState().current.shuffledIndex;
|
||||||
|
|
||||||
|
return currentIndex === 0;
|
||||||
|
}, [shuffle]);
|
||||||
|
|
||||||
|
const handleAutoNext = useCallback(() => {
|
||||||
|
const isLastTrack = checkIsLastTrack();
|
||||||
|
|
||||||
|
const handleRepeatAll = {
|
||||||
|
local: () => {
|
||||||
|
const playerData = autoNext();
|
||||||
|
mpvPlayer.playerAutoNext(playerData);
|
||||||
|
play();
|
||||||
|
},
|
||||||
|
web: () => {
|
||||||
|
autoNext();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRepeatNone = {
|
||||||
|
local: () => {
|
||||||
|
if (isLastTrack) {
|
||||||
|
const playerData = setCurrentIndex(0);
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
mpvPlayer.pause();
|
||||||
|
pause();
|
||||||
|
} else {
|
||||||
|
const playerData = autoNext();
|
||||||
|
mpvPlayer.playerAutoNext(playerData);
|
||||||
|
play();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
web: () => {
|
||||||
|
if (isLastTrack) {
|
||||||
|
resetPlayers();
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
resetPlayers();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRepeatOne = {
|
||||||
|
local: () => {
|
||||||
|
const playerData = autoNext();
|
||||||
|
mpvPlayer.playerAutoNext(playerData);
|
||||||
|
play();
|
||||||
|
},
|
||||||
|
web: () => {
|
||||||
|
if (isLastTrack) {
|
||||||
|
resetPlayers();
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
resetPlayers();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (repeat) {
|
||||||
|
case PlayerRepeat.NONE:
|
||||||
|
handleRepeatNone[playerType]();
|
||||||
|
break;
|
||||||
|
case PlayerRepeat.ALL:
|
||||||
|
handleRepeatAll[playerType]();
|
||||||
|
break;
|
||||||
|
case PlayerRepeat.ONE:
|
||||||
|
handleRepeatOne[playerType]();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
autoNext,
|
||||||
|
checkIsLastTrack,
|
||||||
|
next,
|
||||||
|
pause,
|
||||||
|
play,
|
||||||
|
playerType,
|
||||||
|
repeat,
|
||||||
|
resetPlayers,
|
||||||
|
setCurrentIndex,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleNextTrack = useCallback(() => {
|
const handleNextTrack = useCallback(() => {
|
||||||
const playerData = next();
|
const isLastTrack = checkIsLastTrack();
|
||||||
|
|
||||||
if (isMpvPlayer) {
|
const handleRepeatAll = {
|
||||||
mpvPlayer.setQueue(playerData);
|
local: () => {
|
||||||
mpvPlayer.next();
|
const playerData = next();
|
||||||
} else {
|
mpvPlayer.setQueue(playerData);
|
||||||
resetPlayers();
|
mpvPlayer.next();
|
||||||
|
},
|
||||||
|
web: () => {
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRepeatNone = {
|
||||||
|
local: () => {
|
||||||
|
if (isLastTrack) {
|
||||||
|
const playerData = setCurrentIndex(0);
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
mpvPlayer.pause();
|
||||||
|
pause();
|
||||||
|
} else {
|
||||||
|
const playerData = next();
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
mpvPlayer.next();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
web: () => {
|
||||||
|
if (isLastTrack) {
|
||||||
|
setCurrentIndex(0);
|
||||||
|
resetPlayers();
|
||||||
|
pause();
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
resetPlayers();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRepeatOne = {
|
||||||
|
local: () => {
|
||||||
|
const playerData = next();
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
mpvPlayer.next();
|
||||||
|
},
|
||||||
|
web: () => {
|
||||||
|
if (!isLastTrack) {
|
||||||
|
resetPlayers();
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
resetPlayers();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (repeat) {
|
||||||
|
case PlayerRepeat.NONE:
|
||||||
|
handleRepeatNone[playerType]();
|
||||||
|
break;
|
||||||
|
case PlayerRepeat.ALL:
|
||||||
|
handleRepeatAll[playerType]();
|
||||||
|
break;
|
||||||
|
case PlayerRepeat.ONE:
|
||||||
|
handleRepeatOne[playerType]();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentTime(0);
|
setCurrentTime(0);
|
||||||
}, [isMpvPlayer, next, resetPlayers, setCurrentTime]);
|
}, [
|
||||||
|
checkIsLastTrack,
|
||||||
|
next,
|
||||||
|
pause,
|
||||||
|
playerType,
|
||||||
|
repeat,
|
||||||
|
resetPlayers,
|
||||||
|
setCurrentIndex,
|
||||||
|
setCurrentTime,
|
||||||
|
]);
|
||||||
|
|
||||||
const handlePrevTrack = useCallback(() => {
|
const handlePrevTrack = useCallback(() => {
|
||||||
const playerData = prev();
|
const currentTime = isMpvPlayer
|
||||||
|
? usePlayerStore.getState().current.time
|
||||||
|
: currentPlayerRef.getCurrentTime();
|
||||||
|
|
||||||
if (isMpvPlayer) {
|
// Reset the current track more than 10 seconds have elapsed
|
||||||
mpvPlayer.setQueue(playerData);
|
if (currentTime >= 10) {
|
||||||
mpvPlayer.previous();
|
if (isMpvPlayer) {
|
||||||
} else {
|
return mpvPlayer.seekTo(0);
|
||||||
resetPlayers();
|
}
|
||||||
|
return currentPlayerRef.seekTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentTime(0);
|
const isFirstTrack = checkIsFirstTrack();
|
||||||
}, [isMpvPlayer, prev, resetPlayers, setCurrentTime]);
|
|
||||||
|
const handleRepeatAll = {
|
||||||
|
local: () => {
|
||||||
|
if (!isFirstTrack) {
|
||||||
|
const playerData = prev();
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
mpvPlayer.previous();
|
||||||
|
} else {
|
||||||
|
const playerData = setCurrentIndex(queue.length - 1);
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
mpvPlayer.previous();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
web: () => {
|
||||||
|
if (!isFirstTrack) {
|
||||||
|
prev();
|
||||||
|
resetPlayers();
|
||||||
|
} else {
|
||||||
|
setCurrentIndex(queue.length - 1);
|
||||||
|
resetPlayers();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRepeatNone = {
|
||||||
|
local: () => {
|
||||||
|
const playerData = prev();
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
mpvPlayer.previous();
|
||||||
|
},
|
||||||
|
web: () => {
|
||||||
|
if (!isFirstTrack) {
|
||||||
|
prev();
|
||||||
|
resetPlayers();
|
||||||
|
} else {
|
||||||
|
resetPlayers();
|
||||||
|
pause();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRepeatOne = {
|
||||||
|
local: () => {
|
||||||
|
if (!isFirstTrack) {
|
||||||
|
const playerData = prev();
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
mpvPlayer.previous();
|
||||||
|
} else {
|
||||||
|
mpvPlayer.stop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
web: () => {
|
||||||
|
if (!isFirstTrack) {
|
||||||
|
prev();
|
||||||
|
resetPlayers();
|
||||||
|
} else {
|
||||||
|
resetPlayers();
|
||||||
|
pause();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (repeat) {
|
||||||
|
case PlayerRepeat.NONE:
|
||||||
|
handleRepeatNone[playerType]();
|
||||||
|
break;
|
||||||
|
case PlayerRepeat.ALL:
|
||||||
|
handleRepeatAll[playerType]();
|
||||||
|
break;
|
||||||
|
case PlayerRepeat.ONE:
|
||||||
|
handleRepeatOne[playerType]();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (isMpvPlayer) {
|
||||||
|
// if (shuffle === PlayerShuffle.TRACK) {
|
||||||
|
// // const playerData = setCurrentIndex(shuffleTrackPrevious());
|
||||||
|
// // mpvPlayer.setQueue(playerData);
|
||||||
|
// mpvPlayer.previous();
|
||||||
|
// } else if (shuffle === PlayerShuffle.ALBUM) {
|
||||||
|
// } else {
|
||||||
|
// const playerData = prev();
|
||||||
|
// mpvPlayer.setQueue(playerData);
|
||||||
|
// mpvPlayer.previous();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!isMpvPlayer) {
|
||||||
|
// resetPlayers();
|
||||||
|
// }
|
||||||
|
|
||||||
|
return setCurrentTime(0);
|
||||||
|
}, [
|
||||||
|
checkIsFirstTrack,
|
||||||
|
currentPlayerRef,
|
||||||
|
isMpvPlayer,
|
||||||
|
pause,
|
||||||
|
playerType,
|
||||||
|
prev,
|
||||||
|
queue.length,
|
||||||
|
repeat,
|
||||||
|
resetPlayers,
|
||||||
|
setCurrentIndex,
|
||||||
|
setCurrentTime,
|
||||||
|
]);
|
||||||
|
|
||||||
const handlePlayPause = useCallback(() => {
|
const handlePlayPause = useCallback(() => {
|
||||||
if (queue) {
|
if (queue) {
|
||||||
@@ -115,12 +429,16 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
}, [handlePause, handlePlay, playerStatus, queue]);
|
}, [handlePause, handlePlay, playerStatus, queue]);
|
||||||
|
|
||||||
const handleSkipBackward = (seconds: number) => {
|
const handleSkipBackward = (seconds: number) => {
|
||||||
|
const currentTime = isMpvPlayer
|
||||||
|
? usePlayerStore.getState().current.time
|
||||||
|
: currentPlayerRef.getCurrentTime();
|
||||||
|
|
||||||
if (isMpvPlayer) {
|
if (isMpvPlayer) {
|
||||||
const newTime = currentTime - seconds;
|
const newTime = currentTime - seconds;
|
||||||
mpvPlayer.seek(-seconds);
|
mpvPlayer.seek(-seconds);
|
||||||
setCurrentTime(newTime < 0 ? 0 : newTime);
|
setCurrentTime(newTime < 0 ? 0 : newTime);
|
||||||
} else {
|
} else {
|
||||||
const newTime = currentPlayerRef?.getCurrentTime() - seconds;
|
const newTime = currentTime - seconds;
|
||||||
resetNextPlayer();
|
resetNextPlayer();
|
||||||
setCurrentTime(newTime);
|
setCurrentTime(newTime);
|
||||||
currentPlayerRef.seekTo(newTime);
|
currentPlayerRef.seekTo(newTime);
|
||||||
@@ -128,12 +446,16 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSkipForward = (seconds: number) => {
|
const handleSkipForward = (seconds: number) => {
|
||||||
|
const currentTime = isMpvPlayer
|
||||||
|
? usePlayerStore.getState().current.time
|
||||||
|
: currentPlayerRef.getCurrentTime();
|
||||||
|
|
||||||
if (isMpvPlayer) {
|
if (isMpvPlayer) {
|
||||||
const newTime = currentTime + seconds;
|
const newTime = currentTime + seconds;
|
||||||
mpvPlayer.seek(seconds);
|
mpvPlayer.seek(seconds);
|
||||||
setCurrentTime(newTime);
|
setCurrentTime(newTime);
|
||||||
} else {
|
} else {
|
||||||
const checkNewTime = currentPlayerRef?.getCurrentTime() + seconds;
|
const checkNewTime = currentTime + seconds;
|
||||||
const songDuration = currentPlayerRef.player.player.duration;
|
const songDuration = currentPlayerRef.player.player.duration;
|
||||||
|
|
||||||
const newTime =
|
const newTime =
|
||||||
@@ -160,46 +482,61 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ipc?.RENDERER_PLAYER_PLAY_PAUSE(() => {
|
ipc?.RENDERER_PLAYER_PLAY_PAUSE(() => {
|
||||||
const { status } = usePlayerStore.getState().current;
|
handlePlayPause();
|
||||||
if (status === PlayerStatus.PAUSED) {
|
|
||||||
play();
|
|
||||||
|
|
||||||
if (isMpvPlayer) {
|
// const { status } = usePlayerStore.getState().current;
|
||||||
mpvPlayer.play();
|
// if (status === PlayerStatus.PAUSED) {
|
||||||
}
|
// play();
|
||||||
} else {
|
|
||||||
pause();
|
// if (isMpvPlayer) {
|
||||||
if (isMpvPlayer) {
|
// mpvPlayer.play();
|
||||||
mpvPlayer.pause();
|
// }
|
||||||
}
|
// } else {
|
||||||
}
|
// pause();
|
||||||
|
// if (isMpvPlayer) {
|
||||||
|
// mpvPlayer.pause();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc?.RENDERER_PLAYER_NEXT(() => {
|
ipc?.RENDERER_PLAYER_NEXT(() => {
|
||||||
const playerData = next();
|
handleNextTrack();
|
||||||
|
// const playerData = next();
|
||||||
|
|
||||||
if (isMpvPlayer) {
|
// if (isMpvPlayer) {
|
||||||
mpvPlayer.setQueue(playerData);
|
// mpvPlayer.setQueue(playerData);
|
||||||
mpvPlayer.next();
|
// mpvPlayer.next();
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc?.RENDERER_PLAYER_PREVIOUS(() => {
|
ipc?.RENDERER_PLAYER_PREVIOUS(() => {
|
||||||
const playerData = prev();
|
handlePrevTrack();
|
||||||
if (isMpvPlayer) {
|
|
||||||
mpvPlayer.setQueue(playerData);
|
// const playerData = prev();
|
||||||
mpvPlayer.previous();
|
// if (isMpvPlayer) {
|
||||||
}
|
// mpvPlayer.setQueue(playerData);
|
||||||
|
// mpvPlayer.previous();
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc?.RENDERER_PLAYER_PLAY(() => play());
|
ipc?.RENDERER_PLAYER_PLAY(() => handlePlay());
|
||||||
|
|
||||||
ipc?.RENDERER_PLAYER_PAUSE(() => pause());
|
ipc?.RENDERER_PLAYER_PAUSE(() => handlePause());
|
||||||
|
|
||||||
ipc?.RENDERER_PLAYER_STOP(() => pause());
|
ipc?.RENDERER_PLAYER_STOP(() => handleStop());
|
||||||
|
|
||||||
ipc?.RENDERER_PLAYER_CURRENT_TIME((_event, time) => setCurrentTime(time));
|
ipc?.RENDERER_PLAYER_CURRENT_TIME((_event, time) => setCurrentTime(time));
|
||||||
|
|
||||||
|
ipc?.RENDERER_PLAYER_AUTO_NEXT(() => {
|
||||||
|
handleAutoNext();
|
||||||
|
// const playerData = autoNext();
|
||||||
|
// console.log('playerData', playerData);
|
||||||
|
// if (playerData.queue.next) {
|
||||||
|
// mpvPlayer.playerAutoNext(playerData);
|
||||||
|
// play();
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
ipc?.removeAllListeners('renderer-player-play-pause');
|
ipc?.removeAllListeners('renderer-player-play-pause');
|
||||||
ipc?.removeAllListeners('renderer-player-next');
|
ipc?.removeAllListeners('renderer-player-next');
|
||||||
@@ -208,8 +545,24 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
ipc?.removeAllListeners('renderer-player-pause');
|
ipc?.removeAllListeners('renderer-player-pause');
|
||||||
ipc?.removeAllListeners('renderer-player-stop');
|
ipc?.removeAllListeners('renderer-player-stop');
|
||||||
ipc?.removeAllListeners('renderer-player-current-time');
|
ipc?.removeAllListeners('renderer-player-current-time');
|
||||||
|
ipc?.removeAllListeners('renderer-player-auto-next');
|
||||||
};
|
};
|
||||||
}, [isMpvPlayer, next, pause, play, prev, setCurrentTime]);
|
}, [
|
||||||
|
autoNext,
|
||||||
|
handleAutoNext,
|
||||||
|
handleNextTrack,
|
||||||
|
handlePause,
|
||||||
|
handlePlay,
|
||||||
|
handlePlayPause,
|
||||||
|
handlePrevTrack,
|
||||||
|
handleStop,
|
||||||
|
isMpvPlayer,
|
||||||
|
next,
|
||||||
|
pause,
|
||||||
|
play,
|
||||||
|
prev,
|
||||||
|
setCurrentTime,
|
||||||
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleNextTrack,
|
handleNextTrack,
|
||||||
@@ -219,5 +572,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||||||
handleSkipBackward,
|
handleSkipBackward,
|
||||||
handleSkipForward,
|
handleSkipForward,
|
||||||
handleStop,
|
handleStop,
|
||||||
|
handleToggleRepeat,
|
||||||
|
handleToggleShuffle,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// renderer/preload.d.ts
|
// renderer/preload.d.ts
|
||||||
|
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { PlayerData, usePlayerStore } from '../../../store';
|
import { PlayerData } from '../../../store';
|
||||||
|
|
||||||
const ipc = isElectron() ? window.electron.ipcRenderer : null;
|
const ipc = isElectron() ? window.electron.ipcRenderer : null;
|
||||||
|
|
||||||
@@ -34,15 +34,6 @@ const volume = (value: number) => ipc?.PLAYER_VOLUME(value);
|
|||||||
|
|
||||||
const mute = () => ipc?.PLAYER_MUTE();
|
const mute = () => ipc?.PLAYER_MUTE();
|
||||||
|
|
||||||
const { autoNext } = usePlayerStore.getState();
|
|
||||||
|
|
||||||
ipc?.RENDERER_PLAYER_AUTO_NEXT(() => {
|
|
||||||
const playerData = autoNext();
|
|
||||||
if (playerData.queue.next) {
|
|
||||||
playerAutoNext(playerData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const mpvPlayer = {
|
export const mpvPlayer = {
|
||||||
currentTime,
|
currentTime,
|
||||||
mute,
|
mute,
|
||||||
|
|||||||
@@ -1,158 +1,422 @@
|
|||||||
/* eslint-disable prefer-destructuring */
|
/* eslint-disable prefer-destructuring */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import produce from 'immer';
|
|
||||||
import map from 'lodash/map';
|
import map from 'lodash/map';
|
||||||
|
import shuffle from 'lodash/shuffle';
|
||||||
import { nanoid } from 'nanoid/non-secure';
|
import { nanoid } from 'nanoid/non-secure';
|
||||||
import create from 'zustand';
|
import create from 'zustand';
|
||||||
import { devtools, persist } from 'zustand/middleware';
|
import { devtools, persist } from 'zustand/middleware';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
import { Song } from '@/renderer/api/types';
|
import { Song } from '@/renderer/api/types';
|
||||||
import { Play, PlayerStatus, UniqueId } from '@/renderer/types';
|
import {
|
||||||
|
Play,
|
||||||
|
PlayerRepeat,
|
||||||
|
PlayerShuffle,
|
||||||
|
PlayerStatus,
|
||||||
|
UniqueId,
|
||||||
|
} from '@/renderer/types';
|
||||||
|
|
||||||
type QueueSong = Song & UniqueId;
|
type QueueSong = Song & UniqueId;
|
||||||
|
|
||||||
export interface PlayerState {
|
export interface PlayerState {
|
||||||
current: {
|
current: {
|
||||||
index: number;
|
index: number;
|
||||||
|
nextIndex: number;
|
||||||
player: 1 | 2;
|
player: 1 | 2;
|
||||||
song: QueueSong;
|
previousIndex: number;
|
||||||
|
shuffledIndex: number;
|
||||||
|
song?: QueueSong;
|
||||||
status: PlayerStatus;
|
status: PlayerStatus;
|
||||||
time: number;
|
time: number;
|
||||||
};
|
};
|
||||||
muted: boolean;
|
muted: boolean;
|
||||||
queue: {
|
queue: {
|
||||||
default: QueueSong[];
|
default: QueueSong[];
|
||||||
previousNode: QueueSong;
|
previousNode?: QueueSong;
|
||||||
shuffled: QueueSong[];
|
shuffled: string[];
|
||||||
sorted: QueueSong[];
|
sorted: QueueSong[];
|
||||||
};
|
};
|
||||||
|
repeat: PlayerRepeat;
|
||||||
|
shuffle: PlayerShuffle;
|
||||||
volume: number;
|
volume: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlayerData {
|
export interface PlayerData {
|
||||||
current: {
|
current: {
|
||||||
index: number;
|
index: number;
|
||||||
|
nextIndex?: number;
|
||||||
player: 1 | 2;
|
player: 1 | 2;
|
||||||
song: QueueSong;
|
previousIndex?: number;
|
||||||
|
shuffledIndex: number;
|
||||||
|
song?: QueueSong;
|
||||||
status: PlayerStatus;
|
status: PlayerStatus;
|
||||||
};
|
};
|
||||||
player1: QueueSong;
|
player1?: QueueSong;
|
||||||
player2: QueueSong;
|
player2?: QueueSong;
|
||||||
queue: QueueData;
|
queue: QueueData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueueData {
|
export interface QueueData {
|
||||||
current: QueueSong;
|
current?: QueueSong;
|
||||||
next: QueueSong;
|
next?: QueueSong;
|
||||||
previous: QueueSong;
|
previous?: QueueSong;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlayerSlice extends PlayerState {
|
export interface PlayerSlice extends PlayerState {
|
||||||
addToQueue: (songs: Song[], type: Play) => PlayerData;
|
addToQueue: (songs: Song[], type: Play) => PlayerData;
|
||||||
autoNext: () => PlayerData;
|
autoNext: () => PlayerData;
|
||||||
|
checkIsFirstTrack: () => boolean;
|
||||||
|
checkIsLastTrack: () => boolean;
|
||||||
|
// getNextTrack: () => QueueSong;
|
||||||
|
// getPreviousTrack: () => QueueSong;
|
||||||
getPlayerData: () => PlayerData;
|
getPlayerData: () => PlayerData;
|
||||||
getQueueData: () => QueueData;
|
getQueueData: () => QueueData;
|
||||||
next: () => PlayerData;
|
next: () => PlayerData;
|
||||||
pause: () => void;
|
pause: () => void;
|
||||||
play: () => void;
|
play: () => void;
|
||||||
player1: () => QueueSong;
|
player1: () => QueueSong | undefined;
|
||||||
player2: () => QueueSong;
|
player2: () => QueueSong | undefined;
|
||||||
prev: () => PlayerData;
|
prev: () => PlayerData;
|
||||||
setCurrentIndex: (index: number) => PlayerData;
|
setCurrentIndex: (index: number) => PlayerData;
|
||||||
setCurrentTime: (time: number) => void;
|
setCurrentTime: (time: number) => void;
|
||||||
setMuted: (muted: boolean) => void;
|
setMuted: (muted: boolean) => void;
|
||||||
|
setRepeat: (type: PlayerRepeat) => PlayerData;
|
||||||
|
setShuffle: (type: PlayerShuffle) => PlayerData;
|
||||||
|
setShuffledIndex: (index: number) => PlayerData;
|
||||||
|
setStore: (data: Partial<PlayerState>) => void;
|
||||||
setVolume: (volume: number) => void;
|
setVolume: (volume: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usePlayerStore = create<PlayerSlice>()(
|
export const usePlayerStore = create<PlayerSlice>()(
|
||||||
persist(
|
persist(
|
||||||
devtools(
|
devtools(
|
||||||
(set, get) => ({
|
immer((set, get) => ({
|
||||||
addToQueue: (songs, type) => {
|
addToQueue: (songs, type) => {
|
||||||
|
const shuffledIndex = get().current.shuffledIndex;
|
||||||
|
const shuffledQueue = get().queue.shuffled;
|
||||||
const queueSongs = map(songs, (song) => ({
|
const queueSongs = map(songs, (song) => ({
|
||||||
...song,
|
...song,
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (type === Play.NOW) {
|
if (type === Play.NOW) {
|
||||||
set(
|
if (get().shuffle === PlayerShuffle.TRACK) {
|
||||||
produce((state) => {
|
const shuffledSongs = shuffle(queueSongs);
|
||||||
|
const foundIndex = queueSongs.findIndex(
|
||||||
|
(song) => song.uniqueId === shuffledSongs[0].uniqueId
|
||||||
|
);
|
||||||
|
set((state) => {
|
||||||
|
state.queue.shuffled = shuffledSongs.map(
|
||||||
|
(song) => song.uniqueId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
state.queue.default = queueSongs;
|
||||||
|
state.current.time = 0;
|
||||||
|
state.current.player = 1;
|
||||||
|
state.current.index = foundIndex;
|
||||||
|
state.current.shuffledIndex = 0;
|
||||||
|
state.current.song = shuffledSongs[0];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
set((state) => {
|
||||||
state.queue.default = queueSongs;
|
state.queue.default = queueSongs;
|
||||||
state.current.time = 0;
|
state.current.time = 0;
|
||||||
state.current.player = 1;
|
state.current.player = 1;
|
||||||
state.current.index = 0;
|
state.current.index = 0;
|
||||||
|
state.current.shuffledIndex = 0;
|
||||||
state.current.song = queueSongs[0];
|
state.current.song = queueSongs[0];
|
||||||
})
|
});
|
||||||
);
|
}
|
||||||
} else if (type === Play.LAST) {
|
} else if (type === Play.LAST) {
|
||||||
set(
|
// Shuffle the queue after the current track
|
||||||
produce((state) => {
|
const shuffledQueueWithNewSongs =
|
||||||
state.queue.default = [...get().queue.default, ...queueSongs];
|
get().shuffle === PlayerShuffle.TRACK
|
||||||
})
|
? [
|
||||||
);
|
...shuffledQueue.slice(0, shuffledIndex + 1),
|
||||||
|
...shuffle([
|
||||||
|
...queueSongs.map((song) => song.uniqueId),
|
||||||
|
...shuffledQueue.slice(shuffledIndex + 1),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
state.queue.default = [...get().queue.default, ...queueSongs];
|
||||||
|
state.queue.shuffled = shuffledQueueWithNewSongs;
|
||||||
|
});
|
||||||
} else if (type === Play.NEXT) {
|
} else if (type === Play.NEXT) {
|
||||||
const queue = get().queue.default;
|
const queue = get().queue.default;
|
||||||
const currentIndex = get().current.index;
|
const currentIndex = get().current.index;
|
||||||
|
|
||||||
set(
|
// Shuffle the queue after the current track
|
||||||
produce((state) => {
|
const shuffledQueueWithNewSongs =
|
||||||
state.queue.default = [
|
get().shuffle === PlayerShuffle.TRACK
|
||||||
...queue.slice(0, currentIndex + 1),
|
? [
|
||||||
...queueSongs,
|
...shuffledQueue.slice(0, shuffledIndex + 1),
|
||||||
...queue.slice(currentIndex + 1),
|
...shuffle([
|
||||||
];
|
...queueSongs.map((song) => song.uniqueId),
|
||||||
})
|
...shuffledQueue.slice(shuffledIndex + 1),
|
||||||
);
|
]),
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
state.queue.default = [
|
||||||
|
...queue.slice(0, currentIndex + 1),
|
||||||
|
...queueSongs,
|
||||||
|
...queue.slice(currentIndex + 1),
|
||||||
|
];
|
||||||
|
state.queue.shuffled = shuffledQueueWithNewSongs;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return get().getPlayerData();
|
return get().getPlayerData();
|
||||||
},
|
},
|
||||||
autoNext: () => {
|
autoNext: () => {
|
||||||
set(
|
const isLastTrack = get().checkIsLastTrack();
|
||||||
produce((state) => {
|
const repeat = get().repeat;
|
||||||
|
|
||||||
|
if (repeat === PlayerRepeat.ONE) {
|
||||||
|
const nextIndex = get().current.index;
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
state.current.time = 0;
|
state.current.time = 0;
|
||||||
state.current.index += 1;
|
state.current.index = nextIndex;
|
||||||
|
state.current.shuffledIndex = get().current.shuffledIndex;
|
||||||
state.current.player = state.current.player === 1 ? 2 : 1;
|
state.current.player = state.current.player === 1 ? 2 : 1;
|
||||||
state.current.song = state.queue.default[state.current.index];
|
state.current.song = get().queue.default[nextIndex];
|
||||||
state.queue.previousNode = get().current.song;
|
state.queue.previousNode = get().current.song;
|
||||||
})
|
});
|
||||||
);
|
} else if (get().shuffle === PlayerShuffle.TRACK) {
|
||||||
|
const nextShuffleIndex = isLastTrack
|
||||||
|
? 0
|
||||||
|
: get().current.shuffledIndex + 1;
|
||||||
|
|
||||||
|
const nextSong = get().queue.default.find(
|
||||||
|
(song) => song.uniqueId === get().queue.shuffled[nextShuffleIndex]
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextSongIndex = get().queue.default.findIndex(
|
||||||
|
(song) => song.uniqueId === nextSong!.uniqueId
|
||||||
|
);
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
state.current.time = 0;
|
||||||
|
state.current.index = nextSongIndex!;
|
||||||
|
state.current.shuffledIndex = nextShuffleIndex;
|
||||||
|
state.current.player = state.current.player === 1 ? 2 : 1;
|
||||||
|
state.current.song = nextSong!;
|
||||||
|
state.queue.previousNode = get().current.song;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const nextIndex = isLastTrack ? 0 : get().current.index + 1;
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
state.current.time = 0;
|
||||||
|
state.current.index = nextIndex;
|
||||||
|
state.current.player = state.current.player === 1 ? 2 : 1;
|
||||||
|
state.current.song = get().queue.default[nextIndex];
|
||||||
|
state.queue.previousNode = get().current.song;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return get().getPlayerData();
|
return get().getPlayerData();
|
||||||
},
|
},
|
||||||
|
checkIsFirstTrack: () => {
|
||||||
|
const currentIndex =
|
||||||
|
get().shuffle === PlayerShuffle.TRACK
|
||||||
|
? get().current.shuffledIndex
|
||||||
|
: get().current.index;
|
||||||
|
|
||||||
|
return currentIndex === 0;
|
||||||
|
},
|
||||||
|
checkIsLastTrack: () => {
|
||||||
|
const currentIndex =
|
||||||
|
get().shuffle === PlayerShuffle.TRACK
|
||||||
|
? get().current.shuffledIndex
|
||||||
|
: get().current.index;
|
||||||
|
|
||||||
|
return currentIndex === get().queue.default.length - 1;
|
||||||
|
},
|
||||||
current: {
|
current: {
|
||||||
index: 0,
|
index: 0,
|
||||||
|
nextIndex: 0,
|
||||||
player: 1,
|
player: 1,
|
||||||
|
previousIndex: 0,
|
||||||
|
shuffledIndex: 0,
|
||||||
song: {} as QueueSong,
|
song: {} as QueueSong,
|
||||||
status: PlayerStatus.PAUSED,
|
status: PlayerStatus.PAUSED,
|
||||||
time: 0,
|
time: 0,
|
||||||
},
|
},
|
||||||
|
// getNextTrack: () => {
|
||||||
|
// const shuffle = get().shuffle;
|
||||||
|
// const queue = get().queue.default;
|
||||||
|
// const shuffledQueue = get().queue.shuffled;
|
||||||
|
|
||||||
|
// if (shuffle === PlayerShuffle.TRACK) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const currentIndex =
|
||||||
|
// shuffle === PlayerShuffle.TRACK
|
||||||
|
// ? get().current.shuffledIndex
|
||||||
|
// : get().current.index;
|
||||||
|
|
||||||
|
// const current = queue.find(
|
||||||
|
// (song) => song.uniqueId === queue[currentIndex]
|
||||||
|
// ) as QueueSong;
|
||||||
|
|
||||||
|
// let nextSongIndex: number | undefined;
|
||||||
|
// if (repeat === PlayerRepeat.ALL) {
|
||||||
|
// if (isLastTrack) nextSongIndex = 0;
|
||||||
|
// else nextSongIndex = currentIndex + 1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (repeat === PlayerRepeat.ONE) {
|
||||||
|
// nextSongIndex = currentIndex;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (repeat === PlayerRepeat.NONE) {
|
||||||
|
// if (isLastTrack) nextSongIndex = undefined;
|
||||||
|
// else nextSongIndex = currentIndex + 1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const next = nextSongIndex
|
||||||
|
// ? (queue.find(
|
||||||
|
// (song) => song.uniqueId === queue[nextSongIndex as number]
|
||||||
|
// ) as QueueSong)
|
||||||
|
// : undefined;
|
||||||
|
// },
|
||||||
getPlayerData: () => {
|
getPlayerData: () => {
|
||||||
const queue = get().queue.default;
|
const queue = get().queue.default;
|
||||||
const currentPlayer = get().current.player;
|
const currentPlayer = get().current.player;
|
||||||
|
const repeat = get().repeat;
|
||||||
|
const isLastTrack = get().checkIsLastTrack();
|
||||||
|
const isFirstTrack = get().checkIsFirstTrack();
|
||||||
|
|
||||||
const player1 =
|
let player1;
|
||||||
currentPlayer === 1
|
let player2;
|
||||||
? queue[get().current.index]
|
if (get().shuffle === PlayerShuffle.TRACK) {
|
||||||
: queue[get().current.index + 1];
|
const shuffledQueue = get().queue.shuffled;
|
||||||
|
const shuffledIndex = get().current.shuffledIndex;
|
||||||
|
const current = queue.find(
|
||||||
|
(song) => song.uniqueId === shuffledQueue[shuffledIndex]
|
||||||
|
) as QueueSong;
|
||||||
|
|
||||||
const player2 =
|
let nextSongIndex: number | undefined;
|
||||||
|
let previousSongIndex: number | undefined;
|
||||||
|
if (repeat === PlayerRepeat.ALL) {
|
||||||
|
if (isLastTrack) nextSongIndex = 0;
|
||||||
|
else nextSongIndex = shuffledIndex + 1;
|
||||||
|
|
||||||
|
if (isFirstTrack) previousSongIndex = queue.length - 1;
|
||||||
|
else previousSongIndex = shuffledIndex - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeat === PlayerRepeat.ONE) {
|
||||||
|
nextSongIndex = shuffledIndex;
|
||||||
|
previousSongIndex = shuffledIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeat === PlayerRepeat.NONE) {
|
||||||
|
if (isLastTrack) nextSongIndex = undefined;
|
||||||
|
else nextSongIndex = shuffledIndex + 1;
|
||||||
|
|
||||||
|
if (isFirstTrack) previousSongIndex = undefined;
|
||||||
|
else previousSongIndex = shuffledIndex - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = nextSongIndex
|
||||||
|
? (queue.find(
|
||||||
|
(song) =>
|
||||||
|
song.uniqueId === shuffledQueue[nextSongIndex as number]
|
||||||
|
) as QueueSong)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const previous = queue.find(
|
||||||
|
(song) => song.uniqueId === shuffledQueue[shuffledIndex - 1]
|
||||||
|
) as QueueSong;
|
||||||
|
|
||||||
|
player1 = currentPlayer === 1 ? current : next;
|
||||||
|
player2 = currentPlayer === 1 ? next : current;
|
||||||
|
|
||||||
|
return {
|
||||||
|
current: {
|
||||||
|
index: get().current.index,
|
||||||
|
nextIndex: nextSongIndex,
|
||||||
|
player: get().current.player,
|
||||||
|
previousIndex: previousSongIndex,
|
||||||
|
shuffledIndex: get().current.shuffledIndex,
|
||||||
|
song: get().current.song,
|
||||||
|
status: get().current.status,
|
||||||
|
},
|
||||||
|
player1,
|
||||||
|
player2,
|
||||||
|
queue: {
|
||||||
|
current,
|
||||||
|
next,
|
||||||
|
previous,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentIndex = get().current.index;
|
||||||
|
|
||||||
|
let nextSongIndex;
|
||||||
|
let previousSongIndex;
|
||||||
|
if (repeat === PlayerRepeat.ALL) {
|
||||||
|
if (isLastTrack) nextSongIndex = 0;
|
||||||
|
else nextSongIndex = currentIndex + 1;
|
||||||
|
|
||||||
|
if (isFirstTrack) previousSongIndex = queue.length - 1;
|
||||||
|
else previousSongIndex = currentIndex - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeat === PlayerRepeat.ONE) {
|
||||||
|
nextSongIndex = currentIndex;
|
||||||
|
previousSongIndex = currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeat === PlayerRepeat.NONE) {
|
||||||
|
if (isLastTrack) nextSongIndex = undefined;
|
||||||
|
else nextSongIndex = currentIndex + 1;
|
||||||
|
|
||||||
|
if (isFirstTrack) previousSongIndex = undefined;
|
||||||
|
else previousSongIndex = currentIndex - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
player1 =
|
||||||
currentPlayer === 1
|
currentPlayer === 1
|
||||||
? queue[get().current.index + 1]
|
? queue[currentIndex]
|
||||||
: queue[get().current.index];
|
: nextSongIndex !== undefined
|
||||||
|
? queue[nextSongIndex]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
player2 =
|
||||||
|
currentPlayer === 1
|
||||||
|
? nextSongIndex !== undefined
|
||||||
|
? queue[nextSongIndex]
|
||||||
|
: undefined
|
||||||
|
: queue[currentIndex];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
current: {
|
current: {
|
||||||
index: get().current.index,
|
index: currentIndex,
|
||||||
|
nextIndex: nextSongIndex,
|
||||||
player: get().current.player,
|
player: get().current.player,
|
||||||
|
previousIndex: previousSongIndex,
|
||||||
|
shuffledIndex: get().current.shuffledIndex,
|
||||||
song: get().current.song,
|
song: get().current.song,
|
||||||
status: get().current.status,
|
status: get().current.status,
|
||||||
},
|
},
|
||||||
player1,
|
player1,
|
||||||
player2,
|
player2,
|
||||||
queue: {
|
queue: {
|
||||||
current: queue[get().current.index],
|
current: queue[currentIndex],
|
||||||
next: queue[get().current.index + 1],
|
next:
|
||||||
previous: queue[get().current.index - 1],
|
nextSongIndex !== undefined ? queue[nextSongIndex] : undefined,
|
||||||
|
previous: queue[currentIndex - 1],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -166,31 +430,60 @@ export const usePlayerStore = create<PlayerSlice>()(
|
|||||||
},
|
},
|
||||||
muted: false,
|
muted: false,
|
||||||
next: () => {
|
next: () => {
|
||||||
set(
|
const isLastTrack = get().checkIsLastTrack();
|
||||||
produce((state) => {
|
const repeat = get().repeat;
|
||||||
|
|
||||||
|
if (get().shuffle === PlayerShuffle.TRACK) {
|
||||||
|
const nextShuffleIndex = isLastTrack
|
||||||
|
? 0
|
||||||
|
: get().current.shuffledIndex + 1;
|
||||||
|
|
||||||
|
const nextSong = get().queue.default.find(
|
||||||
|
(song) => song.uniqueId === get().queue.shuffled[nextShuffleIndex]
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextSongIndex = get().queue.default.findIndex(
|
||||||
|
(song) => song.uniqueId === nextSong?.uniqueId
|
||||||
|
);
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
state.current.time = 0;
|
state.current.time = 0;
|
||||||
state.current.index += 1;
|
state.current.index = nextSongIndex!;
|
||||||
|
state.current.shuffledIndex = nextShuffleIndex;
|
||||||
state.current.player = 1;
|
state.current.player = 1;
|
||||||
state.current.song = state.queue.default[state.current.index];
|
state.current.song = nextSong!;
|
||||||
state.queue.previousNode = get().current.song;
|
state.queue.previousNode = get().current.song;
|
||||||
})
|
});
|
||||||
);
|
} else {
|
||||||
|
const nextIndex =
|
||||||
|
repeat === PlayerRepeat.ALL
|
||||||
|
? isLastTrack
|
||||||
|
? 0
|
||||||
|
: get().current.index + 1
|
||||||
|
: isLastTrack
|
||||||
|
? get().current.index
|
||||||
|
: get().current.index + 1;
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
state.current.time = 0;
|
||||||
|
state.current.index = nextIndex;
|
||||||
|
state.current.player = 1;
|
||||||
|
state.current.song = get().queue.default[nextIndex];
|
||||||
|
state.queue.previousNode = get().current.song;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return get().getPlayerData();
|
return get().getPlayerData();
|
||||||
},
|
},
|
||||||
pause: () => {
|
pause: () => {
|
||||||
set(
|
set((state) => {
|
||||||
produce((state) => {
|
state.current.status = PlayerStatus.PAUSED;
|
||||||
state.current.status = PlayerStatus.PAUSED;
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
play: () => {
|
play: () => {
|
||||||
set(
|
set((state) => {
|
||||||
produce((state) => {
|
state.current.status = PlayerStatus.PLAYING;
|
||||||
state.current.status = PlayerStatus.PLAYING;
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
player1: () => {
|
player1: () => {
|
||||||
return get().getPlayerData().player1;
|
return get().getPlayerData().player1;
|
||||||
@@ -199,61 +492,154 @@ export const usePlayerStore = create<PlayerSlice>()(
|
|||||||
return get().getPlayerData().player2;
|
return get().getPlayerData().player2;
|
||||||
},
|
},
|
||||||
prev: () => {
|
prev: () => {
|
||||||
set(
|
const isFirstTrack = get().checkIsFirstTrack();
|
||||||
produce((state) => {
|
const repeat = get().repeat;
|
||||||
|
|
||||||
|
if (get().shuffle === PlayerShuffle.TRACK) {
|
||||||
|
const prevShuffleIndex = isFirstTrack
|
||||||
|
? 0
|
||||||
|
: get().current.shuffledIndex - 1;
|
||||||
|
|
||||||
|
const prevSong = get().queue.default.find(
|
||||||
|
(song) => song.uniqueId === get().queue.shuffled[prevShuffleIndex]
|
||||||
|
);
|
||||||
|
|
||||||
|
const prevIndex = get().queue.default.findIndex(
|
||||||
|
(song) => song.uniqueId === prevSong?.uniqueId
|
||||||
|
);
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
state.current.time = 0;
|
state.current.time = 0;
|
||||||
state.current.index =
|
state.current.index = prevIndex!;
|
||||||
state.current.index - 1 < 0 ? 0 : state.current.index - 1;
|
state.current.shuffledIndex = prevShuffleIndex;
|
||||||
|
state.current.player = 1;
|
||||||
|
state.current.song = prevSong!;
|
||||||
|
state.queue.previousNode = get().current.song;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let prevIndex: number;
|
||||||
|
if (repeat === PlayerRepeat.ALL) {
|
||||||
|
prevIndex = isFirstTrack
|
||||||
|
? get().queue.default.length - 1
|
||||||
|
: get().current.index - 1;
|
||||||
|
} else {
|
||||||
|
prevIndex = isFirstTrack ? 0 : get().current.index - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
state.current.time = 0;
|
||||||
|
state.current.index = prevIndex;
|
||||||
state.current.player = 1;
|
state.current.player = 1;
|
||||||
state.current.song = state.queue.default[state.current.index];
|
state.current.song = state.queue.default[state.current.index];
|
||||||
state.queue.previousNode = get().current.song;
|
state.queue.previousNode = get().current.song;
|
||||||
})
|
});
|
||||||
);
|
}
|
||||||
|
|
||||||
return get().getPlayerData();
|
return get().getPlayerData();
|
||||||
},
|
},
|
||||||
queue: {
|
queue: {
|
||||||
default: [],
|
default: [],
|
||||||
|
played: [],
|
||||||
previousNode: {} as QueueSong,
|
previousNode: {} as QueueSong,
|
||||||
shuffled: [],
|
shuffled: [],
|
||||||
sorted: [],
|
sorted: [],
|
||||||
},
|
},
|
||||||
|
repeat: PlayerRepeat.NONE,
|
||||||
setCurrentIndex: (index) => {
|
setCurrentIndex: (index) => {
|
||||||
set(
|
if (get().shuffle === PlayerShuffle.TRACK) {
|
||||||
produce((state) => {
|
const foundSong = get().queue.default.find(
|
||||||
|
(song) => song.uniqueId === get().queue.shuffled[index]
|
||||||
|
);
|
||||||
|
const foundIndex = get().queue.default.findIndex(
|
||||||
|
(song) => song.uniqueId === foundSong?.uniqueId
|
||||||
|
);
|
||||||
|
set((state) => {
|
||||||
|
state.current.time = 0;
|
||||||
|
state.current.index = foundIndex!;
|
||||||
|
state.current.shuffledIndex = index;
|
||||||
|
state.current.player = 1;
|
||||||
|
state.current.song = foundSong!;
|
||||||
|
state.queue.previousNode = get().current.song;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
set((state) => {
|
||||||
state.current.time = 0;
|
state.current.time = 0;
|
||||||
state.current.index = index;
|
state.current.index = index;
|
||||||
state.current.player = 1;
|
state.current.player = 1;
|
||||||
state.current.song = state.queue.default[index];
|
state.current.song = state.queue.default[index];
|
||||||
state.queue.previousNode = get().current.song;
|
state.queue.previousNode = get().current.song;
|
||||||
})
|
});
|
||||||
);
|
}
|
||||||
|
|
||||||
return get().getPlayerData();
|
return get().getPlayerData();
|
||||||
},
|
},
|
||||||
setCurrentTime: (time) => {
|
setCurrentTime: (time) => {
|
||||||
set(
|
set((state) => {
|
||||||
produce((state) => {
|
state.current.time = time;
|
||||||
state.current.time = time;
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
setMuted: (muted: boolean) => {
|
setMuted: (muted: boolean) => {
|
||||||
set(
|
set((state) => {
|
||||||
produce((state) => {
|
state.muted = muted;
|
||||||
state.muted = muted;
|
});
|
||||||
})
|
},
|
||||||
|
setRepeat: (type: PlayerRepeat) => {
|
||||||
|
set((state) => {
|
||||||
|
state.repeat = type;
|
||||||
|
});
|
||||||
|
|
||||||
|
return get().getPlayerData();
|
||||||
|
},
|
||||||
|
setShuffle: (type: PlayerShuffle) => {
|
||||||
|
if (type === PlayerShuffle.NONE) {
|
||||||
|
set((state) => {
|
||||||
|
state.shuffle = type;
|
||||||
|
state.queue.shuffled = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
return get().getPlayerData();
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentSongId = get().current.song?.uniqueId;
|
||||||
|
|
||||||
|
const queueWithoutCurrentSong = get().queue.default.filter(
|
||||||
|
(song) => song.uniqueId !== currentSongId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const shuffledSongIds = shuffle(queueWithoutCurrentSong).map(
|
||||||
|
(song) => song.uniqueId
|
||||||
|
);
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
state.shuffle = type;
|
||||||
|
state.current.shuffledIndex = 0;
|
||||||
|
state.queue.shuffled = [currentSongId!, ...shuffledSongIds];
|
||||||
|
});
|
||||||
|
|
||||||
|
return get().getPlayerData();
|
||||||
|
},
|
||||||
|
setShuffledIndex: (index) => {
|
||||||
|
set((state) => {
|
||||||
|
state.current.time = 0;
|
||||||
|
state.current.shuffledIndex = index;
|
||||||
|
state.current.player = 1;
|
||||||
|
state.current.song = state.queue.default[index];
|
||||||
|
state.queue.previousNode = get().current.song;
|
||||||
|
});
|
||||||
|
|
||||||
|
return get().getPlayerData();
|
||||||
|
},
|
||||||
|
setStore: (data) => {
|
||||||
|
set({ ...get(), ...data });
|
||||||
},
|
},
|
||||||
setVolume: (volume: number) => {
|
setVolume: (volume: number) => {
|
||||||
set(
|
set((state) => {
|
||||||
produce((state) => {
|
state.volume = volume;
|
||||||
state.volume = volume;
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
shuffle: PlayerShuffle.NONE,
|
||||||
volume: 50,
|
volume: 50,
|
||||||
}),
|
})),
|
||||||
{ name: 'store_player' }
|
{ name: 'store_player' }
|
||||||
),
|
),
|
||||||
{ name: 'store_player' }
|
{ name: 'store_player' }
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
Play,
|
Play,
|
||||||
PlaybackStyle,
|
PlaybackStyle,
|
||||||
PlaybackType,
|
PlaybackType,
|
||||||
PlayerRepeat,
|
|
||||||
} from '@/renderer/types';
|
} from '@/renderer/types';
|
||||||
|
|
||||||
export interface SettingsState {
|
export interface SettingsState {
|
||||||
@@ -20,12 +19,10 @@ export interface SettingsState {
|
|||||||
globalMediaHotkeys: boolean;
|
globalMediaHotkeys: boolean;
|
||||||
muted: boolean;
|
muted: boolean;
|
||||||
playButtonBehavior: Play;
|
playButtonBehavior: Play;
|
||||||
repeat: PlayerRepeat;
|
|
||||||
scrobble: {
|
scrobble: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
scrobbleAtPercentage: number;
|
scrobbleAtPercentage: number;
|
||||||
};
|
};
|
||||||
shuffle: boolean;
|
|
||||||
skipButtons: {
|
skipButtons: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
skipBackwardSeconds: number;
|
skipBackwardSeconds: number;
|
||||||
@@ -33,7 +30,6 @@ export interface SettingsState {
|
|||||||
};
|
};
|
||||||
style: PlaybackStyle;
|
style: PlaybackStyle;
|
||||||
type: PlaybackType;
|
type: PlaybackType;
|
||||||
volume: number;
|
|
||||||
};
|
};
|
||||||
tab: 'general' | 'playback' | 'view' | string;
|
tab: 'general' | 'playback' | 'view' | string;
|
||||||
}
|
}
|
||||||
@@ -53,12 +49,10 @@ export const useSettingsStore = create<SettingsSlice>()(
|
|||||||
globalMediaHotkeys: true,
|
globalMediaHotkeys: true,
|
||||||
muted: false,
|
muted: false,
|
||||||
playButtonBehavior: Play.NOW,
|
playButtonBehavior: Play.NOW,
|
||||||
repeat: PlayerRepeat.NONE,
|
|
||||||
scrobble: {
|
scrobble: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
scrobbleAtPercentage: 75,
|
scrobbleAtPercentage: 75,
|
||||||
},
|
},
|
||||||
shuffle: false,
|
|
||||||
skipButtons: {
|
skipButtons: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
skipBackwardSeconds: 10,
|
skipBackwardSeconds: 10,
|
||||||
@@ -66,7 +60,6 @@ export const useSettingsStore = create<SettingsSlice>()(
|
|||||||
},
|
},
|
||||||
style: PlaybackStyle.GAPLESS,
|
style: PlaybackStyle.GAPLESS,
|
||||||
type: PlaybackType.LOCAL,
|
type: PlaybackType.LOCAL,
|
||||||
volume: 50,
|
|
||||||
},
|
},
|
||||||
setSettings: (data) => {
|
setSettings: (data) => {
|
||||||
set({ ...get(), ...data });
|
set({ ...get(), ...data });
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ export enum PlayerRepeat {
|
|||||||
ONE = 'one',
|
ONE = 'one',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum PlayerShuffle {
|
||||||
|
ALBUM = 'album',
|
||||||
|
NONE = 'none',
|
||||||
|
TRACK = 'track',
|
||||||
|
}
|
||||||
|
|
||||||
export enum Play {
|
export enum Play {
|
||||||
LAST = 'last',
|
LAST = 'last',
|
||||||
NEXT = 'next',
|
NEXT = 'next',
|
||||||
|
|||||||
Reference in New Issue
Block a user