diff --git a/frontend/src/components/MusicPlayer.tsx b/frontend/src/components/MusicPlayer.tsx index 2925da4..15d585d 100644 --- a/frontend/src/components/MusicPlayer.tsx +++ b/frontend/src/components/MusicPlayer.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useEffect, useCallback } from 'react'; +import { useState, useRef, useEffect } from 'react'; interface Playlist { id: string; @@ -17,90 +17,29 @@ export default function MusicPlayer() { const [activeId, setActiveId] = useState('lofi-girl'); const [volume, setVolume] = useState(0.3); const [started, setStarted] = useState(false); - const [playing, setPlaying] = useState(false); - const playerRef = useRef(null); - const volumeRef = useRef(volume); - const activeIdRef = useRef(activeId); - const readyResolveRef = useRef<(() => void) | null>(null); + const iframeRef = useRef(null); - useEffect(() => { volumeRef.current = volume; }, [volume]); - useEffect(() => { activeIdRef.current = activeId; }, [activeId]); + const active = LOFI_PLAYLISTS.find((p) => p.id === activeId) ?? LOFI_PLAYLISTS[0]; - // Load YouTube IFrame API and create player - const initPlayer = useCallback(() => { - const active = LOFI_PLAYLISTS.find((p) => p.id === activeIdRef.current); - if (!active || playerRef.current) return; - - const createYT = () => { - playerRef.current = new (window as any).YT.Player('kira-youtube-player', { - height: '1', - width: '1', - videoId: active.videoId, - playerVars: { - autoplay: 1, - controls: 0, - loop: 1, - playlist: active.videoId, - enablejsapi: 1, - modestbranding: 1, - fs: 0, - rel: 0, - }, - events: { - onReady: (e: any) => { - e.target.setVolume(Math.round(volumeRef.current * 100)); - e.target.playVideo(); - setPlaying(true); - }, - onStateChange: (e: any) => { - // YT.PlayerState.PLAYING = 1, PAUSED = 2, ENDED = 0 - setPlaying(e.data === 1); - }, - onError: (e: any) => { - console.warn('[MusicPlayer] YouTube error:', e.data); - }, - }, - }); - }; - - if ((window as any).YT?.Player) { - createYT(); - } else { - // Load the script - const tag = document.createElement('script'); - tag.src = 'https://www.youtube.com/iframe_api'; - document.head.appendChild(tag); - (window as any).onYouTubeIframeAPIReady = createYT; - } - }, []); + // Sync volume to iframe via YT postMessage API + useEffect(() => { + if (!started || !iframeRef.current) return; + const target = Math.round(volume * 100); + // YouTube IFrame postMessage API for volume control + iframeRef.current.contentWindow?.postMessage( + JSON.stringify({ event: 'command', func: 'setVolume', args: [target] }), + '*' + ); + }, [volume, started]); const handlePlay = () => { setStarted(true); - initPlayer(); }; const changeStation = (id: string) => { setActiveId(id); - if (playerRef.current && (window as any).YT) { - const active = LOFI_PLAYLISTS.find((p) => p.id === id); - if (active) { - try { - playerRef.current.loadVideoById(active.videoId); - playerRef.current.setVolume(Math.round(volume * 100)); - } catch {} - } - } }; - // Volume sync - useEffect(() => { - try { - if (playerRef.current?.setVolume) { - playerRef.current.setVolume(Math.round(volume * 100)); - } - } catch {} - }, [volume]); - return (
@@ -125,7 +64,7 @@ export default function MusicPlayer() { {LOFI_PLAYLISTS.map((p) => (
- {/* YouTube player: must have real dimensions for the API to work */} -
+ {/* Hidden YouTube iframe — direct embed, no API */} + {started && ( +