Fix various queue behavior
- Fix add next behavior when shuffle is enabled - Fix shuffled queue when songs are removed from queue - Fix queue indices when currently playing song is removed - Re-shuffle queue after queue is finished when shuffle is enabled
This commit is contained in:
parent
2fac9efc1b
commit
a6990fd732
3 changed files with 93 additions and 45 deletions
|
@ -44,7 +44,12 @@ import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||||
import { useDeletePlaylist } from '/@/renderer/features/playlists';
|
import { useDeletePlaylist } from '/@/renderer/features/playlists';
|
||||||
import { useRemoveFromPlaylist } from '/@/renderer/features/playlists/mutations/remove-from-playlist-mutation';
|
import { useRemoveFromPlaylist } from '/@/renderer/features/playlists/mutations/remove-from-playlist-mutation';
|
||||||
import { useCreateFavorite, useDeleteFavorite, useSetRating } from '/@/renderer/features/shared';
|
import { useCreateFavorite, useDeleteFavorite, useSetRating } from '/@/renderer/features/shared';
|
||||||
import { useAuthStore, useCurrentServer, useQueueControls } from '/@/renderer/store';
|
import {
|
||||||
|
useAuthStore,
|
||||||
|
useCurrentServer,
|
||||||
|
usePlayerStore,
|
||||||
|
useQueueControls,
|
||||||
|
} from '/@/renderer/store';
|
||||||
import { usePlayerType } from '/@/renderer/store/settings.store';
|
import { usePlayerType } from '/@/renderer/store/settings.store';
|
||||||
import { Play, PlaybackType } from '/@/renderer/types';
|
import { Play, PlaybackType } from '/@/renderer/types';
|
||||||
|
|
||||||
|
@ -558,10 +563,16 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
||||||
const uniqueIds = ctx.dataNodes?.map((row) => row.data.uniqueId);
|
const uniqueIds = ctx.dataNodes?.map((row) => row.data.uniqueId);
|
||||||
if (!uniqueIds?.length) return;
|
if (!uniqueIds?.length) return;
|
||||||
|
|
||||||
|
const currentSong = usePlayerStore.getState().current.song;
|
||||||
const playerData = removeFromQueue(uniqueIds);
|
const playerData = removeFromQueue(uniqueIds);
|
||||||
|
const isCurrentSongRemoved = currentSong && uniqueIds.includes(currentSong?.uniqueId);
|
||||||
|
|
||||||
if (playerType === PlaybackType.LOCAL) {
|
if (playerType === PlaybackType.LOCAL) {
|
||||||
mpvPlayer.setQueueNext(playerData);
|
if (isCurrentSongRemoved) {
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
} else {
|
||||||
|
mpvPlayer.setQueueNext(playerData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [ctx.dataNodes, playerType, removeFromQueue]);
|
}, [ctx.dataNodes, playerType, removeFromQueue]);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { Song } from '/@/renderer/api/types';
|
||||||
import { usePlayerControls, useQueueControls } from '/@/renderer/store';
|
import { usePlayerControls, useQueueControls } from '/@/renderer/store';
|
||||||
import { PlaybackType, TableType } from '/@/renderer/types';
|
import { PlaybackType, TableType } from '/@/renderer/types';
|
||||||
import { usePlayerType } from '/@/renderer/store/settings.store';
|
import { usePlayerType } from '/@/renderer/store/settings.store';
|
||||||
import { useSetCurrentTime } from '../../../store/player.store';
|
import { usePlayerStore, useSetCurrentTime } from '../../../store/player.store';
|
||||||
import { TableConfigDropdown } from '/@/renderer/components/virtual-table';
|
import { TableConfigDropdown } from '/@/renderer/components/virtual-table';
|
||||||
|
|
||||||
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
|
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
|
||||||
|
@ -63,10 +63,16 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
|
||||||
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 playerData = removeFromQueue(uniqueIds);
|
const playerData = removeFromQueue(uniqueIds);
|
||||||
|
const isCurrentSongRemoved = currentSong && uniqueIds.includes(currentSong.uniqueId);
|
||||||
|
|
||||||
if (playerType === PlaybackType.LOCAL) {
|
if (playerType === PlaybackType.LOCAL) {
|
||||||
mpvPlayer.setQueueNext(playerData);
|
if (isCurrentSongRemoved) {
|
||||||
|
mpvPlayer.setQueue(playerData);
|
||||||
|
} else {
|
||||||
|
mpvPlayer.setQueueNext(playerData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ export interface PlayerSlice extends PlayerState {
|
||||||
addToQueue: (args: { initialIndex: number; playType: Play; songs: QueueSong[] }) => PlayerData;
|
addToQueue: (args: { initialIndex: number; playType: Play; songs: QueueSong[] }) => PlayerData;
|
||||||
autoNext: () => PlayerData;
|
autoNext: () => PlayerData;
|
||||||
checkIsFirstTrack: () => boolean;
|
checkIsFirstTrack: () => boolean;
|
||||||
checkIsLastTrack: () => boolean;
|
checkIsLastTrack: (type?: 'next' | 'prev') => boolean;
|
||||||
clearQueue: () => PlayerData;
|
clearQueue: () => PlayerData;
|
||||||
getPlayerData: () => PlayerData;
|
getPlayerData: () => PlayerData;
|
||||||
getQueueData: () => QueueData;
|
getQueueData: () => QueueData;
|
||||||
|
@ -101,7 +101,7 @@ export const usePlayerStore = create<PlayerSlice>()(
|
||||||
const { initialIndex, playType, songs } = args;
|
const { initialIndex, playType, songs } = args;
|
||||||
const { shuffledIndex } = get().current;
|
const { shuffledIndex } = get().current;
|
||||||
const shuffledQueue = get().queue.shuffled;
|
const shuffledQueue = get().queue.shuffled;
|
||||||
const queueSongs = map(songs, (song) => ({
|
const songsToAddToQueue = map(songs, (song) => ({
|
||||||
...song,
|
...song,
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
}));
|
}));
|
||||||
|
@ -109,8 +109,8 @@ export const usePlayerStore = create<PlayerSlice>()(
|
||||||
if (playType === Play.NOW) {
|
if (playType === Play.NOW) {
|
||||||
if (get().shuffle === PlayerShuffle.TRACK) {
|
if (get().shuffle === PlayerShuffle.TRACK) {
|
||||||
const index = initialIndex || 0;
|
const index = initialIndex || 0;
|
||||||
const initialSong = queueSongs[index];
|
const initialSong = songsToAddToQueue[index];
|
||||||
const queueCopy = [...queueSongs];
|
const queueCopy = [...songsToAddToQueue];
|
||||||
|
|
||||||
// Splice the initial song from the queue
|
// Splice the initial song from the queue
|
||||||
queueCopy.splice(index, 1);
|
queueCopy.splice(index, 1);
|
||||||
|
@ -127,7 +127,7 @@ export const usePlayerStore = create<PlayerSlice>()(
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.queue.shuffled = shuffledSongIndices;
|
state.queue.shuffled = shuffledSongIndices;
|
||||||
state.queue.default = queueSongs;
|
state.queue.default = songsToAddToQueue;
|
||||||
state.current.time = 0;
|
state.current.time = 0;
|
||||||
state.current.player = 1;
|
state.current.player = 1;
|
||||||
state.current.index = 0;
|
state.current.index = 0;
|
||||||
|
@ -137,12 +137,12 @@ export const usePlayerStore = create<PlayerSlice>()(
|
||||||
} else {
|
} else {
|
||||||
const index = initialIndex || 0;
|
const index = initialIndex || 0;
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.queue.default = queueSongs;
|
state.queue.default = songsToAddToQueue;
|
||||||
state.current.time = 0;
|
state.current.time = 0;
|
||||||
state.current.player = 1;
|
state.current.player = 1;
|
||||||
state.current.index = index;
|
state.current.index = index;
|
||||||
state.current.shuffledIndex = 0;
|
state.current.shuffledIndex = 0;
|
||||||
state.current.song = queueSongs[index];
|
state.current.song = songsToAddToQueue[index];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (playType === Play.LAST) {
|
} else if (playType === Play.LAST) {
|
||||||
|
@ -152,40 +152,49 @@ export const usePlayerStore = create<PlayerSlice>()(
|
||||||
? [
|
? [
|
||||||
...shuffledQueue.slice(0, shuffledIndex + 1),
|
...shuffledQueue.slice(0, shuffledIndex + 1),
|
||||||
...shuffle([
|
...shuffle([
|
||||||
...queueSongs.map((song) => song.uniqueId),
|
...songsToAddToQueue.map((song) => song.uniqueId),
|
||||||
...shuffledQueue.slice(shuffledIndex + 1),
|
...shuffledQueue.slice(shuffledIndex + 1),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.queue.default = [...get().queue.default, ...queueSongs];
|
state.queue.default = [...get().queue.default, ...songsToAddToQueue];
|
||||||
state.queue.shuffled = shuffledQueueWithNewSongs;
|
state.queue.shuffled = shuffledQueueWithNewSongs;
|
||||||
});
|
});
|
||||||
} else if (playType === Play.NEXT) {
|
} else if (playType === Play.NEXT) {
|
||||||
const queue = get().queue.default;
|
const queue = get().queue.default;
|
||||||
const currentIndex = get().current.index;
|
const currentIndex = get().current.index;
|
||||||
|
|
||||||
// Shuffle the queue after the current track
|
if (get().shuffle === PlayerShuffle.TRACK) {
|
||||||
const shuffledQueueWithNewSongs =
|
const shuffledIndex = get().current.shuffledIndex;
|
||||||
get().shuffle === PlayerShuffle.TRACK
|
const shuffledQueue = get().queue.shuffled;
|
||||||
? [
|
|
||||||
...shuffledQueue.slice(0, shuffledIndex + 1),
|
|
||||||
...shuffle([
|
|
||||||
...queueSongs.map((song) => song.uniqueId),
|
|
||||||
...shuffledQueue.slice(shuffledIndex + 1),
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
: [];
|
|
||||||
|
|
||||||
set((state) => {
|
// Shuffle the queue after the current track
|
||||||
state.queue.default = [
|
const shuffledQueueWithNewSongs = [
|
||||||
...queue.slice(0, currentIndex + 1),
|
...shuffledQueue.slice(0, shuffledIndex + 1),
|
||||||
...queueSongs,
|
...shuffle(songsToAddToQueue.map((song) => song.uniqueId)),
|
||||||
...queue.slice(currentIndex + 1),
|
...shuffledQueue.slice(shuffledIndex + 1),
|
||||||
];
|
];
|
||||||
state.queue.shuffled = shuffledQueueWithNewSongs;
|
|
||||||
});
|
set((state) => {
|
||||||
|
state.queue.default = [
|
||||||
|
...queue.slice(0, currentIndex + 1),
|
||||||
|
...songsToAddToQueue,
|
||||||
|
...queue.slice(currentIndex + 1),
|
||||||
|
];
|
||||||
|
state.queue.shuffled = shuffledQueueWithNewSongs;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
set((state) => {
|
||||||
|
state.queue.default = [
|
||||||
|
...queue.slice(0, currentIndex + 1),
|
||||||
|
...songsToAddToQueue,
|
||||||
|
...queue.slice(currentIndex + 1),
|
||||||
|
];
|
||||||
|
state.queue.shuffled = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return get().actions.getPlayerData();
|
return get().actions.getPlayerData();
|
||||||
|
@ -223,6 +232,10 @@ export const usePlayerStore = create<PlayerSlice>()(
|
||||||
state.current.player = state.current.player === 1 ? 2 : 1;
|
state.current.player = state.current.player === 1 ? 2 : 1;
|
||||||
state.current.song = nextSong!;
|
state.current.song = nextSong!;
|
||||||
state.queue.previousNode = get().current.song;
|
state.queue.previousNode = get().current.song;
|
||||||
|
|
||||||
|
if (isLastTrack) {
|
||||||
|
state.queue.shuffled = shuffle(get().queue.shuffled);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const nextIndex = isLastTrack ? 0 : get().current.index + 1;
|
const nextIndex = isLastTrack ? 0 : get().current.index + 1;
|
||||||
|
@ -246,13 +259,17 @@ export const usePlayerStore = create<PlayerSlice>()(
|
||||||
|
|
||||||
return currentIndex === 0;
|
return currentIndex === 0;
|
||||||
},
|
},
|
||||||
checkIsLastTrack: () => {
|
checkIsLastTrack: (type) => {
|
||||||
const currentIndex =
|
const isShuffled = get().shuffle === PlayerShuffle.TRACK;
|
||||||
get().shuffle === PlayerShuffle.TRACK
|
const queueLength = get().queue.default.length - 1;
|
||||||
? get().current.shuffledIndex
|
const modifier = type === 'next' ? 1 : type === 'prev' ? -1 : 0;
|
||||||
: get().current.index;
|
|
||||||
|
|
||||||
return currentIndex === get().queue.default.length - 1;
|
if (isShuffled) {
|
||||||
|
const currentIndex = get().current.shuffledIndex + modifier;
|
||||||
|
return currentIndex === queueLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return get().current.index + modifier === queueLength;
|
||||||
},
|
},
|
||||||
clearQueue: () => {
|
clearQueue: () => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
|
@ -496,15 +513,18 @@ export const usePlayerStore = create<PlayerSlice>()(
|
||||||
state.current.song = nextSong!;
|
state.current.song = nextSong!;
|
||||||
state.queue.previousNode = get().current.song;
|
state.queue.previousNode = get().current.song;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isLastTrack) {
|
||||||
|
get().actions.setShuffle(PlayerShuffle.TRACK);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const nextIndex =
|
let nextIndex = 0;
|
||||||
repeat === PlayerRepeat.ALL
|
|
||||||
? isLastTrack
|
if (repeat === PlayerRepeat.ALL) {
|
||||||
? 0
|
nextIndex = isLastTrack ? 0 : get().current.index + 1;
|
||||||
: get().current.index + 1
|
} else {
|
||||||
: isLastTrack
|
nextIndex = isLastTrack ? get().current.index : get().current.index + 1;
|
||||||
? get().current.index
|
}
|
||||||
: get().current.index + 1;
|
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.current.time = 0;
|
state.current.time = 0;
|
||||||
|
@ -579,11 +599,22 @@ export const usePlayerStore = create<PlayerSlice>()(
|
||||||
},
|
},
|
||||||
removeFromQueue: (uniqueIds) => {
|
removeFromQueue: (uniqueIds) => {
|
||||||
const queue = get().queue.default;
|
const queue = get().queue.default;
|
||||||
|
const currentSong = get().current.song;
|
||||||
|
|
||||||
const newQueue = queue.filter((song) => !uniqueIds.includes(song.uniqueId));
|
const newQueue = queue.filter((song) => !uniqueIds.includes(song.uniqueId));
|
||||||
|
const newShuffledQueue = get().queue.shuffled.filter(
|
||||||
|
(uniqueId) => !uniqueIds.includes(uniqueId),
|
||||||
|
);
|
||||||
|
|
||||||
|
const isCurrentSongRemoved = currentSong && uniqueIds.includes(currentSong?.uniqueId);
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.queue.default = newQueue;
|
state.queue.default = newQueue;
|
||||||
|
state.queue.shuffled = newShuffledQueue;
|
||||||
|
if (isCurrentSongRemoved) {
|
||||||
|
state.current.song = newQueue[0];
|
||||||
|
state.current.index = 0;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return get().actions.getPlayerData();
|
return get().actions.getPlayerData();
|
||||||
|
|
Reference in a new issue