import { createContext, useContext, useState, useEffect, type ReactNode } from 'react' import { useSocket } from './SocketContext' import { apiService } from '@/services/api' export interface Track { id: string title: string artist?: string duration: number thumbnail?: string url: string } export type PlayMode = 'continuous' | 'loop-playlist' | 'loop-one' | 'random' | 'single' interface MusicPlayerContextType { // Playback state isPlaying: boolean currentTime: number duration: number volume: number isMuted: boolean playMode: PlayMode // Current track and playlist currentTrack: Track | null playlist: Track[] currentTrackIndex: number // UI state isMinimized: boolean isUltraMinimized: boolean showPlaylist: boolean // Actions play: () => void pause: () => void stop: () => void togglePlayPause: () => void seekTo: (time: number) => void setVolume: (volume: number) => void toggleMute: () => void setPlayMode: (mode: PlayMode) => void nextTrack: () => void previousTrack: () => void playTrack: (trackIndex: number) => void loadPlaylist: (playlistId: number) => void addToPlaylist: (track: Track) => void removeFromPlaylist: (trackId: string) => void clearPlaylist: () => void toggleMaximize: () => void toggleUltraMinimize: () => void togglePlaylistVisibility: () => void } const MusicPlayerContext = createContext(undefined) export function useMusicPlayer() { const context = useContext(MusicPlayerContext) if (context === undefined) { throw new Error('useMusicPlayer must be used within a MusicPlayerProvider') } return context } interface MusicPlayerProviderProps { children: ReactNode } export function MusicPlayerProvider({ children }: MusicPlayerProviderProps) { const { socket } = useSocket() // Playback state const [isPlaying, setIsPlaying] = useState(false) const [currentTime, setCurrentTime] = useState(0) const [duration, setDuration] = useState(0) const [volume, setVolumeState] = useState(80) const [isMuted, setIsMuted] = useState(false) const [playMode, setPlayModeState] = useState('continuous') // Playlist state const [currentTrackIndex, setCurrentTrackIndex] = useState(0) const [playlist, setPlaylist] = useState([]) const [currentPlaylistId, setCurrentPlaylistId] = useState(null) const [currentTrack, setCurrentTrack] = useState(null) // UI state const [isMinimized, setIsMinimized] = useState(true) const [isUltraMinimized, setIsUltraMinimized] = useState(false) const [showPlaylist, setShowPlaylist] = useState(false) // Fetch initial player state on mount useEffect(() => { const fetchInitialState = async () => { try { const response = await apiService.get('/api/player/state') const state = await response.json() // Update all state from backend setIsPlaying(state.is_playing || false) setCurrentTime(state.current_time || 0) setDuration(state.duration || 0) setVolumeState(state.volume || 80) setIsMuted(state.volume === 0) setPlayModeState(state.play_mode || 'continuous') setCurrentTrackIndex(state.current_track_index || 0) setPlaylist(state.playlist || []) setCurrentPlaylistId(state.playlist_id || null) setCurrentTrack(state.current_track || null) } catch (error) { console.error('Failed to fetch initial player state:', error) } } fetchInitialState() }, []) // Listen for real-time player updates via SocketIO useEffect(() => { if (!socket) { return; } const handlePlayerStateUpdate = (state: any) => { setIsPlaying(state.is_playing || false) setCurrentTime(state.current_time || 0) setDuration(state.duration || 0) setVolumeState(state.volume || 80) setIsMuted(state.volume === 0) setPlayModeState(state.play_mode || 'continuous') setCurrentTrackIndex(state.current_track_index || 0) setPlaylist(state.playlist || []) setCurrentPlaylistId(state.playlist_id || null) setCurrentTrack(state.current_track || null) } socket.on('player_state_update', handlePlayerStateUpdate) return () => { socket.off('player_state_update', handlePlayerStateUpdate) } }, [socket]) const play = async () => { try { await apiService.post('/api/player/play') } catch (error) { console.error('Failed to play:', error) } } const pause = async () => { try { await apiService.post('/api/player/pause') } catch (error) { console.error('Failed to pause:', error) } } const stop = async () => { try { await apiService.post('/api/player/stop') } catch (error) { console.error('Failed to stop:', error) } } const togglePlayPause = async () => { if (isPlaying) { await pause() } else { await play() } } const seekTo = async (time: number) => { try { const position = duration > 0 ? time / duration : 0 await apiService.post('/api/player/seek', { position }) } catch (error) { console.error('Failed to seek:', error) } } const setVolume = async (newVolume: number) => { try { await apiService.post('/api/player/volume', { volume: newVolume }) } catch (error) { console.error('Failed to set volume:', error) } } const toggleMute = async () => { try { const newVolume = isMuted ? 80 : 0 await apiService.post('/api/player/volume', { volume: newVolume }) } catch (error) { console.error('Failed to toggle mute:', error) } } const nextTrack = async () => { try { await apiService.post('/api/player/next') } catch (error) { console.error('Failed to skip to next track:', error) } } const previousTrack = async () => { try { await apiService.post('/api/player/previous') } catch (error) { console.error('Failed to skip to previous track:', error) } } const playTrack = async (trackIndex: number) => { try { await apiService.post('/api/player/play-track', { index: trackIndex }) } catch (error) { console.error('Failed to play track:', error) } } const loadPlaylist = async (playlistId: number) => { try { await apiService.post('/api/player/playlist', { playlist_id: playlistId }) } catch (error) { console.error('Failed to load playlist:', error) } } const addToPlaylist = (track: Track) => { // This would need to be implemented via API console.log('Adding to playlist not yet implemented') } const removeFromPlaylist = (trackId: string) => { // This would need to be implemented via API console.log('Removing from playlist not yet implemented') } const clearPlaylist = () => { // This would need to be implemented via API console.log('Clearing playlist not yet implemented') } const setPlayMode = async (mode: PlayMode) => { try { await apiService.post('/api/player/mode', { mode }) } catch (error) { console.error('Failed to set play mode:', error) } } const toggleMaximize = () => { setIsMinimized(!isMinimized) setIsUltraMinimized(false) // When maximizing, exit ultra-minimize mode } const toggleUltraMinimize = () => { setIsUltraMinimized(!isUltraMinimized) if (!isUltraMinimized) { setIsMinimized(true) // When ultra-minimizing, ensure we're in minimized mode } } const togglePlaylistVisibility = () => { setShowPlaylist(!showPlaylist) } const value: MusicPlayerContextType = { // Playback state isPlaying, currentTime, duration, volume, isMuted, playMode, // Current track and playlist currentTrack, playlist, currentTrackIndex, // UI state isMinimized, isUltraMinimized, showPlaylist, // Actions play, pause, stop, togglePlayPause, seekTo, setVolume, toggleMute, setPlayMode, nextTrack, previousTrack, playTrack, loadPlaylist, addToPlaylist, removeFromPlaylist, clearPlaylist, toggleMaximize, toggleUltraMinimize, togglePlaylistVisibility, } return ( {children} ) }