|
|
@ -1,4 +1,5 @@ |
|
|
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; |
|
|
|
import settings from 'electron-settings'; |
|
|
|
import _ from 'lodash'; |
|
|
|
import arrayMove from 'array-move'; |
|
|
|
import { areConsecutive, consecutiveRanges } from '../shared/utils'; |
|
|
@ -50,6 +51,7 @@ export interface PlayQueue { |
|
|
|
shuffle: boolean; |
|
|
|
displayQueue: boolean; |
|
|
|
entry: Entry[]; |
|
|
|
shuffledEntry: Entry[]; |
|
|
|
} |
|
|
|
|
|
|
|
const initialState: PlayQueue = { |
|
|
@ -68,10 +70,11 @@ const initialState: PlayQueue = { |
|
|
|
autoIncremented: false, |
|
|
|
volume: 0.5, |
|
|
|
isLoading: false, |
|
|
|
repeat: 'none', |
|
|
|
shuffle: false, |
|
|
|
repeat: String(settings.getSync('defaultRepeat') || 'all'), |
|
|
|
shuffle: Boolean(settings.getSync('defaultShuffle') || false), |
|
|
|
displayQueue: false, |
|
|
|
entry: [], |
|
|
|
shuffledEntry: [], |
|
|
|
}; |
|
|
|
|
|
|
|
const resetPlayerDefaults = (state: PlayQueue) => { |
|
|
@ -93,31 +96,105 @@ const removeItem = (array: any, index: any) => { |
|
|
|
return [...array.slice(0, index), ...array.slice(index + 1)]; |
|
|
|
}; |
|
|
|
|
|
|
|
const entrySelect = (state: PlayQueue) => |
|
|
|
state.shuffle ? 'shuffledEntry' : 'entry'; |
|
|
|
|
|
|
|
const playQueueSlice = createSlice({ |
|
|
|
name: 'nowPlaying', |
|
|
|
initialState, |
|
|
|
reducers: { |
|
|
|
// ! Reducers need major refactoring and cleanup due to rapid
|
|
|
|
// ! development/experimentation to get things working
|
|
|
|
|
|
|
|
resetPlayQueue: (state) => { |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
resetPlayerDefaults(state); |
|
|
|
state.currentSongId = state.entry[0].id; |
|
|
|
state.currentSongId = state[currentEntry][0].id; |
|
|
|
}, |
|
|
|
|
|
|
|
shufflePlayQueue: (state) => { |
|
|
|
// Remove the current song before shuffling the array
|
|
|
|
// We do this so that the currently playing song doesn't
|
|
|
|
// suddenly switch because the index has changed
|
|
|
|
const shuffledEntriesWithoutCurrent = _.shuffle( |
|
|
|
removeItem(state.entry, state.currentIndex) |
|
|
|
); |
|
|
|
shuffleInPlace: (state) => { |
|
|
|
if (state.shuffledEntry.length > 0) { |
|
|
|
state.shuffle = true; |
|
|
|
|
|
|
|
// Readd the current song back into its original index
|
|
|
|
const shuffledEntries = insertItem( |
|
|
|
shuffledEntriesWithoutCurrent, |
|
|
|
state.currentIndex, |
|
|
|
state.entry[state.currentIndex] |
|
|
|
); |
|
|
|
const shuffledEntriesWithoutCurrent = _.shuffle( |
|
|
|
removeItem(state.shuffledEntry, state.currentIndex) |
|
|
|
); |
|
|
|
|
|
|
|
// Readd the current song back into its original index
|
|
|
|
const shuffledEntries = insertItem( |
|
|
|
shuffledEntriesWithoutCurrent, |
|
|
|
state.currentIndex, |
|
|
|
state.shuffledEntry[state.currentIndex] |
|
|
|
); |
|
|
|
|
|
|
|
state.entry = shuffledEntries; |
|
|
|
state.shuffledEntry = shuffledEntries; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
shufflePlayQueue: (state) => { |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
if (state.shuffledEntry.length < 1) { |
|
|
|
// Remove the current song before shuffling the array
|
|
|
|
// We do this so that the currently playing song doesn't
|
|
|
|
// suddenly switch because the index has changed
|
|
|
|
const shuffledEntriesWithoutCurrent = _.shuffle( |
|
|
|
removeItem(state.entry, state.currentIndex) |
|
|
|
); |
|
|
|
|
|
|
|
// Readd the current song back into its original index
|
|
|
|
const shuffledEntries = insertItem( |
|
|
|
shuffledEntriesWithoutCurrent, |
|
|
|
state.currentIndex, |
|
|
|
state.entry[state.currentIndex] |
|
|
|
); |
|
|
|
|
|
|
|
state.shuffledEntry = shuffledEntries; |
|
|
|
state.currentSongId = shuffledEntries[0].id; |
|
|
|
} else { |
|
|
|
const findIndex = state.entry.findIndex( |
|
|
|
(track) => track.id === state.currentSongId |
|
|
|
); |
|
|
|
|
|
|
|
if (state[currentEntry].length >= 1 && state.repeat !== 'one') { |
|
|
|
if (state.currentIndex < state[currentEntry].length - 1) { |
|
|
|
state.currentIndex = findIndex; |
|
|
|
state.player1.index = findIndex; |
|
|
|
state.currentPlayer = 1; |
|
|
|
state.isFading = false; |
|
|
|
state.player1.volume = state.volume; |
|
|
|
state.player1.index = state.currentIndex; |
|
|
|
if (state.currentIndex + 1 >= state[currentEntry].length) { |
|
|
|
state.player2.index = 0; |
|
|
|
} else { |
|
|
|
state.player2.index = state.currentIndex + 1; |
|
|
|
} |
|
|
|
} else if (state.repeat === 'all') { |
|
|
|
state.currentIndex = findIndex; |
|
|
|
state.currentPlayer = 1; |
|
|
|
state.isFading = false; |
|
|
|
state.player1.volume = state.volume; |
|
|
|
state.player1.index = state.currentIndex; |
|
|
|
if (state.currentIndex + 1 >= state[currentEntry].length) { |
|
|
|
state.player2.index = 0; |
|
|
|
} else { |
|
|
|
state.player2.index = state.currentIndex + 1; |
|
|
|
} |
|
|
|
} else if ( |
|
|
|
state[currentEntry].length >= 1 && |
|
|
|
state.repeat === 'one' |
|
|
|
) { |
|
|
|
state.currentIndex = findIndex; |
|
|
|
state.currentPlayer = 1; |
|
|
|
state.isFading = false; |
|
|
|
state.player1.volume = state.volume; |
|
|
|
state.player1.index = state.currentIndex; |
|
|
|
state.player2.index = state.currentIndex; |
|
|
|
} |
|
|
|
} |
|
|
|
state.currentSongId = state.entry[findIndex].id; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
setAutoIncremented: (state, action: PayloadAction<boolean>) => { |
|
|
@ -125,17 +202,21 @@ const playQueueSlice = createSlice({ |
|
|
|
}, |
|
|
|
|
|
|
|
setStar: (state, action: PayloadAction<{ id: string; type: string }>) => { |
|
|
|
const findIndex = state.entry.findIndex( |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
const findIndex = state[currentEntry].findIndex( |
|
|
|
(track) => track.id === action.payload.id |
|
|
|
); |
|
|
|
if (action.payload.type === 'unstar') { |
|
|
|
state.entry[findIndex].starred = undefined; |
|
|
|
state[currentEntry][findIndex].starred = undefined; |
|
|
|
} else { |
|
|
|
state.entry[findIndex].starred = String(Date.now()); |
|
|
|
state[currentEntry][findIndex].starred = String(Date.now()); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
toggleRepeat: (state) => { |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
if (state.repeat === 'none') { |
|
|
|
state.repeat = 'all'; |
|
|
|
} else if (state.repeat === 'all') { |
|
|
@ -154,11 +235,11 @@ const playQueueSlice = createSlice({ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (state.player1.index > state.entry.length - 1) { |
|
|
|
if (state.player1.index > state[currentEntry].length - 1) { |
|
|
|
state.player1.index = 0; |
|
|
|
} |
|
|
|
|
|
|
|
if (state.player2.index > state.entry.length - 1) { |
|
|
|
if (state.player2.index > state[currentEntry].length - 1) { |
|
|
|
state.player2.index = 0; |
|
|
|
} |
|
|
|
}, |
|
|
@ -184,8 +265,9 @@ const playQueueSlice = createSlice({ |
|
|
|
}, |
|
|
|
|
|
|
|
incrementCurrentIndex: (state, action: PayloadAction<string>) => { |
|
|
|
if (state.entry.length >= 1 && state.repeat !== 'one') { |
|
|
|
if (state.currentIndex < state.entry.length - 1) { |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
if (state[currentEntry].length >= 1 && state.repeat !== 'one') { |
|
|
|
if (state.currentIndex < state[currentEntry].length - 1) { |
|
|
|
// Check that current index isn't on the last track of the queue
|
|
|
|
state.currentIndex += 1; |
|
|
|
if (action.payload === 'usingHotkey') { |
|
|
@ -193,7 +275,7 @@ const playQueueSlice = createSlice({ |
|
|
|
state.isFading = false; |
|
|
|
state.player1.volume = state.volume; |
|
|
|
state.player1.index = state.currentIndex; |
|
|
|
if (state.currentIndex + 1 >= state.entry.length) { |
|
|
|
if (state.currentIndex + 1 >= state[currentEntry].length) { |
|
|
|
state.player2.index = 0; |
|
|
|
} else { |
|
|
|
state.player2.index = state.currentIndex + 1; |
|
|
@ -207,15 +289,15 @@ const playQueueSlice = createSlice({ |
|
|
|
state.isFading = false; |
|
|
|
state.player1.volume = state.volume; |
|
|
|
state.player1.index = state.currentIndex; |
|
|
|
if (state.currentIndex + 1 >= state.entry.length) { |
|
|
|
if (state.currentIndex + 1 >= state[currentEntry].length) { |
|
|
|
state.player2.index = 0; |
|
|
|
} else { |
|
|
|
state.player2.index = state.currentIndex + 1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
state.currentSongId = state.entry[state.currentIndex].id; |
|
|
|
} else if (state.entry.length >= 1 && state.repeat === 'one') { |
|
|
|
state.currentSongId = state[currentEntry][state.currentIndex].id; |
|
|
|
} else if (state[currentEntry].length >= 1 && state.repeat === 'one') { |
|
|
|
// If repeating one, then we can just increment to the next track
|
|
|
|
state.currentIndex += 1; |
|
|
|
if (action.payload === 'usingHotkey') { |
|
|
@ -225,23 +307,25 @@ const playQueueSlice = createSlice({ |
|
|
|
state.player1.index = state.currentIndex; |
|
|
|
state.player2.index = state.currentIndex; |
|
|
|
} |
|
|
|
state.currentSongId = state.entry[state.currentIndex].id; |
|
|
|
state.currentSongId = state[currentEntry][state.currentIndex].id; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
incrementPlayerIndex: (state, action: PayloadAction<number>) => { |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
// If the entry list is greater than two, we don't need to increment,
|
|
|
|
// just keep swapping playback between the tracks [0 <=> 0] or [0 <=> 1]
|
|
|
|
// without changing the index of either player
|
|
|
|
if (state.entry.length > 2 && state.repeat !== 'one') { |
|
|
|
if (state[currentEntry].length > 2 && state.repeat !== 'one') { |
|
|
|
if (action.payload === 1) { |
|
|
|
if ( |
|
|
|
state.player1.index + 1 === state.entry.length && |
|
|
|
state.player1.index + 1 === state[currentEntry].length && |
|
|
|
state.repeat === 'none' |
|
|
|
) { |
|
|
|
// Reset the player on the end of the playlist if no repeat
|
|
|
|
resetPlayerDefaults(state); |
|
|
|
} else if (state.player1.index + 2 >= state.entry.length) { |
|
|
|
} else if (state.player1.index + 2 >= state[currentEntry].length) { |
|
|
|
// If incrementing would be greater than the total number of entries,
|
|
|
|
// reset it back to 0. Also check if player1 is already set to 0.
|
|
|
|
if (state.player2.index === 0) { |
|
|
@ -255,12 +339,12 @@ const playQueueSlice = createSlice({ |
|
|
|
state.currentPlayer = 2; |
|
|
|
} else { |
|
|
|
if ( |
|
|
|
state.player2.index + 1 === state.entry.length && |
|
|
|
state.player2.index + 1 === state[currentEntry].length && |
|
|
|
state.repeat === 'none' |
|
|
|
) { |
|
|
|
// Reset the player on the end of the playlist if no repeat
|
|
|
|
resetPlayerDefaults(state); |
|
|
|
} else if (state.player2.index + 2 >= state.entry.length) { |
|
|
|
} else if (state.player2.index + 2 >= state[currentEntry].length) { |
|
|
|
// If incrementing would be greater than the total number of entries,
|
|
|
|
// reset it back to 0. Also check if player1 is already set to 0.
|
|
|
|
if (state.player1.index === 0) { |
|
|
@ -277,7 +361,9 @@ const playQueueSlice = createSlice({ |
|
|
|
}, |
|
|
|
|
|
|
|
setPlayerIndex: (state, action: PayloadAction<Entry>) => { |
|
|
|
const findIndex = state.entry.findIndex( |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
const findIndex = state[currentEntry].findIndex( |
|
|
|
(track) => track.id === action.payload.id |
|
|
|
); |
|
|
|
|
|
|
@ -307,7 +393,9 @@ const playQueueSlice = createSlice({ |
|
|
|
}, |
|
|
|
|
|
|
|
decrementCurrentIndex: (state, action: PayloadAction<string>) => { |
|
|
|
if (state.entry.length >= 1) { |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
if (state[currentEntry].length >= 1) { |
|
|
|
if (state.currentIndex > 0) { |
|
|
|
state.currentIndex -= 1; |
|
|
|
if (action.payload === 'usingHotkey') { |
|
|
@ -325,13 +413,19 @@ const playQueueSlice = createSlice({ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
state.currentSongId = state.entry[state.currentIndex].id; |
|
|
|
state.currentSongId = state[currentEntry][state.currentIndex].id; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
fixPlayer2Index: (state) => { |
|
|
|
if (state.entry.length >= 2 && state.repeat !== 'one') { |
|
|
|
state.player2.index = state.currentIndex + 1; |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
if (state[currentEntry].length >= 2 && state.repeat !== 'one') { |
|
|
|
if (state.currentIndex + 1 === state[currentEntry].length) { |
|
|
|
state.player2.index = 0; |
|
|
|
} else { |
|
|
|
state.player2.index = state.currentIndex + 1; |
|
|
|
} |
|
|
|
} else if (state.repeat === 'one') { |
|
|
|
state.player2.index = state.currentIndex; |
|
|
|
} else { |
|
|
@ -340,7 +434,9 @@ const playQueueSlice = createSlice({ |
|
|
|
}, |
|
|
|
|
|
|
|
setCurrentIndex: (state, action: PayloadAction<Entry>) => { |
|
|
|
const findIndex = state.entry.findIndex( |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
const findIndex = state[currentEntry].findIndex( |
|
|
|
(track) => track.id === action.payload.id |
|
|
|
); |
|
|
|
|
|
|
@ -351,18 +447,30 @@ const playQueueSlice = createSlice({ |
|
|
|
setPlayQueue: (state, action: PayloadAction<Entry[]>) => { |
|
|
|
// Reset player defaults
|
|
|
|
state.entry = []; |
|
|
|
state.shuffledEntry = []; |
|
|
|
resetPlayerDefaults(state); |
|
|
|
|
|
|
|
state.currentSongId = action.payload[0].id; |
|
|
|
action.payload.map((entry: any) => state.entry.push(entry)); |
|
|
|
if (state.shuffle) { |
|
|
|
const shuffledEntries = _.shuffle(action.payload); |
|
|
|
shuffledEntries.map((entry: any) => state.shuffledEntry.push(entry)); |
|
|
|
state.currentSongId = shuffledEntries[0].id; |
|
|
|
} else { |
|
|
|
state.currentSongId = action.payload[0].id; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
appendPlayQueue: (state, action: PayloadAction<Entry[]>) => { |
|
|
|
action.payload.map((entry: any) => state.entry.push(entry)); |
|
|
|
if (state.shuffle) { |
|
|
|
const shuffledEntries = _.shuffle(action.payload); |
|
|
|
shuffledEntries.map((entry: any) => state.shuffledEntry.push(entry)); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
clearPlayQueue: (state) => { |
|
|
|
state.entry = []; |
|
|
|
state.shuffledEntry = []; |
|
|
|
resetPlayerDefaults(state); |
|
|
|
}, |
|
|
|
|
|
|
@ -379,8 +487,10 @@ const playQueueSlice = createSlice({ |
|
|
|
}, |
|
|
|
|
|
|
|
moveUp: (state, action: PayloadAction<number[]>) => { |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
// Create a copy of the queue so we can mutate it in place with arrayMove.mutate
|
|
|
|
const tempQueue = state.entry.slice(); |
|
|
|
const tempQueue = state[currentEntry].slice(); |
|
|
|
|
|
|
|
// Ascending index is needed to move the indexes in order
|
|
|
|
const selectedIndexesAsc = action.payload.sort((a, b) => a - b); |
|
|
@ -406,12 +516,14 @@ const playQueueSlice = createSlice({ |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
state.entry = tempQueue; |
|
|
|
state[currentEntry] = tempQueue; |
|
|
|
}, |
|
|
|
|
|
|
|
moveDown: (state, action: PayloadAction<number[]>) => { |
|
|
|
const currentEntry = entrySelect(state); |
|
|
|
|
|
|
|
// Create a copy of the queue so we can mutate it in place with arrayMove.mutate
|
|
|
|
const tempQueue = state.entry.slice(); |
|
|
|
const tempQueue = state[currentEntry].slice(); |
|
|
|
|
|
|
|
// Descending index is needed to move the indexes in order
|
|
|
|
const cr = consecutiveRanges(action.payload.sort((a, b) => a - b)); |
|
|
@ -437,7 +549,7 @@ const playQueueSlice = createSlice({ |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
state.entry = tempQueue; |
|
|
|
state[currentEntry] = tempQueue; |
|
|
|
}, |
|
|
|
}, |
|
|
|
}); |
|
|
@ -466,5 +578,6 @@ export const { |
|
|
|
resetPlayQueue, |
|
|
|
setStar, |
|
|
|
shufflePlayQueue, |
|
|
|
shuffleInPlace, |
|
|
|
} = playQueueSlice.actions; |
|
|
|
export default playQueueSlice.reducer; |
|
|
|