Spaces:
Sleeping
Sleeping
| "use client" | |
| import { useCallback, useEffect, useRef } from "react"; | |
| import { create } from "zustand"; | |
| import { MediaInfo } from "@/types/general"; | |
| // Define the new track type with an optional playNow property | |
| interface PlaybackOptions<T> { | |
| url: string; | |
| meta: T; | |
| isLastTrackOfPlaylist?: boolean; | |
| playNow?: boolean; // New optional parameter | |
| } | |
| // Define the Zustand store | |
| interface PlaylistState<T> { | |
| playlist: PlaybackOptions<T>[]; | |
| audio: HTMLAudioElement | null; | |
| current: T | null; | |
| interval: NodeJS.Timer | null; | |
| progress: number; | |
| setProgress: (progress: number) => void; | |
| isPlaying: boolean; | |
| isSwitchingTracks: boolean; | |
| enqueue: (options: PlaybackOptions<T>) => void; | |
| dequeue: () => void; | |
| togglePause: () => void; | |
| } | |
| function getAudio(): HTMLAudioElement | null { | |
| try { | |
| return new Audio() | |
| } catch (err) { | |
| return null | |
| } | |
| } | |
| export const usePlaylistStore = create<PlaylistState<MediaInfo>>((set, get) => ({ | |
| playlist: [], | |
| audio: getAudio(), | |
| current: null, | |
| progress: 0, | |
| interval: null, | |
| setProgress: (progress) => set((state) => ({ | |
| progress: isNaN(progress) ? 0 : progress, | |
| })), | |
| isPlaying: false, | |
| isSwitchingTracks: false, | |
| enqueue: (options) => set((state) => ({ playlist: [...state.playlist, options] })), | |
| dequeue: () => set((state) => { | |
| const nextPlaying = state.playlist.length > 0 ? state.playlist[0] : null; | |
| return { | |
| current: nextPlaying ? nextPlaying.meta : null, | |
| playlist: state.playlist.slice(1), | |
| isSwitchingTracks: state.playlist.length > 1, | |
| }; | |
| }), | |
| togglePause: () => { | |
| const { audio, isPlaying } = get() | |
| // console.log("togglePause: " + isPlaying) | |
| if (!audio) { return } | |
| // console.log("doing the thing") | |
| if (isPlaying) { | |
| // console.log("we are playing! so setting to false..") | |
| set({ isPlaying: false }); | |
| audio.pause(); | |
| } else { | |
| // console.log("we are not playing! so setting to true..") | |
| set({ isPlaying: true }); | |
| try { | |
| audio.play() | |
| } catch (err) { | |
| console.error("Play failed:", err); | |
| set({ isPlaying: false }); | |
| } | |
| } | |
| } | |
| })); | |
| // The refactored useAudioPlayer hook | |
| export function usePlaylist() { | |
| const intervalRef = useRef<NodeJS.Timer>(); | |
| const { | |
| playlist, | |
| current, | |
| progress, | |
| isPlaying, | |
| isSwitchingTracks, | |
| enqueue, | |
| dequeue, | |
| audio, | |
| interval, | |
| setProgress, | |
| togglePause, | |
| } = usePlaylistStore(); | |
| const updateProgress = useCallback(() => { | |
| if (!audio) { return } | |
| // if (!isPlaying) { return } | |
| const currentProgress = audio.currentTime / audio.duration; | |
| // console.log("updateProgress: " + currentProgress) | |
| setProgress(currentProgress); | |
| if (currentProgress >= 1) { | |
| if (!audio.loop) { | |
| console.log("we reached the end!") | |
| dequeue(); | |
| } | |
| } | |
| }, [audio, audio?.currentTime, dequeue, setProgress, isPlaying]); | |
| const playback = useCallback(async (options?: PlaybackOptions<MediaInfo>): Promise<void> => { | |
| if (!audio) { return } | |
| if (!options) { | |
| clearInterval(intervalRef.current!); | |
| // console.log("playback called with nothing, so setting isPlaying to false") | |
| usePlaylistStore.setState({ | |
| playlist: [], | |
| current: null, | |
| isPlaying: false, | |
| isSwitchingTracks: false | |
| }); | |
| return | |
| } | |
| // console.log("playback!", options) | |
| if (options.playNow) { | |
| clearInterval(intervalRef.current!); | |
| usePlaylistStore.setState({ | |
| playlist: [options as any], // Clears the previous playlist and adds the new track | |
| current: options.meta, | |
| isPlaying: true, | |
| isSwitchingTracks: false | |
| }); | |
| try { | |
| audio.pause(); | |
| } catch (err) {} | |
| try { | |
| audio.src = options.url; | |
| audio.load(); | |
| } catch (err) {} | |
| try { | |
| await audio.play(); | |
| } catch (err) { | |
| } | |
| intervalRef.current = setInterval(updateProgress, 250); | |
| } else { | |
| enqueue(options as any); | |
| } | |
| }, [enqueue, updateProgress]); | |
| useEffect(() => { | |
| return () => { | |
| if (intervalRef.current) { | |
| clearInterval(intervalRef.current); | |
| intervalRef.current = undefined; | |
| usePlaylistStore.setState({ interval: null }); | |
| } | |
| if (audio) { | |
| audio.pause(); | |
| } | |
| }; | |
| }, [audio]); | |
| return { current, playlist, playback, progress, isPlaying, isSwitchingTracks, togglePause }; | |
| } |