import React, { createContext, useContext, useRef, useState, useEffect, } from "react"; const MusicPlayerContext = createContext(); export const MusicPlayerProvider = ({ children }) => { const videoRef = useRef(null); const [src, setSrc] = useState(""); const [title, setTitle] = useState(""); const [nowPlaying, setNowPlaying] = useState(""); const [isPlayerVisible, setIsPlayerVisible] = useState(false); const [isPlayerMaximized, setIsPlayerMaximized] = useState(false); const [loadingProgress, setLoadingProgress] = useState(null); const [abortController, setAbortController] = useState(null); const [didDestroy, setDidDestroy] = useState(false); // Playqueue and index const [playqueue, setPlayqueue] = useState([]); const [isPlayEueOpen, setIsPlayEueOpen] = useState(false); const [currentIndex, setCurrentIndex] = useState(0); const canPlayPrevious = currentIndex > 0; const canPlayNext = currentIndex < playqueue.length - 1; const initializePlayer = async (source, title) => { // Check if song already exists in playqueue const songIndex = playqueue.findIndex((song) => song.source === source); if (songIndex !== -1) { setCurrentIndex(songIndex); // Set to existing song index } else { // Add new song and set to that new index const newSong = { source, title }; setPlayqueue((prev) => [...prev, newSong]); setCurrentIndex(playqueue.length); // Set index to the end (new song position) } if (abortController) { abortController.abort(); } const newAbortController = new AbortController(); setAbortController(newAbortController); const extractedFileName = source.split("/").pop(); setTitle(title); if (videoRef.current) { videoRef.current.pause(); } try { const response = await fetch( `/api/get/music/${encodeURIComponent(extractedFileName)}`, { signal: newAbortController.signal, } ); const data = await response.json(); if (data.status === "Download started") { const progressUrl = data.progress_url; checkProgress(progressUrl, extractedFileName, newAbortController); } else { startPlayer(data.url); } } catch (error) { if (error.name !== "AbortError") { console.error("Error initializing player:", error); } } }; const checkProgress = (progressUrl, fileName, abortController) => { const intervalId = setInterval(async () => { try { const response = await fetch(progressUrl, { signal: abortController.signal, }); const progressData = await response.json(); setLoadingProgress(progressData.progress); if (progressData.progress.status === "Completed") { clearInterval(intervalId); fetchFinalUrl(fileName, abortController); } } catch (error) { if (error.name === "AbortError") { clearInterval(intervalId); } else { console.error("Error fetching progress:", error); } } }, 1000); }; const fetchFinalUrl = async (fileName, abortController, attempt = 1) => { try { const response = await fetch( `/api/get/music/${encodeURIComponent(fileName)}`, { signal: abortController.signal, } ); const data = await response.json(); if (data.url) { startPlayer(data.url); } else { retryFetchFinalUrl(fileName, abortController, attempt); } } catch (error) { if (error.name !== "AbortError") { console.error("Error fetching final URL:", error); retryFetchFinalUrl(fileName, abortController, attempt); } } }; const retryFetchFinalUrl = (fileName, abortController, attempt) => { const retryDelay = 2000; setTimeout(() => { console.log(`Retry attempt ${attempt} for ${fileName}`); fetchFinalUrl(fileName, abortController, attempt + 1); }, retryDelay); }; const startPlayer = (source) => { setDidDestroy(false); setSrc(source); setIsPlayerVisible(true); if (videoRef.current) { videoRef.current.src = source; videoRef.current.load(); } }; const togglePlayerSize = () => setIsPlayerMaximized(!isPlayerMaximized); const addToPlayqueue = (song) => { const songIndex = playqueue.findIndex( (track) => track.source === song.source ); if (songIndex === -1) { setPlayqueue((prev) => [...prev, song]); } }; const removeFromPlayqueue = (source) => { setPlayqueue((prev) => prev.filter((track) => track.source !== source)); if (currentIndex >= playqueue.length) { setCurrentIndex(playqueue.length - 1); } }; const playNext = () => { if (canPlayNext) { const nextIndex = currentIndex + 1; setCurrentIndex(nextIndex); const { source, title } = playqueue[nextIndex]; initializePlayer(source, title); } }; const playPrevious = () => { if (canPlayPrevious) { const prevIndex = currentIndex - 1; setCurrentIndex(prevIndex); const { source, title } = playqueue[prevIndex]; initializePlayer(source, title); } }; const playAtIndex = (index) => { if (index >= 0 && index < playqueue.length) { setCurrentIndex(index); const { source, title } = playqueue[index]; initializePlayer(source, title); } }; useEffect(() => { const handleEnded = () => playNext(); if (videoRef.current) { videoRef.current.addEventListener("ended", handleEnded); } return () => { if (videoRef.current) { videoRef.current.removeEventListener("ended", handleEnded); } }; }, [currentIndex, playqueue]); return ( {children} ); }; export const useMusicPlayer = () => { return useContext(MusicPlayerContext); };