ChandimaPrabath commited on
Commit
b615593
·
1 Parent(s): a34fbc5

playqueue button controll

Browse files
frontend/src/app/layout.js CHANGED
@@ -6,7 +6,7 @@ import MusicPlayer from "@/components/MusicPlayer";
6
  import { MusicPlayerProvider } from "@/context/MusicPlayerContext";
7
  import { AppProgressBar as ProgressBar } from "next-nprogress-bar";
8
  import Header from "@/components/Header";
9
- import Playlist from "@/components/Playlist";
10
 
11
  const geistSans = localFont({
12
  src: "./fonts/GeistVF.woff",
@@ -35,7 +35,7 @@ export default function RootLayout({ children }) {
35
  <Header />
36
  <div className="app-container">{children} </div>
37
  <footer className="bottom-0 flex items-center w-full">
38
- <Playlist />
39
  <MusicPlayer />
40
  </footer>
41
  </body>
 
6
  import { MusicPlayerProvider } from "@/context/MusicPlayerContext";
7
  import { AppProgressBar as ProgressBar } from "next-nprogress-bar";
8
  import Header from "@/components/Header";
9
+ import PlayQueue from "@/components/PlaylQueue";
10
 
11
  const geistSans = localFont({
12
  src: "./fonts/GeistVF.woff",
 
35
  <Header />
36
  <div className="app-container">{children} </div>
37
  <footer className="bottom-0 flex items-center w-full">
38
+ <PlayQueue isPlayQueueOpen={false}/>
39
  <MusicPlayer />
40
  </footer>
41
  </body>
frontend/src/components/MusicPlayer.js CHANGED
@@ -35,6 +35,8 @@ export default function MusicPlayer() {
35
  playPrevious,
36
  canPlayPrevious,
37
  canPlayNext,
 
 
38
  } = useMusicPlayer();
39
 
40
  const [currentSrc, setCurrentSrc] = useState(src);
@@ -316,6 +318,10 @@ export default function MusicPlayer() {
316
  setShowControls(true);
317
  };
318
 
 
 
 
 
319
  if (!isPlayerVisible || !currentSrc) return null;
320
 
321
  return (
@@ -401,14 +407,14 @@ export default function MusicPlayer() {
401
  <button className="player-min-button" onClick={destroyPlayer}>
402
  <TbPlayerStop />
403
  </button>
404
- {isPlaying ? (
405
- <button className="playlist-btn player-min-button">
406
  <TbPlaylist />
407
  </button>
408
  ) : (
409
  <button
410
  className="playlist-btn player-min-button"
411
- disabled={true}
412
  >
413
  <TbPlaylistOff />
414
  </button>
@@ -494,6 +500,18 @@ export default function MusicPlayer() {
494
  value={volume}
495
  onChange={handleVolumeChange}
496
  />
 
 
 
 
 
 
 
 
 
 
 
 
497
  </div>
498
  </div>
499
  )}
 
35
  playPrevious,
36
  canPlayPrevious,
37
  canPlayNext,
38
+ isPlayEueOpen,
39
+ setIsPlayEueOpen,
40
  } = useMusicPlayer();
41
 
42
  const [currentSrc, setCurrentSrc] = useState(src);
 
318
  setShowControls(true);
319
  };
320
 
321
+ const handleIsPlayQueueOpen = () =>{
322
+ setIsPlayEueOpen(!isPlayEueOpen);
323
+ };
324
+
325
  if (!isPlayerVisible || !currentSrc) return null;
326
 
327
  return (
 
407
  <button className="player-min-button" onClick={destroyPlayer}>
408
  <TbPlayerStop />
409
  </button>
410
+ {isPlayEueOpen ? (
411
+ <button className="playlist-btn player-min-button" onClick={handleIsPlayQueueOpen}>
412
  <TbPlaylist />
413
  </button>
414
  ) : (
415
  <button
416
  className="playlist-btn player-min-button"
417
+ onClick={handleIsPlayQueueOpen}
418
  >
419
  <TbPlaylistOff />
420
  </button>
 
500
  value={volume}
501
  onChange={handleVolumeChange}
502
  />
503
+ {isPlayEueOpen ? (
504
+ <button className="playlist-btn player-min-button" onClick={handleIsPlayQueueOpen}>
505
+ <TbPlaylist />
506
+ </button>
507
+ ) : (
508
+ <button
509
+ className="playlist-btn player-min-button"
510
+ onClick={handleIsPlayQueueOpen}
511
+ >
512
+ <TbPlaylistOff />
513
+ </button>
514
+ )}
515
  </div>
516
  </div>
517
  )}
frontend/src/components/PlaylQueue.js ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import React, { useState } from "react";
4
+ import { useMusicPlayer } from "@/context/MusicPlayerContext";
5
+ import { formatTitle } from "@/lib/utils";
6
+ import { FaTrash } from "react-icons/fa";
7
+ import { TbPlaylistX } from "react-icons/tb";
8
+ import "./Playlist.css";
9
+
10
+ const PlayQueue = () => {
11
+ const {
12
+ playqueue,
13
+ initializePlayer,
14
+ removeFromPlayqueue,
15
+ currentIndex,
16
+ setPlayqueue,
17
+ isPlayEueOpen,
18
+ setIsPlayEueOpen,
19
+ } = useMusicPlayer();
20
+ const [isModalOpen, setIsModalOpen] = useState(false);
21
+ const [playlistName, setPlaylistName] = useState("");
22
+
23
+ const handlePlayTrack = (src, title) => {
24
+ initializePlayer(src, title);
25
+ };
26
+
27
+ const handleClearPlaylist = () => {
28
+ if (window.confirm("Are you sure you want to clear the play queue?")) {
29
+ setPlayqueue([]); // Clear the play queue and trigger a re-render
30
+ }
31
+ };
32
+
33
+ const handleSavePlaylist = () => {
34
+ if (!playlistName.trim()) return;
35
+ const existingPlaylists =
36
+ JSON.parse(localStorage.getItem("playlists")) || {};
37
+ existingPlaylists[playlistName] = playqueue;
38
+ localStorage.setItem("playlists", JSON.stringify(existingPlaylists));
39
+ setIsModalOpen(false);
40
+ setPlaylistName("");
41
+ };
42
+
43
+ return (
44
+ isPlayEueOpen && (
45
+ <div className="playlist-container">
46
+ <h2>Play Queue</h2>
47
+ <div className="playlist-action-container">
48
+ <button onClick={() => setIsModalOpen(true)} className="save-button">
49
+ Save to Playlist
50
+ </button>
51
+ <button onClick={handleClearPlaylist} className="clear-button">
52
+ <TbPlaylistX /> Clear Queue
53
+ </button>
54
+ </div>
55
+ {isModalOpen && (
56
+ <div className="modal">
57
+ <div className="modal-content">
58
+ <h3>Save Playlist</h3>
59
+ <input
60
+ className="playlist-name-input"
61
+ type="text"
62
+ placeholder="Enter playlist name"
63
+ value={playlistName}
64
+ onChange={(e) => setPlaylistName(e.target.value)}
65
+ />
66
+ <button
67
+ onClick={handleSavePlaylist}
68
+ className="save-confirm-button"
69
+ >
70
+ Save
71
+ </button>
72
+ <button
73
+ onClick={() => setIsModalOpen(false)}
74
+ className="modal-close-button"
75
+ >
76
+ Cancel
77
+ </button>
78
+ </div>
79
+ </div>
80
+ )}
81
+
82
+ {playqueue.length === 0 ? (
83
+ <p>Empty</p>
84
+ ) : (
85
+ <ul className="playlist">
86
+ {playqueue.map((track, index) => (
87
+ <li
88
+ key={index}
89
+ className={`playlist-item ${
90
+ currentIndex === index ? "playing" : ""
91
+ }`}
92
+ >
93
+ <div className="track-info">
94
+ <span
95
+ className="track-title"
96
+ onClick={() => handlePlayTrack(track.source, track.title)}
97
+ >
98
+ {formatTitle(track.source)}
99
+ </span>
100
+ </div>
101
+ <button
102
+ className="remove-button"
103
+ onClick={() => removeFromPlayqueue(track.source)}
104
+ >
105
+ <FaTrash />
106
+ </button>
107
+ </li>
108
+ ))}
109
+ </ul>
110
+ )}
111
+ </div>
112
+ )
113
+ );
114
+ };
115
+
116
+ export default PlayQueue;
frontend/src/components/Playlist.js DELETED
@@ -1,93 +0,0 @@
1
- "use client";
2
- import React, { useState } from "react";
3
- import { useMusicPlayer } from "@/context/MusicPlayerContext";
4
- import { formatTitle } from "@/lib/utils";
5
- import { FaTrash } from "react-icons/fa";
6
- import { TbPlaylistX } from "react-icons/tb";
7
- import "./Playlist.css";
8
-
9
- const Playlist = () => {
10
- const { playqueue, initializePlayer, removeFromPlayqueue, currentIndex, setPlayqueue } = useMusicPlayer();
11
- const [isModalOpen, setIsModalOpen] = useState(false);
12
- const [playlistName, setPlaylistName] = useState("");
13
-
14
- const handlePlayTrack = (src, title) => {
15
- initializePlayer(src, title);
16
- };
17
-
18
- const handleClearPlaylist = () => {
19
- if (window.confirm("Are you sure you want to clear the play queue?")) {
20
- setPlayqueue([]); // Clear the play queue and trigger a re-render
21
- }
22
- };
23
-
24
- const handleSavePlaylist = () => {
25
- if (!playlistName.trim()) return;
26
- const existingPlaylists = JSON.parse(localStorage.getItem("playlists")) || {};
27
- existingPlaylists[playlistName] = playlist;
28
- localStorage.setItem("playlists", JSON.stringify(existingPlaylists));
29
- setIsModalOpen(false);
30
- setPlaylistName("");
31
- };
32
-
33
- return (
34
- <div className="playlist-container">
35
- <h2>Play Queue</h2>
36
- <div className="playlist-action-container">
37
- <button onClick={() => setIsModalOpen(true)} className="save-button">
38
- Save to Playlist
39
- </button>
40
- <button onClick={handleClearPlaylist} className="clear-button">
41
- <TbPlaylistX /> Clear Queue
42
- </button>
43
- </div>
44
- {isModalOpen && (
45
- <div className="modal">
46
- <div className="modal-content">
47
- <h3>Save Playlist</h3>
48
- <input
49
- className="playlist-name-input"
50
- type="text"
51
- placeholder="Enter playlist name"
52
- value={playlistName}
53
- onChange={(e) => setPlaylistName(e.target.value)}
54
- />
55
- <button onClick={handleSavePlaylist} className="save-confirm-button">
56
- Save
57
- </button>
58
- <button onClick={() => setIsModalOpen(false)} className="modal-close-button">
59
- Cancel
60
- </button>
61
- </div>
62
- </div>
63
- )}
64
-
65
- {playqueue.length === 0 ? (
66
- <p>Empty</p>
67
- ) : (
68
- <ul className="playlist">
69
- {playqueue.map((track, index) => (
70
- <li
71
- key={index}
72
- className={`playlist-item ${currentIndex === index ? "playing" : ""}`}
73
- >
74
- <div className="track-info">
75
- <span
76
- className="track-title"
77
- onClick={() => handlePlayTrack(track.source, track.title)}
78
- >
79
- {formatTitle(track.source)}
80
- </span>
81
- </div>
82
- <button className="remove-button" onClick={() => removeFromPlayqueue(track.source)}>
83
- <FaTrash />
84
- </button>
85
- </li>
86
- ))}
87
- </ul>
88
- )}
89
- </div>
90
- );
91
- };
92
-
93
- export default Playlist;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/context/MusicPlayerContext.js CHANGED
@@ -1,4 +1,10 @@
1
- import React, { createContext, useContext, useRef, useState, useEffect } from "react";
 
 
 
 
 
 
2
 
3
  const MusicPlayerContext = createContext();
4
 
@@ -15,6 +21,7 @@ export const MusicPlayerProvider = ({ children }) => {
15
 
16
  // Playqueue and index
17
  const [playqueue, setPlayqueue] = useState([]);
 
18
  const [currentIndex, setCurrentIndex] = useState(0);
19
 
20
  const canPlayPrevious = currentIndex > 0;
@@ -22,15 +29,15 @@ export const MusicPlayerProvider = ({ children }) => {
22
 
23
  const initializePlayer = async (source, title) => {
24
  // Check if song already exists in playqueue
25
- const songIndex = playqueue.findIndex(song => song.source === source);
26
-
27
  if (songIndex !== -1) {
28
- setCurrentIndex(songIndex); // Set to existing song index
29
  } else {
30
  // Add new song and set to that new index
31
  const newSong = { source, title };
32
  setPlayqueue((prev) => [...prev, newSong]);
33
- setCurrentIndex(playqueue.length); // Set index to the end (new song position)
34
  }
35
 
36
  if (abortController) {
@@ -40,16 +47,19 @@ export const MusicPlayerProvider = ({ children }) => {
40
  const newAbortController = new AbortController();
41
  setAbortController(newAbortController);
42
 
43
- const extractedFileName = source.split('/').pop();
44
  setTitle(title);
45
  if (videoRef.current) {
46
  videoRef.current.pause();
47
  }
48
 
49
  try {
50
- const response = await fetch(`/api/get/music/${encodeURIComponent(extractedFileName)}`, {
51
- signal: newAbortController.signal,
52
- });
 
 
 
53
  const data = await response.json();
54
 
55
  if (data.status === "Download started") {
@@ -59,7 +69,7 @@ export const MusicPlayerProvider = ({ children }) => {
59
  startPlayer(data.url);
60
  }
61
  } catch (error) {
62
- if (error.name !== 'AbortError') {
63
  console.error("Error initializing player:", error);
64
  }
65
  }
@@ -68,7 +78,9 @@ export const MusicPlayerProvider = ({ children }) => {
68
  const checkProgress = (progressUrl, fileName, abortController) => {
69
  const intervalId = setInterval(async () => {
70
  try {
71
- const response = await fetch(progressUrl, { signal: abortController.signal });
 
 
72
  const progressData = await response.json();
73
  setLoadingProgress(progressData.progress);
74
 
@@ -77,7 +89,7 @@ export const MusicPlayerProvider = ({ children }) => {
77
  fetchFinalUrl(fileName, abortController);
78
  }
79
  } catch (error) {
80
- if (error.name === 'AbortError') {
81
  clearInterval(intervalId);
82
  } else {
83
  console.error("Error fetching progress:", error);
@@ -88,9 +100,12 @@ export const MusicPlayerProvider = ({ children }) => {
88
 
89
  const fetchFinalUrl = async (fileName, abortController, attempt = 1) => {
90
  try {
91
- const response = await fetch(`/api/get/music/${encodeURIComponent(fileName)}`, {
92
- signal: abortController.signal,
93
- });
 
 
 
94
  const data = await response.json();
95
 
96
  if (data.url) {
@@ -99,7 +114,7 @@ export const MusicPlayerProvider = ({ children }) => {
99
  retryFetchFinalUrl(fileName, abortController, attempt);
100
  }
101
  } catch (error) {
102
- if (error.name !== 'AbortError') {
103
  console.error("Error fetching final URL:", error);
104
  retryFetchFinalUrl(fileName, abortController, attempt);
105
  }
@@ -127,14 +142,16 @@ export const MusicPlayerProvider = ({ children }) => {
127
  const togglePlayerSize = () => setIsPlayerMaximized(!isPlayerMaximized);
128
 
129
  const addToPlayqueue = (song) => {
130
- const songIndex = playqueue.findIndex(track => track.source === song.source);
 
 
131
  if (songIndex === -1) {
132
  setPlayqueue((prev) => [...prev, song]);
133
  }
134
  };
135
 
136
  const removeFromPlayqueue = (source) => {
137
- setPlayqueue((prev) => prev.filter(track => track.source !== source));
138
  if (currentIndex >= playqueue.length) {
139
  setCurrentIndex(playqueue.length - 1);
140
  }
@@ -192,7 +209,7 @@ export const MusicPlayerProvider = ({ children }) => {
192
  loadingProgress,
193
  nowPlaying,
194
  setNowPlaying,
195
- didDestroy,
196
  setDidDestroy,
197
  playqueue,
198
  setPlayqueue,
@@ -204,6 +221,8 @@ export const MusicPlayerProvider = ({ children }) => {
204
  currentIndex,
205
  canPlayPrevious,
206
  canPlayNext,
 
 
207
  }}
208
  >
209
  {children}
 
1
+ import React, {
2
+ createContext,
3
+ useContext,
4
+ useRef,
5
+ useState,
6
+ useEffect,
7
+ } from "react";
8
 
9
  const MusicPlayerContext = createContext();
10
 
 
21
 
22
  // Playqueue and index
23
  const [playqueue, setPlayqueue] = useState([]);
24
+ const [isPlayEueOpen, setIsPlayEueOpen] = useState(false);
25
  const [currentIndex, setCurrentIndex] = useState(0);
26
 
27
  const canPlayPrevious = currentIndex > 0;
 
29
 
30
  const initializePlayer = async (source, title) => {
31
  // Check if song already exists in playqueue
32
+ const songIndex = playqueue.findIndex((song) => song.source === source);
33
+
34
  if (songIndex !== -1) {
35
+ setCurrentIndex(songIndex); // Set to existing song index
36
  } else {
37
  // Add new song and set to that new index
38
  const newSong = { source, title };
39
  setPlayqueue((prev) => [...prev, newSong]);
40
+ setCurrentIndex(playqueue.length); // Set index to the end (new song position)
41
  }
42
 
43
  if (abortController) {
 
47
  const newAbortController = new AbortController();
48
  setAbortController(newAbortController);
49
 
50
+ const extractedFileName = source.split("/").pop();
51
  setTitle(title);
52
  if (videoRef.current) {
53
  videoRef.current.pause();
54
  }
55
 
56
  try {
57
+ const response = await fetch(
58
+ `/api/get/music/${encodeURIComponent(extractedFileName)}`,
59
+ {
60
+ signal: newAbortController.signal,
61
+ }
62
+ );
63
  const data = await response.json();
64
 
65
  if (data.status === "Download started") {
 
69
  startPlayer(data.url);
70
  }
71
  } catch (error) {
72
+ if (error.name !== "AbortError") {
73
  console.error("Error initializing player:", error);
74
  }
75
  }
 
78
  const checkProgress = (progressUrl, fileName, abortController) => {
79
  const intervalId = setInterval(async () => {
80
  try {
81
+ const response = await fetch(progressUrl, {
82
+ signal: abortController.signal,
83
+ });
84
  const progressData = await response.json();
85
  setLoadingProgress(progressData.progress);
86
 
 
89
  fetchFinalUrl(fileName, abortController);
90
  }
91
  } catch (error) {
92
+ if (error.name === "AbortError") {
93
  clearInterval(intervalId);
94
  } else {
95
  console.error("Error fetching progress:", error);
 
100
 
101
  const fetchFinalUrl = async (fileName, abortController, attempt = 1) => {
102
  try {
103
+ const response = await fetch(
104
+ `/api/get/music/${encodeURIComponent(fileName)}`,
105
+ {
106
+ signal: abortController.signal,
107
+ }
108
+ );
109
  const data = await response.json();
110
 
111
  if (data.url) {
 
114
  retryFetchFinalUrl(fileName, abortController, attempt);
115
  }
116
  } catch (error) {
117
+ if (error.name !== "AbortError") {
118
  console.error("Error fetching final URL:", error);
119
  retryFetchFinalUrl(fileName, abortController, attempt);
120
  }
 
142
  const togglePlayerSize = () => setIsPlayerMaximized(!isPlayerMaximized);
143
 
144
  const addToPlayqueue = (song) => {
145
+ const songIndex = playqueue.findIndex(
146
+ (track) => track.source === song.source
147
+ );
148
  if (songIndex === -1) {
149
  setPlayqueue((prev) => [...prev, song]);
150
  }
151
  };
152
 
153
  const removeFromPlayqueue = (source) => {
154
+ setPlayqueue((prev) => prev.filter((track) => track.source !== source));
155
  if (currentIndex >= playqueue.length) {
156
  setCurrentIndex(playqueue.length - 1);
157
  }
 
209
  loadingProgress,
210
  nowPlaying,
211
  setNowPlaying,
212
+ didDestroy,
213
  setDidDestroy,
214
  playqueue,
215
  setPlayqueue,
 
221
  currentIndex,
222
  canPlayPrevious,
223
  canPlayNext,
224
+ isPlayEueOpen,
225
+ setIsPlayEueOpen,
226
  }}
227
  >
228
  {children}