initial state: muzick music player + recommendation engine
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import { create } from 'zustand';
|
||||
import type { Track } from '../types';
|
||||
|
||||
export type RepeatMode = 'none' | 'all' | 'one';
|
||||
|
||||
interface PlaybackState {
|
||||
currentTrack: Track | null;
|
||||
queue: Track[];
|
||||
isPlaying: boolean;
|
||||
position: number;
|
||||
duration: number;
|
||||
volume: number;
|
||||
shuffle: boolean;
|
||||
repeat: RepeatMode;
|
||||
|
||||
setQueue: (queue: Track[]) => void;
|
||||
playTrack: (track: Track) => void;
|
||||
play: () => void;
|
||||
pause: () => void;
|
||||
next: () => void;
|
||||
prev: () => void;
|
||||
setPosition: (position: number) => void;
|
||||
setDuration: (duration: number) => void;
|
||||
setVolume: (volume: number) => void;
|
||||
setCurrentTrack: (track: Track | null) => void;
|
||||
toggleShuffle: () => void;
|
||||
cycleRepeat: () => void;
|
||||
}
|
||||
|
||||
export const usePlaybackStore = create<PlaybackState>((set, get) => ({
|
||||
currentTrack: null,
|
||||
queue: [],
|
||||
isPlaying: false,
|
||||
position: 0,
|
||||
duration: 0,
|
||||
volume: 1,
|
||||
shuffle: false,
|
||||
repeat: 'none',
|
||||
|
||||
setQueue: (queue) => set({ queue }),
|
||||
|
||||
playTrack: (track) =>
|
||||
set({
|
||||
currentTrack: track,
|
||||
isPlaying: true,
|
||||
position: 0,
|
||||
duration: track.duration ?? 0,
|
||||
}),
|
||||
|
||||
play: () => set({ isPlaying: true }),
|
||||
pause: () => set({ isPlaying: false }),
|
||||
|
||||
next: () => {
|
||||
const { queue, currentTrack, shuffle, repeat } = get();
|
||||
if (queue.length === 0) return;
|
||||
|
||||
const idx = currentTrack ? queue.findIndex((t) => t.id === currentTrack.id) : -1;
|
||||
|
||||
// Repeat one: replay current track
|
||||
if (repeat === 'one' && currentTrack) {
|
||||
set({ position: 0, isPlaying: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (shuffle) {
|
||||
// Shuffle: pick a random track from the remaining queue (excluding current)
|
||||
const remaining = queue.filter((t) => t.id !== currentTrack?.id);
|
||||
if (remaining.length === 0) {
|
||||
if (repeat === 'all') {
|
||||
const pick = queue[Math.floor(Math.random() * queue.length)];
|
||||
set({ currentTrack: pick, position: 0, duration: pick.duration ?? 0, isPlaying: true });
|
||||
}
|
||||
return;
|
||||
}
|
||||
const pick = remaining[Math.floor(Math.random() * remaining.length)];
|
||||
set({ currentTrack: pick, position: 0, duration: pick.duration ?? 0, isPlaying: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// Sequential
|
||||
const nextTrack = idx >= 0 ? queue[idx + 1] : null;
|
||||
if (nextTrack) {
|
||||
// Trim played tracks from queue to prevent unbounded growth (Vibe prefetch leak)
|
||||
const trimmed = queue.slice(idx + 1);
|
||||
set({ queue: trimmed, currentTrack: nextTrack, position: 0, duration: nextTrack.duration ?? 0, isPlaying: true });
|
||||
} else if (repeat === 'all') {
|
||||
const first = queue[0];
|
||||
if (first) {
|
||||
set({ currentTrack: first, position: 0, duration: first.duration ?? 0, isPlaying: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
prev: () => {
|
||||
const { queue, currentTrack } = get();
|
||||
if (queue.length === 0) return;
|
||||
const idx = currentTrack ? queue.findIndex((t) => t.id === currentTrack.id) : -1;
|
||||
const prevTrack = idx > 0 ? queue[idx - 1] : null;
|
||||
if (prevTrack) {
|
||||
set({ currentTrack: prevTrack, position: 0, duration: prevTrack.duration ?? 0, isPlaying: true });
|
||||
}
|
||||
},
|
||||
|
||||
setPosition: (position) => set({ position }),
|
||||
setDuration: (duration) => set({ duration }),
|
||||
setVolume: (volume) => set({ volume }),
|
||||
setCurrentTrack: (currentTrack) => set({ currentTrack }),
|
||||
|
||||
toggleShuffle: () => set((state) => ({ shuffle: !state.shuffle })),
|
||||
cycleRepeat: () =>
|
||||
set((state) => {
|
||||
const modes: RepeatMode[] = ['none', 'all', 'one'];
|
||||
const next = modes[(modes.indexOf(state.repeat) + 1) % modes.length];
|
||||
return { repeat: next };
|
||||
}),
|
||||
}));
|
||||
Reference in New Issue
Block a user