ChandimaPrabath's picture
playqueue button controll
b615593
import { useState, useEffect, useRef } from "react";
import "./MusicPlayer.css";
import { useMusicPlayer } from "@/context/MusicPlayerContext";
import { IoPlayOutline, IoPauseOutline } from "react-icons/io5";
import { IoIosArrowDown, IoIosArrowUp } from "react-icons/io";
import {
TbRewindBackward5,
TbRewindForward5,
TbPlayerStop,
TbVolumeOff,
TbVolume,
TbPlayerSkipBack,
TbPlayerSkipForward,
TbPlaylist,
TbPlaylistOff,
} from "react-icons/tb";
import { RiFullscreenLine, RiFullscreenExitLine } from "react-icons/ri";
import { formatTime } from "./utils";
export default function MusicPlayer() {
const {
videoRef,
isPlayerVisible,
src,
isPlayerMaximized,
togglePlayerSize,
setIsPlayerVisible,
initializePlayer,
title,
setNowPlaying,
nowPlaying,
didDestroy,
setDidDestroy,
playNext,
playPrevious,
canPlayPrevious,
canPlayNext,
isPlayEueOpen,
setIsPlayEueOpen,
} = useMusicPlayer();
const [currentSrc, setCurrentSrc] = useState(src);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [progress, setProgress] = useState(0);
const [bufferProgress, setBufferProgress] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const [showControls, setShowControls] = useState(true);
const [isBuffering, setIsBuffering] = useState(true);
const [volume, setVolume] = useState(1);
const [isMuted, setIsMuted] = useState(false);
const [isFullscreen, setIsFullscreen] = useState(false);
const overlayTimeout = useRef(null);
const seekTime = 5;
const poster = "https://dlcdnwebimgs.asus.com/gain/4BB18AEF-347E-4DB6-B78C-C0FFE1F20385/w750/h470"
// Event Handlers
const handleTimeUpdate = (videoElement) => {
if (videoElement) {
setCurrentTime(videoElement.currentTime);
setProgress((videoElement.currentTime / videoElement.duration) * 100);
}
};
const handleLoadedMetadata = (videoElement) => {
if (videoElement) {
setDuration(videoElement.duration);
setProgress(0);
setCurrentTime(0);
videoElement.play();
}
};
const handleProgress = (videoElement) => {
if (videoElement && videoElement.buffered.length > 0) {
const bufferEnd = videoElement.buffered.end(
videoElement.buffered.length - 1
);
const bufferValue = (bufferEnd / videoElement.duration) * 100;
setBufferProgress(bufferValue);
}
};
const handlePlay = () => {
setIsPlaying(true);
setNowPlaying(title);
};
const handlePause = () => {
setIsPlaying(false);
setNowPlaying("");
};
const attachEventListeners = (videoElement) => {
if (videoElement) {
videoElement.addEventListener("timeupdate", () =>
handleTimeUpdate(videoElement)
);
videoElement.addEventListener("loadedmetadata", () =>
handleLoadedMetadata(videoElement)
);
videoElement.addEventListener("progress", () =>
handleProgress(videoElement)
);
videoElement.addEventListener("play", handlePlay);
videoElement.addEventListener("pause", handlePause);
}
};
const detachEventListeners = (videoElement) => {
if (videoElement) {
videoElement.removeEventListener("timeupdate", () =>
handleTimeUpdate(videoElement)
);
videoElement.removeEventListener("loadedmetadata", () =>
handleLoadedMetadata(videoElement)
);
videoElement.removeEventListener("progress", () =>
handleProgress(videoElement)
);
videoElement.removeEventListener("play", handlePlay);
videoElement.removeEventListener("pause", handlePause);
}
};
useEffect(() => {
const videoElement = videoRef.current;
// Attach event listeners
attachEventListeners(videoElement);
return () => {
// Detach event listeners
detachEventListeners(videoElement);
};
}, [videoRef, currentSrc, nowPlaying, didDestroy]);
useEffect(() => {
if (src !== currentSrc) {
setCurrentSrc(src);
}
}, [src, currentSrc]);
useEffect(() => {
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: title,
artwork: [
{ src: poster } // Ensure 'poster' is defined in your component
]
});
navigator.mediaSession.setActionHandler('play', () => {
videoRef.current.play();
});
navigator.mediaSession.setActionHandler('pause', () => {
videoRef.current.pause();
});
navigator.mediaSession.setActionHandler('seekbackward', (details) => {
videoRef.current.currentTime = Math.max(videoRef.current.currentTime - (details.seekOffset || 10), 0);
});
navigator.mediaSession.setActionHandler('seekforward', (details) => {
videoRef.current.currentTime = Math.min(videoRef.current.currentTime + (details.seekOffset || 10), videoRef.current.duration);
});
navigator.mediaSession.setActionHandler('stop', () => {
destroyPlayer();
});
// New action handlers for previous and next
navigator.mediaSession.setActionHandler('previoustrack', () => {
playPrevious(); // Assuming playPrevious is a function that plays the previous track
});
navigator.mediaSession.setActionHandler('nexttrack', () => {
playNext(); // Assuming playNext is a function that plays the next track
});
}
// Cleanup function to reset media session when component unmounts
return () => {
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = null;
navigator.mediaSession.setActionHandler('play', null);
navigator.mediaSession.setActionHandler('pause', null);
navigator.mediaSession.setActionHandler('seekbackward', null);
navigator.mediaSession.setActionHandler('seekforward', null);
navigator.mediaSession.setActionHandler('stop', null);
navigator.mediaSession.setActionHandler('previoustrack', null);
navigator.mediaSession.setActionHandler('nexttrack', null);
}
};
}, [title, poster, playPrevious, playNext]);
useEffect(() => {
if (showControls) {
if (overlayTimeout.current) {
clearTimeout(overlayTimeout.current);
}
overlayTimeout.current = setTimeout(() => setShowControls(false), 3000);
}
return () => clearTimeout(overlayTimeout.current);
}, [showControls]);
const togglePlayPause = () => {
if (videoRef.current) {
if (isPlaying) {
videoRef.current.pause();
} else {
videoRef.current.play();
}
setIsPlaying(!isPlaying);
}
};
const handleVolumeChange = (event) => {
let volumeValue = parseFloat(event.target.value);
// Ensure the volume is a finite number between 0 and 1
volumeValue = Math.max(0, Math.min(1, volumeValue));
setVolume(volumeValue);
if (videoRef.current) {
videoRef.current.volume = volumeValue;
}
setIsMuted(volumeValue === 0);
};
const toggleMute = () => {
if (isMuted) {
videoRef.current.volume = volume;
setIsMuted(false);
} else {
videoRef.current.volume = 0;
setIsMuted(true);
}
};
const toggleFullscreen = () => {
const doc = window.document;
const docEl = doc.documentElement;
const requestFullscreen =
docEl.requestFullscreen ||
docEl.mozRequestFullScreen ||
docEl.webkitRequestFullscreen ||
docEl.msRequestFullscreen;
const exitFullscreen =
doc.exitFullscreen ||
doc.mozCancelFullScreen ||
docEl.webkitExitFullscreen ||
doc.msExitFullscreen;
if (!isFullscreen) {
requestFullscreen.call(docEl);
} else {
exitFullscreen.call(doc);
}
setIsFullscreen(!isFullscreen);
};
const destroyPlayer = () => {
if (videoRef.current) {
videoRef.current.pause();
videoRef.current.currentTime = 0;
videoRef.current.removeAttribute("src"); // Clear the source
videoRef.current.load(); // Reload to reset duration/currentTime
setNowPlaying("");
setCurrentSrc("/reset");
setCurrentTime(0);
setDuration(0);
setProgress(0);
setBufferProgress(0);
setIsPlaying(false);
setIsPlayerVisible(false);
setDidDestroy(true);
console.log("setting didDestroy to true");
}
};
const handleProgressClick = (e) => {
const progressBar = e.currentTarget;
const rect = progressBar.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const newTime = (clickX / rect.width) * duration;
if (videoRef.current) {
videoRef.current.currentTime = newTime;
setCurrentTime(newTime);
}
};
const handleFastForward = () => {
if (videoRef.current) {
videoRef.current.currentTime = Math.min(
videoRef.current.duration,
videoRef.current.currentTime + seekTime
);
}
};
const handleRewind = () => {
if (videoRef.current) {
videoRef.current.currentTime = Math.max(
0,
videoRef.current.currentTime - seekTime
);
}
};
const handleMouseMove = () => {
setShowControls(true);
};
const handleIsPlayQueueOpen = () =>{
setIsPlayEueOpen(!isPlayEueOpen);
};
if (!isPlayerVisible || !currentSrc) return null;
return (
<div
className={
isPlayerMaximized
? "player-maximized relative"
: "player-minimized relative"
}
onMouseMove={handleMouseMove}
>
{isPlayerMaximized && (
<div className={`player-controls ${showControls ? "show" : "hide"}`}>
<div className="player-controls-top">
<div className="name-container">
<label className="music-title">{title}</label>
<button className="player-max-button" onClick={togglePlayerSize}>
<IoIosArrowDown />
</button>
</div>
</div>
<div className="player-controls-center">
<button className="player-max-button" onClick={handleRewind}>
<TbRewindBackward5 />
</button>
<button className="player-max-button" onClick={togglePlayPause}>
{isPlaying ? <IoPauseOutline /> : <IoPlayOutline />}
</button>
<button className="player-max-button" onClick={handleFastForward}>
<TbRewindForward5 />
</button>
</div>
<div className="player-controls-bottom">
<div className="player-control-bottom-top">
<label className="current-time">{formatTime(currentTime)}</label>
<div
className="progress-bar-container"
onClick={handleProgressClick}
>
<div
className="buffer-bar"
style={{ width: `${bufferProgress}%` }}
/>
<div
className="progress-bar"
style={{ width: `${progress}%` }}
/>
</div>
<label className="duration">{formatTime(duration)}</label>
</div>
<div className="player-controls-down">
<div className="player-controls-left">
<button
onClick={toggleMute}
className="player-min-button volumn-btn"
>
{isMuted ? <TbVolumeOff /> : <TbVolume />}
</button>
<input
type="range"
className="volume-control"
min="0"
max="1"
step="0.01"
value={volume}
onChange={handleVolumeChange}
/>
<button
onClick={playPrevious}
className="previous-btn player-min-button"
disabled={!canPlayPrevious}
>
<TbPlayerSkipBack />
</button>
<button
onClick={playNext}
className="next-btn player-min-button"
disabled={!canPlayNext}
>
<TbPlayerSkipForward />
</button>
<button className="player-min-button" onClick={destroyPlayer}>
<TbPlayerStop />
</button>
{isPlayEueOpen ? (
<button className="playlist-btn player-min-button" onClick={handleIsPlayQueueOpen}>
<TbPlaylist />
</button>
) : (
<button
className="playlist-btn player-min-button"
onClick={handleIsPlayQueueOpen}
>
<TbPlaylistOff />
</button>
)}
</div>
<div className="player-controls-right">
<button
onClick={toggleFullscreen}
className="player-min-button"
>
{isFullscreen ? (
<RiFullscreenExitLine />
) : (
<RiFullscreenLine />
)}
</button>
</div>
</div>
</div>
</div>
)}
<div className="player-top">
<video
ref={videoRef}
src={currentSrc}
poster={poster}
preload="metadata"
className="video-element"
autoPlay
onClick={isPlayerMaximized ? null : togglePlayerSize}
/>
{!isPlayerMaximized && (
<div className="player-controls-mini">
<div className="player-mini-control-top">
<label className="music-title player-mini">{title}</label>
<button className="player-min-button" onClick={togglePlayerSize}>
<IoIosArrowUp />
</button>
</div>
<div className="player-mini-control-center">
<button
onClick={playPrevious}
className="previous-btn player-min-button"
disabled={!canPlayPrevious}
>
<TbPlayerSkipBack />
</button>
<button className="player-min-button" onClick={handleRewind}>
<TbRewindBackward5 />
</button>
<button className="player-min-button" onClick={togglePlayPause}>
{isPlaying ? <IoPauseOutline /> : <IoPlayOutline />}
</button>
<button className="player-min-button" onClick={handleFastForward}>
<TbRewindForward5 />
</button>
<button
onClick={playNext}
className="next-btn player-min-button"
disabled={!canPlayNext}
>
<TbPlayerSkipForward />
</button>
<button className="player-min-button" onClick={destroyPlayer}>
<TbPlayerStop />
</button>
</div>
<div className="player-mini-volume">
<button
onClick={toggleMute}
className="player-min-button volumn-btn"
>
{isMuted ? <TbVolumeOff /> : <TbVolume />}
</button>
<input
type="range"
className="volume-control"
min="0"
max="1"
step="0.01"
value={volume}
onChange={handleVolumeChange}
/>
{isPlayEueOpen ? (
<button className="playlist-btn player-min-button" onClick={handleIsPlayQueueOpen}>
<TbPlaylist />
</button>
) : (
<button
className="playlist-btn player-min-button"
onClick={handleIsPlayQueueOpen}
>
<TbPlaylistOff />
</button>
)}
</div>
</div>
)}
</div>
{!isPlayerMaximized && (
<div className="player-mini-control-bottom">
<label className="current-time">{formatTime(currentTime)}</label>
<div
className="progress-bar-container player-mini"
onClick={handleProgressClick}
>
<div
className="buffer-bar"
style={{ width: `${bufferProgress}%` }}
/>
<div className="progress-bar" style={{ width: `${progress}%` }} />
</div>
<label className="duration">{formatTime(duration)}</label>
</div>
)}
</div>
);
}