feat: enhance MusicPlayer and SocketProvider with playlist management and real-time updates
This commit is contained in:
@@ -47,7 +47,8 @@ export function MusicPlayer() {
|
|||||||
|
|
||||||
const progressBarRef = useRef<HTMLDivElement>(null)
|
const progressBarRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
if (!currentTrack) {
|
// Show player if there's a playlist, even if no current track is playing
|
||||||
|
if (playlist.length === 0) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ export function MusicPlayer() {
|
|||||||
return (
|
return (
|
||||||
<Card className="fixed bottom-4 right-4 w-80 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 border shadow-lg z-50">
|
<Card className="fixed bottom-4 right-4 w-80 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 border shadow-lg z-50">
|
||||||
{/* Thumbnail */}
|
{/* Thumbnail */}
|
||||||
{currentTrack.thumbnail && (
|
{currentTrack?.thumbnail && (
|
||||||
<div className="relative h-32 w-full overflow-hidden rounded-t-lg">
|
<div className="relative h-32 w-full overflow-hidden rounded-t-lg">
|
||||||
<img
|
<img
|
||||||
src={currentTrack.thumbnail}
|
src={currentTrack.thumbnail}
|
||||||
@@ -122,9 +123,9 @@ export function MusicPlayer() {
|
|||||||
{/* Track info */}
|
{/* Track info */}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<h3 className="font-medium text-sm leading-tight line-clamp-1">
|
<h3 className="font-medium text-sm leading-tight line-clamp-1">
|
||||||
{currentTrack.title}
|
{currentTrack?.title || 'No track selected'}
|
||||||
</h3>
|
</h3>
|
||||||
{currentTrack.artist && (
|
{currentTrack?.artist && (
|
||||||
<p className="text-xs text-muted-foreground line-clamp-1">
|
<p className="text-xs text-muted-foreground line-clamp-1">
|
||||||
{currentTrack.artist}
|
{currentTrack.artist}
|
||||||
</p>
|
</p>
|
||||||
@@ -262,7 +263,7 @@ export function MusicPlayer() {
|
|||||||
{/* Main player area */}
|
{/* Main player area */}
|
||||||
<div className="flex-1 flex flex-col items-center justify-center p-8">
|
<div className="flex-1 flex flex-col items-center justify-center p-8">
|
||||||
{/* Large thumbnail */}
|
{/* Large thumbnail */}
|
||||||
{currentTrack.thumbnail && (
|
{currentTrack?.thumbnail && (
|
||||||
<div className="w-80 h-80 rounded-lg overflow-hidden mb-6 shadow-lg">
|
<div className="w-80 h-80 rounded-lg overflow-hidden mb-6 shadow-lg">
|
||||||
<img
|
<img
|
||||||
src={currentTrack.thumbnail}
|
src={currentTrack.thumbnail}
|
||||||
@@ -274,8 +275,8 @@ export function MusicPlayer() {
|
|||||||
|
|
||||||
{/* Track info */}
|
{/* Track info */}
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-6">
|
||||||
<h1 className="text-2xl font-bold mb-2">{currentTrack.title}</h1>
|
<h1 className="text-2xl font-bold mb-2">{currentTrack?.title || 'No track selected'}</h1>
|
||||||
{currentTrack.artist && (
|
{currentTrack?.artist && (
|
||||||
<p className="text-lg text-muted-foreground">{currentTrack.artist}</p>
|
<p className="text-lg text-muted-foreground">{currentTrack.artist}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export function NavPlan({ user }: NavPlanProps) {
|
|||||||
if (!socket || !isConnected) return
|
if (!socket || !isConnected) return
|
||||||
|
|
||||||
const handleCreditsChanged = (data: { credits: number }) => {
|
const handleCreditsChanged = (data: { credits: number }) => {
|
||||||
|
console.log('WAAAAAAZAAAA')
|
||||||
setCredits(data.credits)
|
setCredits(data.credits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { createContext, useContext, useState, useRef, useEffect, type ReactNode } from 'react'
|
import { createContext, useContext, useState, useRef, useEffect, type ReactNode } from 'react'
|
||||||
|
import { useSocket } from './SocketContext'
|
||||||
|
import { apiService } from '@/services/api'
|
||||||
|
|
||||||
export interface Track {
|
export interface Track {
|
||||||
id: string
|
id: string
|
||||||
@@ -41,6 +43,7 @@ interface MusicPlayerContextType {
|
|||||||
nextTrack: () => void
|
nextTrack: () => void
|
||||||
previousTrack: () => void
|
previousTrack: () => void
|
||||||
playTrack: (trackIndex: number) => void
|
playTrack: (trackIndex: number) => void
|
||||||
|
loadPlaylist: (playlistId: number) => void
|
||||||
addToPlaylist: (track: Track) => void
|
addToPlaylist: (track: Track) => void
|
||||||
removeFromPlaylist: (trackId: string) => void
|
removeFromPlaylist: (trackId: string) => void
|
||||||
clearPlaylist: () => void
|
clearPlaylist: () => void
|
||||||
@@ -63,232 +66,233 @@ interface MusicPlayerProviderProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function MusicPlayerProvider({ children }: MusicPlayerProviderProps) {
|
export function MusicPlayerProvider({ children }: MusicPlayerProviderProps) {
|
||||||
const audioRef = useRef<HTMLAudioElement | null>(null)
|
const { socket } = useSocket()
|
||||||
|
|
||||||
// Playback state
|
// Playback state
|
||||||
const [isPlaying, setIsPlaying] = useState(false)
|
const [isPlaying, setIsPlaying] = useState(false)
|
||||||
const [currentTime, setCurrentTime] = useState(0)
|
const [currentTime, setCurrentTime] = useState(0)
|
||||||
const [duration, setDuration] = useState(0)
|
const [duration, setDuration] = useState(0)
|
||||||
const [volume, setVolumeState] = useState(0.8)
|
const [volume, setVolumeState] = useState(80)
|
||||||
const [isMuted, setIsMuted] = useState(false)
|
const [isMuted, setIsMuted] = useState(false)
|
||||||
const [playMode, setPlayMode] = useState<PlayMode>('continuous')
|
const [playMode, setPlayModeState] = useState<PlayMode>('continuous')
|
||||||
|
|
||||||
// Playlist state
|
// Playlist state
|
||||||
const [currentTrackIndex, setCurrentTrackIndex] = useState(0)
|
const [currentTrackIndex, setCurrentTrackIndex] = useState(0)
|
||||||
const [playlist, setPlaylist] = useState<Track[]>([
|
const [playlist, setPlaylist] = useState<Track[]>([])
|
||||||
{
|
const [currentPlaylistId, setCurrentPlaylistId] = useState<number | null>(null)
|
||||||
id: '1',
|
const [currentTrack, setCurrentTrack] = useState<Track | null>(null)
|
||||||
title: 'Cheryl Lynn - Got To Be Real',
|
|
||||||
artist: 'Cheryl Lynn',
|
|
||||||
duration: 240,
|
|
||||||
thumbnail: '/api/sounds/stream/thumbnails/Cheryl Lynn - Got To Be Real Official Audio_fI569nw0YUQ.webp',
|
|
||||||
url: '/api/sounds/stream/Cheryl Lynn - Got To Be Real Official Audio_fI569nw0YUQ.opus'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
title: 'The Whispers - And The Beat Goes On',
|
|
||||||
artist: 'The Whispers',
|
|
||||||
duration: 280,
|
|
||||||
thumbnail: '/api/sounds/stream/thumbnails/The Whispers - And The Beat Goes On Official Video_pEmX5HR9ZxU.jpg',
|
|
||||||
url: '/api/sounds/stream/The Whispers - And The Beat Goes On Official Video_pEmX5HR9ZxU.opus'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
title: 'OLD RAP VS NEW RAP',
|
|
||||||
artist: 'Mister V, Jhon Rachid, Maskey',
|
|
||||||
duration: 320,
|
|
||||||
thumbnail: '/api/sounds/stream/thumbnails/OLD RAP VS NEW RAP Ft Mister V Jhon Rachid Maskey _PAFYcOFE3DY.webp',
|
|
||||||
url: '/api/sounds/stream/OLD RAP VS NEW RAP Ft Mister V Jhon Rachid Maskey _PAFYcOFE3DY.opus'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// UI state
|
// UI state
|
||||||
const [isMinimized, setIsMinimized] = useState(true)
|
const [isMinimized, setIsMinimized] = useState(true)
|
||||||
const [showPlaylist, setShowPlaylist] = useState(false)
|
const [showPlaylist, setShowPlaylist] = useState(false)
|
||||||
|
|
||||||
const currentTrack = playlist[currentTrackIndex] || null
|
// Fetch initial player state on mount
|
||||||
|
|
||||||
// Initialize audio element
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
audioRef.current = new Audio()
|
const fetchInitialState = async () => {
|
||||||
|
try {
|
||||||
|
console.log('🎵 MusicPlayerContext: Fetching initial player state...')
|
||||||
|
const response = await apiService.get('/api/player/state')
|
||||||
|
const state = await response.json()
|
||||||
|
|
||||||
const audio = audioRef.current
|
console.log('🎵 MusicPlayerContext: Initial state received', {
|
||||||
|
playlist_id: state.playlist_id,
|
||||||
|
is_playing: state.is_playing,
|
||||||
|
current_time: state.current_time,
|
||||||
|
duration: state.duration,
|
||||||
|
playlist_length: state.playlist?.length
|
||||||
|
})
|
||||||
|
|
||||||
const handleTimeUpdate = () => {
|
// If no playlist is loaded, try to load the main playlist
|
||||||
setCurrentTime(audio.currentTime)
|
if (!state.playlist_id) {
|
||||||
|
console.log('🎵 MusicPlayerContext: No playlist loaded, attempting to load main playlist...')
|
||||||
|
try {
|
||||||
|
await apiService.post('/api/player/load-main-playlist')
|
||||||
|
// Fetch state again after loading main playlist
|
||||||
|
const newResponse = await apiService.get('/api/player/state')
|
||||||
|
const newState = await newResponse.json()
|
||||||
|
console.log('🎵 MusicPlayerContext: Main playlist loaded, new state:', {
|
||||||
|
playlist_id: newState.playlist_id,
|
||||||
|
playlist_length: newState.playlist?.length
|
||||||
|
})
|
||||||
|
state = newState
|
||||||
|
} catch (loadError) {
|
||||||
|
console.warn('🎵 MusicPlayerContext: Failed to load main playlist:', loadError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLoadedMetadata = () => {
|
// Update all state from backend
|
||||||
setDuration(audio.duration)
|
console.log('🎵 MusicPlayerContext: Updating initial state in React')
|
||||||
|
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)
|
||||||
|
|
||||||
|
console.log('🎵 MusicPlayerContext: Initial state setup complete')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ MusicPlayerContext: Failed to fetch initial player state:', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleEnded = () => {
|
fetchInitialState()
|
||||||
handleTrackEnd()
|
|
||||||
}
|
|
||||||
|
|
||||||
audio.addEventListener('timeupdate', handleTimeUpdate)
|
|
||||||
audio.addEventListener('loadedmetadata', handleLoadedMetadata)
|
|
||||||
audio.addEventListener('ended', handleEnded)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
audio.removeEventListener('timeupdate', handleTimeUpdate)
|
|
||||||
audio.removeEventListener('loadedmetadata', handleLoadedMetadata)
|
|
||||||
audio.removeEventListener('ended', handleEnded)
|
|
||||||
audio.pause()
|
|
||||||
}
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Update audio source when track changes
|
// Listen for real-time player updates via SocketIO
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (audioRef.current && currentTrack) {
|
console.log('🎵 MusicPlayerContext: Setting up SocketIO listeners', { hasSocket: !!socket });
|
||||||
audioRef.current.src = currentTrack.url
|
|
||||||
audioRef.current.load()
|
|
||||||
}
|
|
||||||
}, [currentTrack])
|
|
||||||
|
|
||||||
// Update audio volume and mute state
|
if (!socket) {
|
||||||
useEffect(() => {
|
console.log('⏳ MusicPlayerContext: No socket available yet');
|
||||||
if (audioRef.current) {
|
return;
|
||||||
audioRef.current.volume = isMuted ? 0 : volume
|
|
||||||
}
|
}
|
||||||
}, [volume, isMuted])
|
|
||||||
|
|
||||||
const handleTrackEnd = () => {
|
const handlePlayerStateUpdate = (state: any) => {
|
||||||
switch (playMode) {
|
console.log('🎵 MusicPlayerContext: Received player state update', {
|
||||||
case 'loop-one':
|
is_playing: state.is_playing,
|
||||||
if (audioRef.current) {
|
current_time: state.current_time,
|
||||||
audioRef.current.currentTime = 0
|
duration: state.duration,
|
||||||
audioRef.current.play()
|
volume: state.volume,
|
||||||
|
current_track: state.current_track?.title,
|
||||||
|
playlist_length: state.playlist?.length
|
||||||
|
});
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
break
|
|
||||||
case 'loop-playlist':
|
console.log('🎵 MusicPlayerContext: Registering player_state_update listener');
|
||||||
nextTrack()
|
socket.on('player_state_update', handlePlayerStateUpdate)
|
||||||
break
|
|
||||||
case 'random':
|
return () => {
|
||||||
playRandomTrack()
|
console.log('🎵 MusicPlayerContext: Cleaning up SocketIO listeners');
|
||||||
break
|
socket.off('player_state_update', handlePlayerStateUpdate)
|
||||||
case 'continuous':
|
}
|
||||||
if (currentTrackIndex < playlist.length - 1) {
|
}, [socket])
|
||||||
nextTrack()
|
|
||||||
|
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 {
|
} else {
|
||||||
stop()
|
await play()
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const play = () => {
|
const seekTo = async (time: number) => {
|
||||||
if (audioRef.current && currentTrack) {
|
try {
|
||||||
audioRef.current.play()
|
const position = duration > 0 ? time / duration : 0
|
||||||
setIsPlaying(true)
|
await apiService.post('/api/player/seek', { position })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to seek:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pause = () => {
|
const setVolume = async (newVolume: number) => {
|
||||||
if (audioRef.current) {
|
try {
|
||||||
audioRef.current.pause()
|
await apiService.post('/api/player/volume', { volume: newVolume })
|
||||||
setIsPlaying(false)
|
} catch (error) {
|
||||||
|
console.error('Failed to set volume:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const stop = () => {
|
const toggleMute = async () => {
|
||||||
if (audioRef.current) {
|
try {
|
||||||
audioRef.current.pause()
|
const newVolume = isMuted ? 80 : 0
|
||||||
audioRef.current.currentTime = 0
|
await apiService.post('/api/player/volume', { volume: newVolume })
|
||||||
setIsPlaying(false)
|
} catch (error) {
|
||||||
setCurrentTime(0)
|
console.error('Failed to toggle mute:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const togglePlayPause = () => {
|
const nextTrack = async () => {
|
||||||
if (isPlaying) {
|
try {
|
||||||
pause()
|
await apiService.post('/api/player/next')
|
||||||
} else {
|
} catch (error) {
|
||||||
play()
|
console.error('Failed to skip to next track:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const seekTo = (time: number) => {
|
const previousTrack = async () => {
|
||||||
if (audioRef.current) {
|
try {
|
||||||
audioRef.current.currentTime = time
|
await apiService.post('/api/player/previous')
|
||||||
setCurrentTime(time)
|
} catch (error) {
|
||||||
|
console.error('Failed to skip to previous track:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setVolume = (newVolume: number) => {
|
const playTrack = async (trackIndex: number) => {
|
||||||
setVolumeState(newVolume)
|
try {
|
||||||
setIsMuted(newVolume === 0)
|
await apiService.post('/api/player/play-track', { index: trackIndex })
|
||||||
}
|
} catch (error) {
|
||||||
|
console.error('Failed to play track:', error)
|
||||||
const toggleMute = () => {
|
|
||||||
setIsMuted(!isMuted)
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextTrack = () => {
|
|
||||||
if (playlist.length === 0) return
|
|
||||||
|
|
||||||
const nextIndex = (currentTrackIndex + 1) % playlist.length
|
|
||||||
setCurrentTrackIndex(nextIndex)
|
|
||||||
|
|
||||||
// Auto-play if currently playing
|
|
||||||
if (isPlaying) {
|
|
||||||
setTimeout(() => play(), 100)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousTrack = () => {
|
const loadPlaylist = async (playlistId: number) => {
|
||||||
if (playlist.length === 0) return
|
try {
|
||||||
|
await apiService.post('/api/player/playlist', { playlist_id: playlistId })
|
||||||
const prevIndex = currentTrackIndex === 0 ? playlist.length - 1 : currentTrackIndex - 1
|
} catch (error) {
|
||||||
setCurrentTrackIndex(prevIndex)
|
console.error('Failed to load playlist:', error)
|
||||||
|
|
||||||
// Auto-play if currently playing
|
|
||||||
if (isPlaying) {
|
|
||||||
setTimeout(() => play(), 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const playRandomTrack = () => {
|
|
||||||
if (playlist.length <= 1) return
|
|
||||||
|
|
||||||
let randomIndex
|
|
||||||
do {
|
|
||||||
randomIndex = Math.floor(Math.random() * playlist.length)
|
|
||||||
} while (randomIndex === currentTrackIndex)
|
|
||||||
|
|
||||||
setCurrentTrackIndex(randomIndex)
|
|
||||||
setTimeout(() => play(), 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
const playTrack = (trackIndex: number) => {
|
|
||||||
if (trackIndex >= 0 && trackIndex < playlist.length) {
|
|
||||||
setCurrentTrackIndex(trackIndex)
|
|
||||||
setTimeout(() => play(), 100)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addToPlaylist = (track: Track) => {
|
const addToPlaylist = (track: Track) => {
|
||||||
setPlaylist(prev => [...prev, track])
|
// This would need to be implemented via API
|
||||||
|
console.log('Adding to playlist not yet implemented')
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeFromPlaylist = (trackId: string) => {
|
const removeFromPlaylist = (trackId: string) => {
|
||||||
setPlaylist(prev => {
|
// This would need to be implemented via API
|
||||||
const newPlaylist = prev.filter(track => track.id !== trackId)
|
console.log('Removing from playlist not yet implemented')
|
||||||
// Adjust current track index if necessary
|
|
||||||
const removedIndex = prev.findIndex(track => track.id === trackId)
|
|
||||||
if (removedIndex !== -1 && removedIndex < currentTrackIndex) {
|
|
||||||
setCurrentTrackIndex(current => Math.max(0, current - 1))
|
|
||||||
} else if (removedIndex === currentTrackIndex && newPlaylist.length > 0) {
|
|
||||||
setCurrentTrackIndex(current => Math.min(current, newPlaylist.length - 1))
|
|
||||||
}
|
|
||||||
return newPlaylist
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearPlaylist = () => {
|
const clearPlaylist = () => {
|
||||||
stop()
|
// This would need to be implemented via API
|
||||||
setPlaylist([])
|
console.log('Clearing playlist not yet implemented')
|
||||||
setCurrentTrackIndex(0)
|
}
|
||||||
|
|
||||||
|
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 = () => {
|
const toggleMaximize = () => {
|
||||||
@@ -303,7 +307,7 @@ export function MusicPlayerProvider({ children }: MusicPlayerProviderProps) {
|
|||||||
// Playback state
|
// Playback state
|
||||||
isPlaying,
|
isPlaying,
|
||||||
currentTime,
|
currentTime,
|
||||||
duration: currentTrack?.duration || 0,
|
duration,
|
||||||
volume,
|
volume,
|
||||||
isMuted,
|
isMuted,
|
||||||
playMode,
|
playMode,
|
||||||
@@ -329,6 +333,7 @@ export function MusicPlayerProvider({ children }: MusicPlayerProviderProps) {
|
|||||||
nextTrack,
|
nextTrack,
|
||||||
previousTrack,
|
previousTrack,
|
||||||
playTrack,
|
playTrack,
|
||||||
|
loadPlaylist,
|
||||||
addToPlaylist,
|
addToPlaylist,
|
||||||
removeFromPlaylist,
|
removeFromPlaylist,
|
||||||
clearPlaylist,
|
clearPlaylist,
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export const SocketProvider: React.FC<SocketProviderProps> = ({ children }) => {
|
|||||||
const { user, loading } = useAuth();
|
const { user, loading } = useAuth();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log('🔌 SocketProvider: Creating socket connection');
|
||||||
|
|
||||||
// Create socket connection
|
// Create socket connection
|
||||||
const newSocket = io("http://localhost:5000", {
|
const newSocket = io("http://localhost:5000", {
|
||||||
withCredentials: true, // Include cookies for authentication
|
withCredentials: true, // Include cookies for authentication
|
||||||
@@ -42,45 +44,101 @@ export const SocketProvider: React.FC<SocketProviderProps> = ({ children }) => {
|
|||||||
upgrade: false, // Disable WebSocket upgrade
|
upgrade: false, // Disable WebSocket upgrade
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('🔌 SocketProvider: Socket created', {
|
||||||
|
id: newSocket.id,
|
||||||
|
connected: newSocket.connected,
|
||||||
|
disconnected: newSocket.disconnected
|
||||||
|
});
|
||||||
|
|
||||||
// Set up event listeners
|
// Set up event listeners
|
||||||
newSocket.on("connect", () => {
|
newSocket.on("connect", () => {
|
||||||
|
console.log('✅ SocketIO: Connected successfully', {
|
||||||
|
id: newSocket.id,
|
||||||
|
transport: newSocket.io.engine.transport.name
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test if events work at all
|
||||||
|
console.log('🧪 SocketIO: Sending test event...');
|
||||||
|
newSocket.emit("test_event", { message: "Frontend test" });
|
||||||
|
|
||||||
|
// Send manual test event
|
||||||
|
console.log('🧪 SocketIO: Sending manual test event...');
|
||||||
|
newSocket.emit("manual_test", { message: "Frontend manual test" });
|
||||||
|
|
||||||
// Send authentication after connection
|
// Send authentication after connection
|
||||||
|
console.log('🔐 SocketIO: Sending authentication...');
|
||||||
newSocket.emit("authenticate", {});
|
newSocket.emit("authenticate", {});
|
||||||
});
|
});
|
||||||
|
|
||||||
newSocket.on("auth_success", () => {
|
newSocket.on("auth_success", (data) => {
|
||||||
|
console.log('🎉 SocketIO: Authentication successful', data);
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
newSocket.on("auth_error", () => {
|
newSocket.on("auth_error", (data) => {
|
||||||
|
console.error('❌ SocketIO: Authentication failed', data);
|
||||||
setIsConnected(false);
|
setIsConnected(false);
|
||||||
newSocket.disconnect();
|
newSocket.disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
newSocket.on("disconnect", () => {
|
newSocket.on("disconnect", (reason) => {
|
||||||
|
console.log('🔌 SocketIO: Disconnected', { reason });
|
||||||
setIsConnected(false);
|
setIsConnected(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
newSocket.on("connect_error", () => {
|
newSocket.on("connect_error", (error) => {
|
||||||
|
console.error('❌ SocketIO: Connection error', error);
|
||||||
setIsConnected(false);
|
setIsConnected(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listen for player state updates
|
||||||
|
newSocket.on("player_state_update", (data) => {
|
||||||
|
console.log('🎵 SocketIO: Player state update received', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for credits updates
|
||||||
|
newSocket.on("credits_changed", (data) => {
|
||||||
|
console.log('💰 SocketIO: Credits changed', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for test response
|
||||||
|
newSocket.on("test_response", (data) => {
|
||||||
|
console.log('🧪 SocketIO: Test response received', data);
|
||||||
|
});
|
||||||
|
|
||||||
setSocket(newSocket);
|
setSocket(newSocket);
|
||||||
|
|
||||||
// Clean up on unmount
|
// Clean up on unmount
|
||||||
return () => {
|
return () => {
|
||||||
|
console.log('🔌 SocketProvider: Cleaning up socket');
|
||||||
newSocket.close();
|
newSocket.close();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Connect/disconnect based on authentication state
|
// Connect/disconnect based on authentication state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!socket || loading) return;
|
console.log('🔄 SocketProvider: Auth state changed', {
|
||||||
|
hasSocket: !!socket,
|
||||||
|
loading,
|
||||||
|
hasUser: !!user,
|
||||||
|
userEmail: user?.email,
|
||||||
|
isConnected,
|
||||||
|
socketConnected: socket?.connected
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!socket || loading) {
|
||||||
|
console.log('⏳ SocketProvider: Waiting for socket or user loading...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (user && !isConnected) {
|
if (user && !isConnected) {
|
||||||
|
console.log('🚀 SocketProvider: User authenticated, connecting socket...');
|
||||||
socket.connect();
|
socket.connect();
|
||||||
} else if (!user && isConnected) {
|
} else if (!user && isConnected) {
|
||||||
|
console.log('🔌 SocketProvider: User logged out, disconnecting socket...');
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
|
} else {
|
||||||
|
console.log('⚡ SocketProvider: No action needed', { user: !!user, isConnected });
|
||||||
}
|
}
|
||||||
}, [socket, user, loading, isConnected]);
|
}, [socket, user, loading, isConnected]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user