Handle web player

This commit is contained in:
jeffvli
2022-11-08 00:20:39 -08:00
parent e774cdf031
commit 587fa2422a
17 changed files with 835 additions and 276 deletions
+182 -201
View File
@@ -6,15 +6,7 @@ import { nanoid } from 'nanoid/non-secure';
import create from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { Song } from '@/renderer/api/types';
import {
Play,
CrossfadeStyle,
PlaybackStyle,
PlaybackType,
PlayerRepeat,
PlayerStatus,
UniqueId,
} from '@/renderer/types';
import { Play, PlayerStatus, UniqueId } from '@/renderer/types';
type QueueSong = Song & UniqueId;
@@ -26,22 +18,14 @@ export interface PlayerState {
status: PlayerStatus;
time: number;
};
muted: boolean;
queue: {
default: QueueSong[];
previousNode: QueueSong;
shuffled: QueueSong[];
sorted: QueueSong[];
};
settings: {
crossfadeDuration: number;
crossfadeStyle: CrossfadeStyle;
muted: boolean;
repeat: PlayerRepeat;
shuffle: boolean;
style: PlaybackStyle;
type: PlaybackType;
volume: number;
};
volume: number;
}
export interface PlayerData {
@@ -75,206 +59,203 @@ export interface PlayerSlice extends PlayerState {
prev: () => PlayerData;
setCurrentIndex: (index: number) => PlayerData;
setCurrentTime: (time: number) => void;
setSettings: (settings: Partial<PlayerState['settings']>) => void;
setMuted: (muted: boolean) => void;
setVolume: (volume: number) => void;
}
export const usePlayerStore = create<PlayerSlice>()(
persist(
devtools((set, get) => ({
addToQueue: (songs, type) => {
const queueSongs = map(songs, (song) => ({
...song,
uniqueId: nanoid(),
}));
devtools(
(set, get) => ({
addToQueue: (songs, type) => {
const queueSongs = map(songs, (song) => ({
...song,
uniqueId: nanoid(),
}));
if (type === Play.NOW) {
if (type === Play.NOW) {
set(
produce((state) => {
state.queue.default = queueSongs;
state.current.time = 0;
state.current.player = 1;
state.current.index = 0;
state.current.song = queueSongs[0];
})
);
} else if (type === Play.LAST) {
set(
produce((state) => {
state.queue.default = [...get().queue.default, ...queueSongs];
})
);
} else if (type === Play.NEXT) {
const queue = get().queue.default;
const currentIndex = get().current.index;
set(
produce((state) => {
state.queue.default = [
...queue.slice(0, currentIndex + 1),
...queueSongs,
...queue.slice(currentIndex + 1),
];
})
);
}
return get().getPlayerData();
},
autoNext: () => {
set(
produce((state) => {
state.queue.default = queueSongs;
state.current.time = 0;
state.current.player = 1;
state.current.index = 0;
state.current.song = queueSongs[0];
state.current.index += 1;
state.current.player = state.current.player === 1 ? 2 : 1;
state.current.song = state.queue.default[state.current.index];
state.queue.previousNode = get().current.song;
})
);
} else if (type === Play.LAST) {
set(
produce((state) => {
state.queue.default = [...get().queue.default, ...queueSongs];
})
);
} else if (type === Play.NEXT) {
return get().getPlayerData();
},
current: {
index: 0,
player: 1,
song: {} as QueueSong,
status: PlayerStatus.PAUSED,
time: 0,
},
getPlayerData: () => {
const queue = get().queue.default;
const currentIndex = get().current.index;
const currentPlayer = get().current.player;
set(
produce((state) => {
state.queue.default = [
...queue.slice(0, currentIndex + 1),
...queueSongs,
...queue.slice(currentIndex + 1),
];
})
);
}
const player1 =
currentPlayer === 1
? queue[get().current.index]
: queue[get().current.index + 1];
return get().getPlayerData();
},
autoNext: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index += 1;
state.current.player = state.current.player === 1 ? 2 : 1;
state.current.song = state.queue.default[state.current.index];
state.queue.previousNode = get().current.song;
})
);
const player2 =
currentPlayer === 1
? queue[get().current.index + 1]
: queue[get().current.index];
return get().getPlayerData();
},
current: {
index: 0,
player: 1,
song: {} as QueueSong,
status: PlayerStatus.PAUSED,
time: 0,
},
getPlayerData: () => {
const queue = get().queue.default;
const currentPlayer = get().current.player;
const player1 =
currentPlayer === 1
? queue[get().current.index]
: queue[get().current.index + 1];
const player2 =
currentPlayer === 1
? queue[get().current.index + 1]
: queue[get().current.index];
return {
current: {
index: get().current.index,
player: get().current.player,
song: get().current.song,
status: get().current.status,
},
player1,
player2,
queue: {
return {
current: {
index: get().current.index,
player: get().current.player,
song: get().current.song,
status: get().current.status,
},
player1,
player2,
queue: {
current: queue[get().current.index],
next: queue[get().current.index + 1],
previous: queue[get().current.index - 1],
},
};
},
getQueueData: () => {
const queue = get().queue.default;
return {
current: queue[get().current.index],
next: queue[get().current.index + 1],
previous: queue[get().current.index - 1],
},
};
},
getQueueData: () => {
const queue = get().queue.default;
return {
current: queue[get().current.index],
next: queue[get().current.index + 1],
previous: queue[get().current.index - 1],
};
},
next: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index += 1;
state.current.player = 1;
state.current.song = state.queue.default[state.current.index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
pause: () => {
set(
produce((state) => {
state.current.status = PlayerStatus.PAUSED;
})
);
},
play: () => {
set(
produce((state) => {
state.current.status = PlayerStatus.PLAYING;
})
);
},
player1: () => {
return get().getPlayerData().player1;
},
player2: () => {
return get().getPlayerData().player2;
},
prev: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index =
state.current.index - 1 < 0 ? 0 : state.current.index - 1;
state.current.player = 1;
state.current.song = state.queue.default[state.current.index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
queue: {
default: [],
previousNode: {} as QueueSong,
shuffled: [],
sorted: [],
},
setCurrentIndex: (index) => {
set(
produce((state) => {
state.current.time = 0;
state.current.index = index;
state.current.player = 1;
state.current.song = state.queue.default[index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
setCurrentTime: (time) => {
set(
produce((state) => {
state.current.time = time;
})
);
},
setSettings: (settings) => {
set(
produce((state) => {
state.settings = { ...get().settings, ...settings };
})
);
// try {
// setLocalStorageSettings('player', get().settings);
// } catch (err) {
// console.log('none');
// }
},
settings: {
crossfadeDuration: 5,
crossfadeStyle: CrossfadeStyle.EQUALPOWER,
};
},
muted: false,
repeat: PlayerRepeat.NONE,
shuffle: false,
style: PlaybackStyle.GAPLESS,
type: PlaybackType.LOCAL,
next: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index += 1;
state.current.player = 1;
state.current.song = state.queue.default[state.current.index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
pause: () => {
set(
produce((state) => {
state.current.status = PlayerStatus.PAUSED;
})
);
},
play: () => {
set(
produce((state) => {
state.current.status = PlayerStatus.PLAYING;
})
);
},
player1: () => {
return get().getPlayerData().player1;
},
player2: () => {
return get().getPlayerData().player2;
},
prev: () => {
set(
produce((state) => {
state.current.time = 0;
state.current.index =
state.current.index - 1 < 0 ? 0 : state.current.index - 1;
state.current.player = 1;
state.current.song = state.queue.default[state.current.index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
queue: {
default: [],
previousNode: {} as QueueSong,
shuffled: [],
sorted: [],
},
setCurrentIndex: (index) => {
set(
produce((state) => {
state.current.time = 0;
state.current.index = index;
state.current.player = 1;
state.current.song = state.queue.default[index];
state.queue.previousNode = get().current.song;
})
);
return get().getPlayerData();
},
setCurrentTime: (time) => {
set(
produce((state) => {
state.current.time = time;
})
);
},
setMuted: (muted: boolean) => {
set(
produce((state) => {
state.muted = muted;
})
);
},
setVolume: (volume: number) => {
set(
produce((state) => {
state.volume = volume;
})
);
},
volume: 50,
},
})),
{ name: 'player' }
}),
{ name: 'store_player' }
),
{ name: 'store_player' }
)
);
+86
View File
@@ -0,0 +1,86 @@
/* eslint-disable prefer-destructuring */
/* eslint-disable @typescript-eslint/no-unused-vars */
import merge from 'lodash/merge';
import create from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import {
CrossfadeStyle,
Play,
PlaybackStyle,
PlaybackType,
PlayerRepeat,
} from '@/renderer/types';
export interface SettingsState {
player: {
audioDeviceId?: string | null;
crossfadeDuration: number;
crossfadeStyle: CrossfadeStyle;
globalMediaHotkeys: boolean;
muted: boolean;
playButtonBehavior: Play;
repeat: PlayerRepeat;
scrobble: {
enabled: boolean;
scrobbleAtPercentage: number;
};
shuffle: boolean;
skipButtons: {
enabled: boolean;
skipBackwardSeconds: number;
skipForwardSeconds: number;
};
style: PlaybackStyle;
type: PlaybackType;
volume: number;
};
tab: 'general' | 'playback' | 'view' | string;
}
export interface SettingsSlice extends SettingsState {
setSettings: (data: Partial<SettingsState>) => void;
}
export const useSettingsStore = create<SettingsSlice>()(
persist(
devtools(
immer((set, get) => ({
player: {
audioDeviceId: undefined,
crossfadeDuration: 5,
crossfadeStyle: CrossfadeStyle.EQUALPOWER,
globalMediaHotkeys: true,
muted: false,
playButtonBehavior: Play.NOW,
repeat: PlayerRepeat.NONE,
scrobble: {
enabled: false,
scrobbleAtPercentage: 75,
},
shuffle: false,
skipButtons: {
enabled: true,
skipBackwardSeconds: 10,
skipForwardSeconds: 30,
},
style: PlaybackStyle.GAPLESS,
type: PlaybackType.LOCAL,
volume: 50,
},
setSettings: (data) => {
set({ ...get(), ...data });
},
tab: 'general',
})),
{ name: 'store_settings' }
),
{
merge: (persistedState, currentState) => {
return merge(persistedState, currentState);
},
name: 'store_settings',
version: 1,
}
)
);