Spaces:
Sleeping
Sleeping
| 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 ( | |
| <MusicPlayerContext.Provider | |
| value={{ | |
| videoRef, | |
| initializePlayer, | |
| isPlayerVisible, | |
| src, | |
| title, | |
| isPlayerMaximized, | |
| togglePlayerSize, | |
| setIsPlayerVisible, | |
| loadingProgress, | |
| nowPlaying, | |
| setNowPlaying, | |
| didDestroy, | |
| setDidDestroy, | |
| playqueue, | |
| setPlayqueue, | |
| addToPlayqueue, | |
| removeFromPlayqueue, | |
| playNext, | |
| playPrevious, | |
| playAtIndex, | |
| currentIndex, | |
| canPlayPrevious, | |
| canPlayNext, | |
| isPlayEueOpen, | |
| setIsPlayEueOpen, | |
| }} | |
| > | |
| {children} | |
| </MusicPlayerContext.Provider> | |
| ); | |
| }; | |
| export const useMusicPlayer = () => { | |
| return useContext(MusicPlayerContext); | |
| }; | |