Skip to content

Commit

Permalink
getting a super fun null error on the audio player w/ linked list
Browse files Browse the repository at this point in the history
  • Loading branch information
tebetourney committed Apr 30, 2024
1 parent 951135e commit 6688e88
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 112 deletions.
4 changes: 2 additions & 2 deletions src/components/audioPlayer/audioPlayer.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.player-body {
width: 100%;
height: 44%;
height: 40%;
margin: 3% 0%;
}

Expand All @@ -17,7 +17,7 @@

.song-title {
text-align: center;
font-size: 58px;
font-size: 54px;
font-weight: bold;
margin: 0px;
color: #fefae0;
Expand Down
252 changes: 146 additions & 106 deletions src/components/audioPlayer/audioPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,121 +4,161 @@ import ProgressCircle from './progressCircle';
import WaveAnimation from './waveAnimation';
import Controls from './controls';

export default function AudioPlayer({ currentTrack, currentIndex, setCurrentIndex, total }) {
const [isPlaying, setIsPlaying] = useState(false);
const [trackProgress, setTrackProgress] = useState(0);
// Doubly-linked list w/ circular behavior
class SongNode {
constructor(track) {
this.track = track;
this.next = null;
this.prev = null;
}
}

const audioRef = useRef(new Audio(total[currentIndex]?.track.preview_url));

const intervalRef = useRef();
const isReady = useRef(false);

const { duration } = audioRef.current;
const currentPercentage = duration ? (trackProgress / duration) * 100 : 0;

intervalRef.current = setInterval(() => {
if (audioRef.current.ended) {
handleNext();
} else {
console.log("Current time:", audioRef.current.currentTime);
setTrackProgress(audioRef.current.currentTime);
class LinkedPlaylist {
constructor() {
this.head = null;
this.tail = null;
}
}, 1000);


const startTimer = () => {
clearInterval(intervalRef.current);
intervalRef.current = setInterval(() => {
if (audioRef.current.ended) {
handleNext();
} else {
setTrackProgress(audioRef.current.currentTime);
addTrack(track) {
const newNode = new SongNode(track);

if (!this.head) {
this.head = newNode;
this.tail = newNode;
this.head.next = this.tail;
this.tail.prev = this.head;
return;
}
}, 1000);
};

useEffect(() => {
if (isPlaying) {
audioRef.current.play().catch((err) => console.error("Play interrupted:", err));
startTimer();
} else {
clearInterval(intervalRef.current);
audioRef.current.pause();

newNode.prev = this.tail;
this.tail.next = newNode;
this.tail = newNode;
this.tail.next = this.head;
this.head.prev = this.tail;
}
}, [isPlaying]);

useEffect(() => {
audioRef.current.pause();
audioRef.current.src = total[currentIndex]?.track.preview_url;
setTrackProgress(0);

if (isReady.current) {
audioRef.current.play().catch((err) => console.error("Play interrupted:", err));
setIsPlaying(true);
startTimer();
} else {
isReady.current = true;

getNext(current) {
if (!current) {
return this.head; // fixing null error
}
return current.next;
}

getPrev(current) {
if (!current) {
return this.tail; // fixing null error
}
return current.prev;
}

getCurrentTrack() {
return this.head;
}
}, [currentIndex]);
}

useEffect(() => {
return () => {
audioRef.current.pause();
clearInterval(intervalRef.current);
export default function AudioPlayer({ total }) {
const [isPlaying, setIsPlaying] = useState(false);
const [trackProgress, setTrackProgress] = useState(0);

const playlist = new LinkedPlaylist();
total.forEach(track => playlist.addTrack(track));

const [currentTrackNode, setCurrentTrackNode] = useState(playlist.getCurrentTrack());
const audioRef = useRef(new Audio(currentTrackNode.track?.preview_url));

const intervalRef = useRef();
const isReady = useRef(false);

const duration = audioRef.current?.duration ?? 0;
const currentPercentage = duration ? (trackProgress / duration) * 100 : 0;

const startTimer = () => {
if (!audioRef.current) {
return; // fixing null error
}
clearInterval(intervalRef.current);
intervalRef.current = setInterval(() => {
if (audioRef.current.ended) {
handleNext();
} else {
setTrackProgress(audioRef.current.currentTime);
}
}, 1000);
};
}, []);

const handleNext = () => {
if (currentIndex < total.length - 1) {
setCurrentIndex(currentIndex + 1);
} else {
setCurrentIndex(0);
}
};

const handlePrev = () => {
if (currentIndex - 1 < 0) {
setCurrentIndex(total.length - 1);
} else {
setCurrentIndex(currentIndex - 1);
}
};

const addZero = (n) => {
return n > 9 ? "" + n : "0" + n;
};

const artists = currentTrack?.album?.artists.map((artist) => artist.name).join(" | ");

return (
<div className="player-body flex">
<div className="player-left-body">
<ProgressCircle
percentage={currentPercentage}
isPlaying={isPlaying}
image={currentTrack?.album?.images[0]?.url}
size={300}
color="#895d2b"
/>
</div>
<div className="player-right-body flex">
<p className="song-title">{currentTrack?.name}</p>
<p className='song-artist'>{artists}</p>
<div className="player-right-bottom flex">
<div className="song-duration flex">
<p className="duration">0:{addZero(Math.round(trackProgress))}</p>
<WaveAnimation isPlaying={isPlaying} />
<p className="duration">0:30</p>
</div>
<Controls

useEffect(() => {
if (isPlaying && audioRef.current) {
audioRef.current.play().catch((err) => console.error("Play interrupted:", err));
startTimer();
} else {
clearInterval(intervalRef.current);
audioRef.current?.pause(); // fixing null error
}
}, [isPlaying]);

useEffect(() => {
audioRef.current.pause();
audioRef.current.src = currentTrackNode.track?.preview_url;
setTrackProgress(0);

if (isReady.current) {
audioRef.current.play().catch((err) => console.error("Play interrupted:", err));
setIsPlaying(true);
startTimer();
} else {
isReady.current = true;
}
}, [currentTrackNode]);

useEffect(() => {
return () => {
audioRef.current.pause();
clearInterval(intervalRef.current);
};
}, []);

const handleNext = () => {
setCurrentTrackNode(playlist.getNext(currentTrackNode));
};

const handlePrev = () => {
setCurrentTrackNode(playlist.getPrev(currentTrackNode));
};

const addZero = (n) => {
return n > 9 ? "" + n : "0" + n;
};

const artists = currentTrackNode.track?.album?.artists.map((artist) => artist.name).join(" | ");

return (
<div className="player-body flex">
<div className="player-left-body">
<ProgressCircle
percentage={currentPercentage}
isPlaying={isPlaying}
setIsPlaying={setIsPlaying}
handleNext={handleNext}
handlePrev={handlePrev}
total={total}
image={currentTrackNode.track?.album?.images[0]?.url}
size={300}
color="#895d2b"
/>
</div>
<div class="player-right-body flex">
<p class="song-title">{currentTrackNode.track?.name}</p>
<p class='song-artist'>{artists}</p>
<div class="player-right-bottom flex">
<div class="song-duration flex">
<p class="duration">0:{addZero(Math.round(trackProgress))}</p>
<WaveAnimation isPlaying={isPlaying} />
<p class="duration">0:30</p>
</div>
<Controls
isPlaying={isPlaying}
setIsPlaying={setIsPlaying}
handleNext={handleNext}
handlePrev={handlePrev}
/>
</div>
</div>
</div>
</div>
);
}
);
}
15 changes: 14 additions & 1 deletion src/components/audioPlayer/controls.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease-in-out;
}

.action-btn:hover {
Expand All @@ -27,4 +28,16 @@
align-items: center;
justify-content: center;
cursor: pointer;
}
transition: all 0.2s ease-in-out;
box-shadow: inset -4px -4px 10px #162114, inset 4px 4px 10px 1px #a9b388;
}

.play-pause-btn.active {
transition: all 0.2s ease-in-out;
box-shadow: inset -4px -4px 10px #6a6f4c, inset 4px 4px 10px 1px #294122;
}

.play-pause-btn:hover {
transform: scale(1.1);
}

5 changes: 3 additions & 2 deletions src/components/audioPlayer/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ export default function Controls({
<div className='action-btn flex' onClick={handlePrev}>
<IoPlaySkipBack />
</div>
<div className="play-pause-btn flex" onClick={() => setIsPlaying(!isPlaying)}>
<div className={isPlaying ? "play-pause-btn flex active" : "play-pause-btn flex"}
onClick={() => setIsPlaying(!isPlaying)}>
{isPlaying ? <IoPause /> : <IoPlay />}
</div>
<div className='action-btn flex' onClick={handleNext}>
<IoPlaySkipForward />
</div>
</div>;
</div>
</IconContext.Provider>
);
}
2 changes: 1 addition & 1 deletion src/components/audioPlayer/progressCircle.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
circle {
transition: all 1s ease-in-out;
transition: all 0.5s ease-in-out;
transform: rotate(93deg);
transform-origin: 50% 50%;
}
Expand Down

0 comments on commit 6688e88

Please sign in to comment.