ChandimaPrabath commited on
Commit
eb2c7d1
·
1 Parent(s): ba6c831
frontend/src/components/Card.js CHANGED
@@ -6,17 +6,17 @@ import { useMusicPlayer } from "@/context/MusicPlayerContext";
6
  import { formatTitle } from "@/lib/utils";
7
 
8
  const Card = ({ src }) => {
9
- const { initializePlayer, nowPlaying, addToPlaylist } = useMusicPlayer();
10
  const title = formatTitle(src);
11
 
12
  const handlePlayButtonClick = () => {
13
  initializePlayer(src, title);
14
  };
15
 
16
- const handleAddToPlaylistClick = (e) => {
17
  e.stopPropagation(); // Prevent the button click from triggering the card play
18
- addToPlaylist({ source: src, title });
19
- console.log(`${title} added to playlist`)
20
  };
21
 
22
  return (
@@ -27,7 +27,7 @@ const Card = ({ src }) => {
27
  <label className="card-title">{title}</label>
28
  </div>
29
  </button>
30
- <button className="add-to-playlist" onClick={handleAddToPlaylistClick}>
31
  <TbPlaylistAdd className="add-icon" />
32
  </button>
33
  </>
 
6
  import { formatTitle } from "@/lib/utils";
7
 
8
  const Card = ({ src }) => {
9
+ const { initializePlayer, nowPlaying, addToPlayqueue } = useMusicPlayer();
10
  const title = formatTitle(src);
11
 
12
  const handlePlayButtonClick = () => {
13
  initializePlayer(src, title);
14
  };
15
 
16
+ const handleAddToPlayqueueClick = (e) => {
17
  e.stopPropagation(); // Prevent the button click from triggering the card play
18
+ addToPlayqueue({ source: src, title });
19
+ console.log(`${title} added to play queue`)
20
  };
21
 
22
  return (
 
27
  <label className="card-title">{title}</label>
28
  </div>
29
  </button>
30
+ <button className="add-to-playlist" onClick={handleAddToPlayqueueClick}>
31
  <TbPlaylistAdd className="add-icon" />
32
  </button>
33
  </>
frontend/src/components/Header.js CHANGED
@@ -8,7 +8,7 @@ const Header = () => {
8
  <header className="header">
9
  <div className="header-container">
10
  <h1 className="logo">
11
- Nexora<sup className="header-music"> Music</sup>
12
  </h1>
13
  <nav className="nav">
14
  <Link href="/" className="nav-link">
 
8
  <header className="header">
9
  <div className="header-container">
10
  <h1 className="logo">
11
+ Nexora<sup className="header-music">Music</sup>
12
  </h1>
13
  <nav className="nav">
14
  <Link href="/" className="nav-link">
frontend/src/components/Playlist.css CHANGED
@@ -78,6 +78,7 @@
78
  .clear-button {
79
  margin-left: 10px; /* Add some spacing */
80
  display: flex;
 
81
  }
82
 
83
  .modal {
 
78
  .clear-button {
79
  margin-left: 10px; /* Add some spacing */
80
  display: flex;
81
+ align-items: center;
82
  }
83
 
84
  .modal {
frontend/src/components/Playlist.js CHANGED
@@ -7,7 +7,7 @@ import { TbPlaylistX } from "react-icons/tb";
7
  import "./Playlist.css";
8
 
9
  const Playlist = () => {
10
- const { playlist, initializePlayer, removeFromPlaylist, currentIndex, setPlaylist } = useMusicPlayer();
11
  const [isModalOpen, setIsModalOpen] = useState(false);
12
  const [playlistName, setPlaylistName] = useState("");
13
 
@@ -16,8 +16,8 @@ const Playlist = () => {
16
  };
17
 
18
  const handleClearPlaylist = () => {
19
- if (window.confirm("Are you sure you want to clear the playlist?")) {
20
- setPlaylist([]); // Clear the playlist and trigger a re-render
21
  }
22
  };
23
 
@@ -32,13 +32,13 @@ const Playlist = () => {
32
 
33
  return (
34
  <div className="playlist-container">
35
- <h2>Your Playlist</h2>
36
  <div className="playlist-action-container">
37
  <button onClick={() => setIsModalOpen(true)} className="save-button">
38
- Save Playlist
39
  </button>
40
  <button onClick={handleClearPlaylist} className="clear-button">
41
- <TbPlaylistX /> Clear Playlist
42
  </button>
43
  </div>
44
  {isModalOpen && (
@@ -62,11 +62,11 @@ const Playlist = () => {
62
  </div>
63
  )}
64
 
65
- {playlist.length === 0 ? (
66
- <p>No tracks in the playlist</p>
67
  ) : (
68
  <ul className="playlist">
69
- {playlist.map((track, index) => (
70
  <li
71
  key={index}
72
  className={`playlist-item ${currentIndex === index ? "playing" : ""}`}
@@ -79,7 +79,7 @@ const Playlist = () => {
79
  {formatTitle(track.source)}
80
  </span>
81
  </div>
82
- <button className="remove-button" onClick={() => removeFromPlaylist(track.source)}>
83
  <FaTrash />
84
  </button>
85
  </li>
 
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
 
 
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
 
 
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 && (
 
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" : ""}`}
 
79
  {formatTitle(track.source)}
80
  </span>
81
  </div>
82
+ <button className="remove-button" onClick={() => removeFromPlayqueue(track.source)}>
83
  <FaTrash />
84
  </button>
85
  </li>
frontend/src/components/SavedPlaylists.css CHANGED
@@ -5,23 +5,21 @@
5
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
6
  }
7
 
8
- .saved-playlists {
9
- list-style: none;
10
- padding: 0;
11
- margin: 0;
12
  }
13
 
14
- .saved-playlist-item {
15
- display: flex;
16
- justify-content: space-between;
17
- align-items: center;
18
  padding: 10px;
 
19
  border-bottom: 1px solid var(--border-color);
20
  }
21
 
22
- .playlist-name {
23
- font-size: 1rem;
24
- color: var(--text-color);
25
  }
26
 
27
  .playlist-controls {
 
5
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
6
  }
7
 
8
+ .saved-playlists-table {
9
+ width: 100%; /* Make table width 100% */
10
+ border-collapse: collapse; /* Collapse borders for better appearance */
 
11
  }
12
 
13
+ .saved-playlists-table th,
14
+ .saved-playlists-table td {
 
 
15
  padding: 10px;
16
+ text-align: left; /* Align text to the left */
17
  border-bottom: 1px solid var(--border-color);
18
  }
19
 
20
+ .saved-playlists-table th {
21
+ background-color: var(--background-secondary); /* Optional: Add background to header */
22
+ color: var(--text-color); /* Set text color for header */
23
  }
24
 
25
  .playlist-controls {
frontend/src/components/SavedPlaylists.js CHANGED
@@ -2,23 +2,17 @@
2
  import React, { useState } from "react";
3
  import { useMusicPlayer } from "@/context/MusicPlayerContext";
4
  import { formatTitle } from "@/lib/utils";
5
- import { FaTrash, FaEdit } from "react-icons/fa"; // Import icons for edit and delete
6
- import "./SavedPlaylists.css"; // Import a CSS file for styling
7
 
8
  const SavedPlaylists = () => {
9
- const { initializePlayer, addToPlaylist } = useMusicPlayer();
10
  const [playlists, setPlaylists] = useState(() => JSON.parse(localStorage.getItem("playlists")) || {});
11
 
12
  const handlePlayPlaylist = (tracks) => {
13
- // Check if there's at least one track to play
14
  if (tracks.length > 0) {
15
- // Initialize the player with the first track
16
- initializePlayer(tracks[0].source, tracks[0].title); // Start playing the first track
17
-
18
- // Add the rest of the tracks to the playlist
19
- tracks.slice(1).forEach(track => {
20
- addToPlaylist({ source: track.source, title: track.title });
21
- });
22
  }
23
  };
24
 
@@ -45,18 +39,26 @@ const SavedPlaylists = () => {
45
  {Object.keys(playlists).length === 0 ? (
46
  <p>No saved playlists</p>
47
  ) : (
48
- <ul className="saved-playlists">
49
- {Object.entries(playlists).map(([name, tracks]) => (
50
- <li key={name} className="saved-playlist-item">
51
- <span className="playlist-name">{name}</span>
52
- <div className="playlist-controls">
53
- <button onClick={() => handlePlayPlaylist(tracks)} className="play-button">Play</button>
54
- <button onClick={() => handleEditPlaylist(name)} className="edit-button"><FaEdit /></button>
55
- <button onClick={() => handleDeletePlaylist(name)} className="remove-button"><FaTrash /></button>
56
- </div>
57
- </li>
58
- ))}
59
- </ul>
 
 
 
 
 
 
 
 
60
  )}
61
  </div>
62
  );
 
2
  import React, { useState } from "react";
3
  import { useMusicPlayer } from "@/context/MusicPlayerContext";
4
  import { formatTitle } from "@/lib/utils";
5
+ import { FaTrash, FaEdit } from "react-icons/fa";
6
+ import "./SavedPlaylists.css";
7
 
8
  const SavedPlaylists = () => {
9
+ const { initializePlayer, addToPlayqueue } = useMusicPlayer();
10
  const [playlists, setPlaylists] = useState(() => JSON.parse(localStorage.getItem("playlists")) || {});
11
 
12
  const handlePlayPlaylist = (tracks) => {
 
13
  if (tracks.length > 0) {
14
+ initializePlayer(tracks[0].source, tracks[0].title);
15
+ tracks.slice(1).forEach(track => addToPlayqueue({ source: track.source, title: track.title }));
 
 
 
 
 
16
  }
17
  };
18
 
 
39
  {Object.keys(playlists).length === 0 ? (
40
  <p>No saved playlists</p>
41
  ) : (
42
+ <table className="saved-playlists-table">
43
+ <thead>
44
+ <tr>
45
+ <th>Playlist Name</th>
46
+ <th>Actions</th>
47
+ </tr>
48
+ </thead>
49
+ <tbody>
50
+ {Object.entries(playlists).map(([name, tracks]) => (
51
+ <tr key={name}>
52
+ <td>{name}</td>
53
+ <td className="playlist-controls">
54
+ <button onClick={() => handlePlayPlaylist(tracks)} className="play-button">Play</button>
55
+ <button onClick={() => handleEditPlaylist(name)} className="edit-button"><FaEdit /></button>
56
+ <button onClick={() => handleDeletePlaylist(name)} className="remove-button"><FaTrash /></button>
57
+ </td>
58
+ </tr>
59
+ ))}
60
+ </tbody>
61
+ </table>
62
  )}
63
  </div>
64
  );
frontend/src/context/MusicPlayerContext.js CHANGED
@@ -13,24 +13,24 @@ export const MusicPlayerProvider = ({ children }) => {
13
  const [abortController, setAbortController] = useState(null);
14
  const [didDestroy, setDidDestroy] = useState(false);
15
 
16
- // Playlist and index
17
- const [playlist, setPlaylist] = useState([]);
18
  const [currentIndex, setCurrentIndex] = useState(0);
19
 
20
  const canPlayPrevious = currentIndex > 0;
21
- const canPlayNext = currentIndex < playlist.length - 1;
22
 
23
  const initializePlayer = async (source, title) => {
24
- // Check if song already exists in playlist
25
- const songIndex = playlist.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
- setPlaylist((prev) => [...prev, newSong]);
33
- setCurrentIndex(playlist.length); // Set index to the end (new song position)
34
  }
35
 
36
  if (abortController) {
@@ -126,17 +126,17 @@ export const MusicPlayerProvider = ({ children }) => {
126
 
127
  const togglePlayerSize = () => setIsPlayerMaximized(!isPlayerMaximized);
128
 
129
- const addToPlaylist = (song) => {
130
- const songIndex = playlist.findIndex(track => track.source === song.source);
131
  if (songIndex === -1) {
132
- setPlaylist((prev) => [...prev, song]);
133
  }
134
  };
135
 
136
- const removeFromPlaylist = (source) => {
137
- setPlaylist((prev) => prev.filter(track => track.source !== source));
138
- if (currentIndex >= playlist.length) {
139
- setCurrentIndex(playlist.length - 1);
140
  }
141
  };
142
 
@@ -144,7 +144,7 @@ export const MusicPlayerProvider = ({ children }) => {
144
  if (canPlayNext) {
145
  const nextIndex = currentIndex + 1;
146
  setCurrentIndex(nextIndex);
147
- const { source, title } = playlist[nextIndex];
148
  initializePlayer(source, title);
149
  }
150
  };
@@ -153,15 +153,15 @@ export const MusicPlayerProvider = ({ children }) => {
153
  if (canPlayPrevious) {
154
  const prevIndex = currentIndex - 1;
155
  setCurrentIndex(prevIndex);
156
- const { source, title } = playlist[prevIndex];
157
  initializePlayer(source, title);
158
  }
159
  };
160
 
161
  const playAtIndex = (index) => {
162
- if (index >= 0 && index < playlist.length) {
163
  setCurrentIndex(index);
164
- const { source, title } = playlist[index];
165
  initializePlayer(source, title);
166
  }
167
  };
@@ -176,7 +176,7 @@ export const MusicPlayerProvider = ({ children }) => {
176
  videoRef.current.removeEventListener("ended", handleEnded);
177
  }
178
  };
179
- }, [currentIndex, playlist]);
180
 
181
  return (
182
  <MusicPlayerContext.Provider
@@ -194,10 +194,10 @@ export const MusicPlayerProvider = ({ children }) => {
194
  setNowPlaying,
195
  didDestroy,
196
  setDidDestroy,
197
- playlist,
198
- setPlaylist,
199
- addToPlaylist,
200
- removeFromPlaylist,
201
  playNext,
202
  playPrevious,
203
  playAtIndex,
 
13
  const [abortController, setAbortController] = useState(null);
14
  const [didDestroy, setDidDestroy] = useState(false);
15
 
16
+ // Playqueue and index
17
+ const [playqueue, setPlayqueue] = useState([]);
18
  const [currentIndex, setCurrentIndex] = useState(0);
19
 
20
  const canPlayPrevious = currentIndex > 0;
21
+ const canPlayNext = currentIndex < playqueue.length - 1;
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) {
 
126
 
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
  }
141
  };
142
 
 
144
  if (canPlayNext) {
145
  const nextIndex = currentIndex + 1;
146
  setCurrentIndex(nextIndex);
147
+ const { source, title } = playqueue[nextIndex];
148
  initializePlayer(source, title);
149
  }
150
  };
 
153
  if (canPlayPrevious) {
154
  const prevIndex = currentIndex - 1;
155
  setCurrentIndex(prevIndex);
156
+ const { source, title } = playqueue[prevIndex];
157
  initializePlayer(source, title);
158
  }
159
  };
160
 
161
  const playAtIndex = (index) => {
162
+ if (index >= 0 && index < playqueue.length) {
163
  setCurrentIndex(index);
164
+ const { source, title } = playqueue[index];
165
  initializePlayer(source, title);
166
  }
167
  };
 
176
  videoRef.current.removeEventListener("ended", handleEnded);
177
  }
178
  };
179
+ }, [currentIndex, playqueue]);
180
 
181
  return (
182
  <MusicPlayerContext.Provider
 
194
  setNowPlaying,
195
  didDestroy,
196
  setDidDestroy,
197
+ playqueue,
198
+ setPlayqueue,
199
+ addToPlayqueue,
200
+ removeFromPlayqueue,
201
  playNext,
202
  playPrevious,
203
  playAtIndex,
frontend/src/lib/config.js CHANGED
@@ -1 +1,2 @@
1
- export const BASE_URL = "https://hans-r-d-load-balancer.hf.space";
 
 
1
+ export const BASE_URL = "https://hans-r-d-load-balancer.hf.space";
2
+ export const WEB_VERSION = "0.3 Alpha";