33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/backend/models/playlistModel.js:
--------------------------------------------------------------------------------
1 | // Import the mongoose package for defining MongoDB schemas and models
2 | const mongoose = require('mongoose');
3 |
4 | // Define a schema for the 'Playlist' collection in MongoDB
5 | const playlistSchema = new mongoose.Schema({
6 | // Define the 'name' field for the playlist with a String type
7 | name: {
8 | type: String,
9 | required: true, // The 'name' field is required
10 | },
11 | // Define the 'creator' field as a reference to a User object (ObjectId)
12 | creator: {
13 | type: mongoose.Schema.Types.ObjectId,
14 | ref: 'User', // Reference to the 'User' model
15 | },
16 | // Define the 'songs' field as an array of references to Song objects (ObjectIds)
17 | songs: [{
18 | type: mongoose.Schema.Types.ObjectId,
19 | ref: 'Song', // Reference to the 'Song' model
20 | }],
21 | playlistPhoto: String
22 | });
23 |
24 | // Create a 'Playlist' model using the defined schema
25 | const Playlist = mongoose.model('Playlist', playlistSchema);
26 |
27 | // Export the 'Playlist' model for use in other parts of the application
28 | module.exports = Playlist;
29 |
--------------------------------------------------------------------------------
/frontend/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/routes/userRoutes.js:
--------------------------------------------------------------------------------
1 | // Import necessary modules and packages
2 | const express = require('express'); // Import Express.js for routing
3 | const router = express.Router(); // Create an instance of an Express router
4 | const userController = require('../controllers/userController'); // Import the userController module
5 | const { verifyjwt, logout, verifyToken } = require("../middleware"); // Import the verifyjwt middleware for authentication
6 |
7 | // Define routes for user-related actions, including registration and login
8 | // These routes do not require authentication
9 |
10 | // Route to handle user registration
11 | router.post('/register', userController.register);
12 |
13 | // Route to handle user login
14 | router.post('/login', userController.login);
15 |
16 | // Route to get user profile information
17 |
18 | // Apply the verifyjwt middleware to the routes below to ensure authentication is required
19 | router.get('/verify',verifyjwt, verifyToken);
20 |
21 | router.get('/profile',verifyjwt, userController.profile);
22 |
23 | router.post('/logout',verifyjwt, logout);
24 | // Define routes that require authentication
25 |
26 | // Route to update user profile information
27 | router.put('/profile/update',verifyjwt, userController.updateProfile);
28 |
29 |
30 | // Export the router with defined routes for use in other parts of the application
31 | module.exports = router;
32 |
--------------------------------------------------------------------------------
/frontend/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 | @import url('https://fonts.googleapis.com/css2?family=Inria+Serif&family=Koulen&family=Roboto:wght@300&display=swap');
5 | @import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
6 |
7 | :root {
8 | --foreground-rgb: 0, 0, 0;
9 | --background-start-rgb: 214, 219, 220;
10 | --background-end-rgb: 255, 255, 255;
11 | }
12 |
13 | @media (prefers-color-scheme: dark) {
14 | :root {
15 | --foreground-rgb: 255, 255, 255;
16 | --background-start-rgb: 0, 0, 0;
17 | --background-end-rgb: 0, 0, 0;
18 | }
19 | }
20 |
21 | body {
22 | color: rgb(var(--foreground-rgb));
23 | background: linear-gradient(to bottom,
24 | transparent,
25 | rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb));
26 | }
27 |
28 | @keyframes spin-slow {
29 | 0% {
30 | transform: rotate(0deg);
31 | }
32 |
33 | 100% {
34 | transform: rotate(360deg);
35 | }
36 | }
37 |
38 | .animate-spin-slow {
39 | animation: spin-slow 7s linear infinite;
40 | }
41 |
42 | .font-inria {
43 | font-family: 'Inria Serif', serif;
44 | }
45 |
46 | .font-koulen {
47 | font-family: 'Koulen', sans-serif;
48 | }
49 |
50 | .font-roboto {
51 | font-family: 'Roboto', sans-serif;
52 | font-weight: 300;
53 | }
54 |
55 | .font-poppins {
56 | font-family: 'Poppins', sans-serif;
57 | }
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/frontend/app/page.js:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import Image from 'next/image';
3 | import Groovewave from '../public/groovewave.svg';
4 | import { FaBars } from 'react-icons/fa';
5 | import Disk from '../public/disk.svg';
6 | import Navbar from '@/components/navbar';
7 |
8 | export default function Home() {
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | Groovewave
17 |
18 |
19 | "Ride the GrooveWave: Your Sound, Your Vibe, Your Way!"
20 |
21 |
Are you ready to dive into a world of limitless music possibilities? GrooveWave is your passport to a universe of sounds, rhythms, and melodies. Whether you're a passionate music lover, an avid collector, or just looking for your next favorite song, GrooveWave has you covered
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/backend/models/songModel.js:
--------------------------------------------------------------------------------
1 | // Import the mongoose package for defining MongoDB schemas and models
2 | const mongoose = require('mongoose');
3 |
4 | // Define a schema for the 'Song' collection in MongoDB
5 | const songSchema = new mongoose.Schema({
6 | // Define the 'title' field for the song with a String type
7 | title: {
8 | type: String,
9 | required: true, // The 'title' field is required
10 | },
11 | // Define the 'artist' field for the song with a String type
12 | artist: {
13 | type: String,
14 | required: true, // The 'artist' field is required
15 | },
16 | // Define the 'genre' field for the song with a String type
17 | genre: {
18 | type: String,
19 | required: true, // The 'genre' field is required
20 | },
21 | // Define the 'audioURL' field for the song with a String type
22 | audioURL: {
23 | type: String,
24 | },
25 | // Define the 'owner' field as a reference to a User object (ObjectId)
26 | owner: {
27 | type: mongoose.Schema.Types.ObjectId,
28 | ref: 'User', // Reference to the 'User' model
29 | required: true, // The 'owner' field is required
30 | },
31 | SongPhoto: String,
32 | // Define the 'audio' field to store the audio data as a Buffer
33 | audio: {
34 | data: Buffer, // Store audio data as a Buffer
35 | contentType: String, // Store the content type (e.g., 'audio/mp3')
36 | },
37 | });
38 |
39 | // Create a 'Song' model using the defined schema
40 | const Song = mongoose.model('Song', songSchema);
41 |
42 | // Export the 'Song' model for use in other parts of the application
43 | module.exports = Song;
44 |
--------------------------------------------------------------------------------
/backend/routes/songRoutes.js:
--------------------------------------------------------------------------------
1 | // Import necessary modules and packages
2 | const express = require('express'); // Import Express.js for routing
3 | const router = express.Router(); // Create an instance of an Express router
4 | const songController = require('../controllers/songController'); // Import the songController module
5 | const { verifyjwt } = require("../middleware"); // Import the verifyjwt middleware for authentication
6 | const multer = require('multer');
7 |
8 | const storage = multer.memoryStorage();
9 | const upload = multer({ storage: storage });
10 |
11 | // Apply the verifyjwt middleware to all routes defined in this router
12 | router.use(verifyjwt);
13 |
14 | // Define various routes and their corresponding controller functions
15 | // These routes are related to managing songs
16 |
17 | // Route to get all songs
18 | router.get('/', songController.getAllSongs);
19 |
20 | // Route to get songs by name
21 | router.get('/name', songController.getSongByName);
22 |
23 | // Route to get songs by artist
24 | router.get('/artist', songController.getSongByArtist);
25 |
26 | // Route to get songs by genre
27 | router.get('/genre', songController.getSongByGenre);
28 |
29 | // Route to upload a new song
30 | router.post('/', upload.single('audio'), songController.uploadSong);
31 |
32 | // Route to delete a song by user ID and song ID
33 | router.delete('/:userId/:songId', songController.deleteSong);
34 |
35 | // Route to edit a song by song ID
36 | router.put('/editsong/:songId', songController.editSong);
37 |
38 | // Route to get songs by user ID
39 | router.get('/:userId', songController.getSongByUser);
40 |
41 | // Export the router with defined routes for use in other parts of the application
42 | module.exports = router;
43 |
--------------------------------------------------------------------------------
/backend/routes/playlistRoutes.js:
--------------------------------------------------------------------------------
1 | // Import necessary modules and packages
2 | const express = require('express'); // Import Express.js for routing
3 | const router = express.Router(); // Create an instance of an Express router
4 | const playlistController = require('../controllers/playlistController'); // Import the playlistController module
5 | const { verifyjwt } = require("../middleware"); // Import the verifyjwt middleware for authentication
6 |
7 | // Apply the verifyjwt middleware to all routes defined in this router
8 | router.use(verifyjwt);
9 |
10 | // Define various routes and their corresponding controller functions
11 | // These routes are related to managing playlists
12 |
13 | // Route to create a new playlist
14 | router.post("/", playlistController.createPlaylist);
15 |
16 | // Route to edit an existing playlist by its ID
17 | router.put("/:id", playlistController.editPlaylist);
18 |
19 | // Route to delete a playlist by its ID
20 | router.delete("/:id", playlistController.deletePlaylist);
21 |
22 | // Route to add a song to a playlist by playlist ID
23 | router.post("/:id/song", playlistController.addSongToPlaylist);
24 |
25 | // Route to remove a song from a playlist by playlist ID and song ID
26 | router.delete("/:id/delete/:songId", playlistController.removeSongFromPlaylist);
27 |
28 | // Route to find playlists by user ID
29 | router.get("/user/:userId", playlistController.findPlaylistsByUser);
30 |
31 | // Route to get all playlists
32 | router.get("/", playlistController.getAllPlaylists);
33 |
34 | // Route to get a playlist by its ID
35 | router.get("/:id", playlistController.getPlaylistbyId);
36 |
37 | // Route to get all songs from a playlist by playlist ID
38 | router.get("/:id/song", playlistController.getAllSongsFromPlaylist);
39 |
40 | // Export the router with defined routes for use in other parts of the application
41 | module.exports = router;
42 |
--------------------------------------------------------------------------------
/backend/models/userModel.js:
--------------------------------------------------------------------------------
1 | // Import the mongoose package for defining MongoDB schemas and models
2 | const mongoose = require('mongoose');
3 |
4 | // Define a schema for the 'User' collection in MongoDB
5 | const userSchema = new mongoose.Schema({
6 | // Define the 'username' field for the user with a String type
7 | username: {
8 | type: String,
9 | required: true, // The 'username' field is required
10 | unique: true, // The 'username' field must be unique
11 | },
12 | // Define the 'email' field for the user with a String type
13 | email: {
14 | type: String,
15 | required: true, // The 'email' field is required
16 | unique: true, // The 'email' field must be unique
17 | },
18 | // Define the 'password' field for the user with a String type
19 | password: {
20 | type: String,
21 | required: true, // The 'password' field is required
22 | },
23 | // Define the 'profilePicture' field for the user with an optional String type
24 | profilePicture: String,
25 | // Define the 'bio' field for the user with an optional String type
26 | bio: String,
27 | // Define the 'createdPlaylists' field as an array of references to Playlist objects (ObjectIds)
28 | createdPlaylists: [{
29 | type: mongoose.Schema.Types.ObjectId,
30 | ref: 'Playlist', // Reference to the 'Playlist' model
31 | }],
32 | // Define the 'uploadedSongs' field as an array of references to Song objects (ObjectIds)
33 | uploadedSongs: [{
34 | type: mongoose.Schema.Types.ObjectId,
35 | ref: 'Song', // Reference to the 'Song' model
36 | }]
37 | });
38 |
39 | // Create a 'User' model using the defined schema
40 | const User = mongoose.model('User', userSchema);
41 |
42 | // Export the 'User' model for use in other parts of the application
43 | module.exports = User;
44 |
--------------------------------------------------------------------------------
/backend/controllers/testmiddleware.js:
--------------------------------------------------------------------------------
1 | // import { getCookie } from "cookies-next";
2 | // import { NextResponse } from "next/server";
3 |
4 | // const authPath = ['/', '/login', '/register'];
5 | // const protectedRoutes = ['/home', '/profile'];
6 |
7 | // export async function middleware(request) {
8 | // const token = getCookie(request, 'token');
9 |
10 | // console.log({ token });
11 | // const response = await fetch("http://localhost:5000/users/verify", {
12 | // headers: new Headers({
13 | // Authorization: "Bearer " + token,
14 | // }),
15 | // });
16 |
17 | // // console.log(response);
18 | // const data = await response.json();
19 |
20 | // // console.log(data);
21 |
22 | // return NextResponse.next();
23 | // // if (!token) {
24 | // // if (authPath.includes(request.url)) {
25 | // // return NextResponse.next();
26 | // // } else {
27 | // // return NextResponse.next(null, { status: 401 });
28 | // // }
29 | // // }
30 |
31 | // // try {
32 | // // const backendURL = 'https://music-streaming-app.onrender.com/'; // Replace with the actual URL
33 | // // const response = await fetch(`${backendURL}/verify-token?token=${token}`);
34 |
35 | // // if (response.status === 200) {
36 | // // if (protectedRoutes.includes(request.url)) {
37 | // // return NextResponse.next();
38 | // // } else {
39 | // // return NextResponse.next(null, { status: 401 });
40 | // // }
41 | // // } else {
42 | // // if (authPath.includes(request.url)) {
43 | // // return NextResponse.next();
44 | // // } else {
45 | // // return NextResponse.next(null, { status: 401 });
46 | // // }
47 | // // }
48 | // // } catch (error) {
49 | // // console.error(error);
50 | // // return new NextResponse(null, { status: 500 });
51 | // // }
52 | // }
53 |
54 | // export const config = {
55 | // matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
56 | // };
--------------------------------------------------------------------------------
/backend/index.js:
--------------------------------------------------------------------------------
1 | // Import necessary packages and modules
2 | const express = require("express"); // Express.js for building the web application
3 | const cors = require('cors'); // Middleware for handling Cross-Origin Resource Sharing (CORS)
4 | const mongoose = require('mongoose'); // MongoDB ORM for database interaction
5 |
6 | const userRoutes = require('./routes/userRoutes');
7 | const songRoutes = require('./routes/songRoutes');
8 | const playlistRoutes = require('./routes/playlistRoutes');
9 |
10 | const port = process.env.PORT || 5000; // Port on which the server will run
11 | require('dotenv').config(); // Load environment variables from a .env file
12 | const app = express(); // Create an instance of the Express application
13 |
14 |
15 |
16 | // Enable CORS to allow cross-origin requests
17 | app.use(cors({
18 | origin: true, // included origin as true
19 | credentials: true,//included credentials as true
20 | }));
21 |
22 |
23 | try {
24 | // Connect to MongoDB using the URL provided in the .env file
25 | mongoose.connect(process.env.MONGO_URL, { useNewUrlParser: true, useUnifiedTopology: true });
26 |
27 | // Event handlers for MongoDB connection
28 | mongoose.connection.on('connected', () => {
29 | console.log('Connected to MongoDB');
30 | });
31 |
32 | mongoose.connection.on('error', (err) => {
33 | console.error('MongoDB connection error:', err);
34 | });
35 | } catch (err) {
36 | // Handle any errors that occur during MongoDB connection
37 | console.error('An error occurred while connecting to MongoDB:', err);
38 | }
39 |
40 | // Import routes for different parts of the application
41 |
42 |
43 | // Use routes and apply CORS middleware to specific routes
44 | app.use(express.json({ limit: "50mb" }));
45 | app.use(express.urlencoded({ extended: true, limit: "50mb" }));
46 | app.use('/users', userRoutes);
47 | app.use('/songs', songRoutes);
48 | app.use('/playlists', playlistRoutes);
49 |
50 | // Define a default route that responds with a welcome message
51 | app.use('/', (req, res) => {
52 | res.send("Welcome to our music app");
53 | })
54 |
55 | // Start the server and listen on the specified port
56 | app.listen(port, () => {
57 | console.log(`Server is running on the port ${port}`)
58 | });
59 |
--------------------------------------------------------------------------------
/frontend/components/navbar.js:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import React, { useState } from 'react'
3 | import Image from 'next/image';
4 | import Groovewave from '../public/groovewave.svg';
5 | import { FaBars } from 'react-icons/fa';
6 |
7 | const Navbar = () => {
8 | const [isMenuOpen, setIsMenuOpen] = useState(false);
9 |
10 | const toggleMenu = () => {
11 | setIsMenuOpen(!isMenuOpen);
12 | };
13 | return (
14 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to SnapGrid
2 |
3 | Welcome to the Music Streaming App project! We appreciate your interest in contributing to our open-source project. Your contributions help us improve and enhance the project for all users. Here are some guidelines to get you started:
4 |
5 | ## Contribution Guidelines
6 |
7 | - Ensure your code follows the project's coding standards and conventions.
8 | - When you're submitting a PR for a UI-related issue, it would be really awesome if you add a screenshot of your change. It makes
9 | it very easy for the reviewers and you'll also get reviews quicker.
10 | - Keep your pull request concise and focused on a single issue or feature.
11 | - Before making any PR please create an Issue
12 | - If you find any upgradation or bug in the website, you can create your own issue or work on the previous issues.
13 | - When creating PR make sure you tag the required issue using #
14 | - Provide clear and informative commit messages.
15 | - Be open to feedback and engage in discussions to improve your contribution.
16 | - Respect the project maintainers and other contributors.
17 | - Please read our [Code of Conduct](./CODE_OF_CONDUCT.md).
18 |
19 |
20 | ## Getting Started
21 |
22 | Before you begin, please make sure you have Git installed on your computer. If you don't have it installed, you can download it from [Git Downloads](https://git-scm.com/downloads).
23 |
24 | ## Cloning the Repository
25 |
26 | To start contributing, you'll need to clone the project's Git repository to your local machine. Here are the steps to do so:
27 |
28 | 1. Open your terminal or command prompt.
29 |
30 | 2. Navigate to the directory where you want to clone the repository.
31 | ```bash
32 | cd /path/to/your/directory
33 | ```
34 |
35 | 3.Clone the repository using the following command, replacing [repository URL] with the actual URL of the repository:
36 | ```
37 | git clone https://github.com/ojasaklechayt/Music-Streaming-App.git
38 | ```
39 |
40 | ## Configuration
41 |
42 | If this is your first time using Git on your computer, you may need to configure your Git user information. You can do this using the following commands, replacing [Your Name] and [youremail@example.com] with your actual name and email address:
43 | ```
44 | git config --global user.name "Your Name"
45 | git config --global user.email "youremail@example.com"
46 | ```
47 |
48 | ## Making Changes
49 |
50 | Now that you have the repository cloned and configured, you can start making changes to the project. Here are some basic Git commands to get you started:
51 |
52 | **git add .**: Stage changes for commit.
53 | **git commit -m "Your commit message"**: Commit staged changes with a message.
54 | **git push origin master**: Push changes to the remote repository.
55 |
56 |
57 | ## Submitting Your Changes
58 | After making your changes, you can submit your contributions by creating a pull request. Please refer to our Pull Request Guidelines for details on how to do this.
59 |
60 |
61 | Please make sure to follow our Contribution Guidelines and review the Code of Conduct to ensure a positive and inclusive community.
62 | **Happy Coding!!**
--------------------------------------------------------------------------------
/backend/controllers/userController.js:
--------------------------------------------------------------------------------
1 | // Import necessary models and packages
2 | const User = require("../models/userModel");
3 | const bcrypt = require("bcrypt");
4 | const { generatejwt } = require("../middleware");
5 | // Controller functions for user-related actions
6 |
7 | // Register a new user
8 | exports.register = async (req, res) => {
9 | try {
10 | const { username, email, password } = req.body;
11 | const saltRounds = 10;
12 |
13 | // Check if a user with the provided email already exists
14 | const existingUser = await User.findOne({ email });
15 | if (existingUser) {
16 | return res.status(400).json({ message: "User already exists" });
17 | }
18 |
19 | // Generate a salt for password hashing
20 | const salt = await bcrypt.genSalt(saltRounds);
21 |
22 | // Hash the provided password using the salt
23 | const encryptedPassword = await bcrypt.hash(password, salt);
24 |
25 | // Create a new user document with the provided data
26 | const newUser = new User({
27 | username,
28 | email,
29 | password: encryptedPassword,
30 | });
31 |
32 | // Save the new user to the database
33 | await newUser.save();
34 |
35 | res
36 | .status(201)
37 | .json({ message: "User registered successfully", user: newUser });
38 | } catch (error) {
39 | console.error("Error registering user: ", error);
40 | res.status(500).json({ message: "Registration failed" });
41 | }
42 | };
43 |
44 | // User login
45 | exports.login = async (req, res) => {
46 | try {
47 | const { email, password } = req.body;
48 |
49 | // Find a user with the provided email
50 | const user = await User.findOne({ email });
51 | if (!user) {
52 | return res.status(401).json({ message: "User Not Found" });
53 | }
54 |
55 | // Compare the provided password with the hashed password in the database
56 | const check = await bcrypt.compare(password, user.password);
57 | if (!check) {
58 | return res.status(401).json({ message: "Inavalid Password" });
59 | }
60 |
61 | const token = await generatejwt(user._id);
62 |
63 | res.status(200).json({ status: "success", token });
64 | // res.status(200).json({message:"Successfull"});
65 | } catch (error) {
66 | console.error("Error Login User: ", error);
67 | res.status(500).json({ message: "Login failed" });
68 | }
69 | };
70 |
71 | // Fetch user profile
72 | exports.profile = async (req, res) => {
73 | try {
74 | const _Id = req.user._id;
75 |
76 | // Find the user by their ID
77 | const user = await User.findOne({ _id: _Id });
78 |
79 | if (!user) {
80 | res.status(404).json({ message: "User Not Found!!" });
81 | }
82 |
83 | res.status(200).json({ user });
84 | } catch (error) {
85 | console.error("Error Fetching Profile: ", error);
86 | res.status(500).json({ message: "Profile Retrieval Failed" });
87 | }
88 | };
89 |
90 | // Update user profile
91 | exports.updateProfile = async (req, res) => {
92 | try {
93 | const { username, profilePicture, bio } = req.body;
94 | const userId = req.user._id;
95 |
96 | // Find the user by their ID
97 | const user = await User.findOne({ _id: userId });
98 |
99 | if (!user) {
100 | res.status(404).json({ message: "User Not Found" });
101 | }
102 |
103 | // Update user data if provided
104 | if (username) user.username = username;
105 | if (profilePicture) user.profilePicture = profilePicture;
106 | if (bio) user.bio = bio;
107 |
108 | // Save the updated user data
109 | await user.save();
110 |
111 | res.status(200).json({ message: "Profile Updated Successfully", user });
112 | } catch (error) {
113 | console.error("Error Updating Profile: ", error);
114 | res.status(500).json({ message: "Profile Updation Failed" });
115 | }
116 | };
117 |
--------------------------------------------------------------------------------
/backend/middleware.js:
--------------------------------------------------------------------------------
1 | // Import necessary modules and packages
2 | const Token = require('./models/tokenModel'); // Import the Token model for working with tokens
3 | const jwt = require("jsonwebtoken"); // Import the JSON Web Token (JWT) package
4 | const catchAsync = require("./utils/catchAsync"); // Import the catchAsync utility function
5 | // Function to generate a JWT token for a user and set it in a cookie
6 | const generatejwt = async (id) => {
7 | // Extract user information like _id and email
8 |
9 | // Create a JWT token with user data and a secret key, set to expire in 2 hours
10 | const token = jwt.sign(
11 | { id },
12 | process.env.TOKEN_KEY, // Use the TOKEN_KEY from environment variables as the secret key
13 | {
14 | expiresIn: "2h", // Token expires in 2 hours
15 | }
16 | );
17 |
18 | // Set the token in a cookie named "token"
19 |
20 | // Create a new Token record in the database to keep track of tokens
21 | const tokenRecord = new Token({ token });
22 |
23 | // Save the token record to the database
24 | await tokenRecord.save();
25 |
26 | return token
27 |
28 | }
29 |
30 | // Function to clear the "token" cookie on the client side
31 | const clearTokenCookie = (res) => {
32 | res.clearCookie("token");
33 | };
34 |
35 | // Function to log out the user by clearing the token on both client and server sides
36 | const logout = async (req, res) => {
37 | try {
38 | const token = req.cookies.token;
39 |
40 | if (!token) {
41 | return res.status(200).json({ message: "Logged out successfully" });
42 | }
43 |
44 | // Delete the token from the database (if it exists)
45 | await Token.findOneAndDelete({ token });
46 |
47 | // Clear the token cookie on the client side
48 | clearTokenCookie(res);
49 |
50 | res.status(200).json({ message: "Logged out successfully" });
51 | } catch (error) {
52 | console.error("Logout error:", error);
53 | res.status(500).json({ message: "Internal server error" });
54 | }
55 | };
56 |
57 | const verifyToken = catchAsync(async (req, res, next) => {
58 | const bearer = req.cookies.token;
59 |
60 | if (!bearer) return res.status(401).json({ message: "You are not Authenticated" });
61 |
62 | try {
63 | const authData = jwt.verify(bearer, process.env.TOKEN_KEY);
64 |
65 | return res.status(200).json({
66 | status: "success",
67 | data: { authData },
68 | });
69 |
70 | } catch (error) {
71 | return res.status(401).json({
72 | status: "failure",
73 | data: { error },
74 | });
75 | }
76 | });
77 |
78 | // Middleware function to verify the JWT token from a cookie and authenticate the user
79 | const verifyjwt = async (req, res, next) => {
80 | try {
81 | // Extract the JWT token from the "token" cookie
82 | const token = req.cookies.token;
83 |
84 | // If no token is found, return an Unauthorized response (HTTP 401)
85 | if (!token) {
86 | return res.status(401).json({ message: "Unauthorized" });
87 | }
88 |
89 | // Verify the token using the secret key (TOKEN_KEY) from environment variables
90 | const decoded = jwt.verify(token, process.env.TOKEN_KEY);
91 |
92 | // Check if the token exists in the database (previously generated by generatejwt)
93 | const tokenRecord = await Token.findOne({ token });
94 |
95 | // If the token does not exist in the database, return Unauthorized
96 | if (!tokenRecord) {
97 | return res.status(401).json({ message: "You are not Loggedin please Login" });
98 | }
99 |
100 | // Attach the decoded user data to the request object for further use
101 | req.user = decoded;
102 |
103 | // Continue to the next middleware or route
104 | next();
105 | } catch {
106 | return res.status(401).json({ message: "Unauthorized" });
107 | }
108 | };
109 |
110 | // Export the generatejwt and verifyjwt functions for use in other parts of the application
111 | module.exports = { generatejwt, verifyjwt, logout, verifyToken };
112 |
--------------------------------------------------------------------------------
/backend/README.md:
--------------------------------------------------------------------------------
1 | # Music-Streaming-App
2 |
3 | ## Backend for Music Streaming App
4 |
5 | Welcome to the backend section for Music Streaming App! This repository contains the server-side code responsible for handling user authentication, managing playlists, and serving music-related data. This README will guide you through setting up the backend code and provide an overview of its structure and functionality.
6 |
7 | ### Table of Contents
8 | - [Prerequisites](#prerequisites)
9 | - [Getting Started](#getting-started)
10 | - [Folder Structure](#folder-structure)
11 | - [Environment Variables](#environment-variables)
12 | - [Middleware](#middleware)
13 | - [Routes](#routes)
14 | - [Controllers](#controllers)
15 | - [Models](#models)
16 | - [Database](#database)
17 | - [Authentication](#authentication)
18 | - [Endpoints](#endpoints)
19 | - [Contributing](#contributing)
20 | - [License](#license)
21 |
22 | ### Prerequisites
23 | Before setting up the backend, make sure you have the following software installed on your system:
24 | - Node.js (v14.x or higher)
25 | - MongoDB (v4.x or higher)
26 | - Git (optional, but recommended for version control)
27 |
28 | ### Getting Started
29 | Follow these steps to get the backend up and running on your local machine:
30 |
31 | 1. Clone the repository:
32 | ```bash
33 | git clone https://github.com/your-username/Music-Streaming-App.git
34 | ```
35 |
36 | 2. Navigate to the project directory:
37 | ```bash
38 | cd Music-Streaming-App
39 | ```
40 |
41 | 3. Install dependencies:
42 | ```bash
43 | npm install
44 | ```
45 |
46 | 4. Create a `.env` file in the root directory and set the necessary environment variables (see [Environment Variables](#environment-variables)).
47 |
48 | 5. Start the server:
49 | ```bash
50 | npm start
51 | ```
52 |
53 | 6. Your backend server should now be running on `http://localhost:5000`.
54 |
55 | ### Folder Structure
56 | The backend code is organized into the following main directories:
57 |
58 | - **middleware**: Contains custom middleware functions used for authentication and request handling.
59 | - **models**: Defines the data models (e.g., User, Song, Playlist) using Mongoose.
60 | - **routes**: Contains route definitions for different API endpoints.
61 | - **controllers**: Implements the logic for handling HTTP requests.
62 | - **config**: Houses configuration files, if any.
63 | - **uploads**: Stores uploaded files (e.g., song audio files).
64 | - **tests**: Contains test files and configurations (if testing is implemented).
65 |
66 | ### Environment Variables
67 | To run the backend code successfully, you need to set the following environment variables in your `.env` file:
68 |
69 | - `MONGODB`: MongoDB connection URI.
70 | - `TOKEN_KEY`: Secret key for JWT (JSON Web Token) generation.
71 | - (Additional variables based on your specific configuration, if any.)
72 |
73 | Here's an example `.env` file:
74 |
75 | ```env
76 | MONGODB=mongodb://localhost:27017/your-music-app
77 | TOKEN_KEY=your-secret-key
78 | ```
79 |
80 | ### Middleware
81 | The `middleware` directory contains functions responsible for various tasks, such as user authentication (`verifyjwt`), handling cookies, and error handling. Middleware functions are used in the route definitions to add specific functionality to the routes.
82 |
83 | ### Routes
84 | Routes define the API endpoints and their associated middleware and controller functions. Each route file is responsible for a specific set of related endpoints (e.g., user routes, song routes, playlist routes).
85 |
86 | ### Controllers
87 | Controllers implement the logic for handling HTTP requests. They interact with the data models and services to perform actions such as user registration, song upload, playlist management, and more.
88 |
89 | ### Models
90 | The `models` directory defines the data schemas using Mongoose. Each model (e.g., User, Song, Playlist) corresponds to a collection in the MongoDB database and provides methods for interacting with that data.
91 |
92 | ### Database
93 | The backend uses MongoDB as its database. Make sure you have MongoDB installed and running. You can configure the database connection URI in the `.env` file.
94 |
95 | ### Authentication
96 | The backend uses JWT (JSON Web Tokens) for user authentication. When a user registers or logs in, a token is generated and stored in a cookie for subsequent authentication on protected routes.
97 |
98 | ### Endpoints
99 | The backend provides various endpoints for user registration, authentication, song management, playlist management, and more. Each endpoint is documented in the route files, and you can test them using tools like Postman or by integrating them with your frontend.
100 |
101 | ### Contributing
102 | Feel free to contribute to the development of the backend by submitting issues, feature requests, or pull requests. Follow the standard Git branching workflow and maintain a clean and well-documented codebase.
103 |
104 | ### License
105 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
106 |
107 | Thank you for using this backend code for my Music App! If you have any questions or need further assistance, please don't hesitate to reach out to the maintainers of this repository.
108 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Groovewave
6 |
Ride the GrooveWave: Your Sound, Your Vibe, Your Way!
41 |
42 |
43 |
44 | ## Table of Contents
45 | - [Groovewave Music Streaming App](#groovewave-music-streaming-app)
46 | - [Tech Stack](#tech-stack)
47 | - [Getting Started](#getting-started)
48 | - [Installation](#installation)
49 | - [Backend Setup](#backend-setup)
50 | - [Frontend Setup](#frontend-setup)
51 | - [Deployment](#deployment)
52 | - [Hacktoberfest](#hacktoberfest)
53 | - [Contributing](#contributing)
54 | - [Contributors](#contributors)
55 | - [License](#license)
56 |
57 |
58 | # Groovewave Music Streaming App
59 |
60 | Our music streaming app redefines how you enjoy and discover music.
61 |
62 | 
63 |
64 |
65 | ## Tech Stack
66 | - Front-End:
67 | * 
68 | * 
69 | - Linting and Code Quality:
70 | * 
71 | - Backend:
72 | * 
73 | * 
74 | * 
75 |
76 | ## Getting Started
77 |
78 | ### Installation
79 | 1. Fork the repository
80 |
81 | 2. Clone your forked copy of the project
82 | ```
83 | git clone https://github.com//Music-Streaming-App.git
84 | ```
85 | 3. Navigate to the project directory
86 | ```
87 | cd Music-Streaming-App
88 | ```
89 |
90 | ### Backend Setup
91 | For the backend, navigate to the "backend" directory:
92 |
93 | ```bash
94 | cd backend
95 | npm install
96 | npm run server
97 | ```
98 |
99 | ### Frontend Setup
100 | To set up the frontend, navigate to the "frontend" directory:
101 |
102 | ```bash
103 | cd frontend
104 | npm install
105 | npm run dev
106 | ```
107 | ### Deployment
108 |
109 | You can deploy this project to any static site hosting service.
110 |
111 | Some of the popular ones are:
112 |
113 | - [Vercel](https://vercel.com/) (Recommended for static sites)
114 | - [Netlify](https://www.netlify.com/)
115 | - [Railway](https://railway.app/) (Ideal for full-stack MERN applications)
116 |
117 | ## Hacktoberfest
118 |
119 | 1. Star the repo if you like it. ⭐
120 | 2. Please read the [rules](https://hacktoberfest.com/participation/) before opening a pull request.
121 | 3. The maintainer(s) will add the `hacktoberfest-accepted` label after reviewing and accepting your pull request.
122 | 4. You can also create your own issue if you have something in mind.
123 | 5. Low quality PRs will not be merged
124 |
125 | ## Contributing
126 |
127 | [Contributing Guide](https://github.com/ojasaklechayt/Music-Streaming-App/blob/main/CONTRIBUTING.md)
128 |
129 | Feel free to open an issue if you find a bug or want to suggest a feature.
130 |
131 |
132 | ## Contributors
133 |
134 |
135 |
136 |
137 |
138 | Made with [contrib.rocks](https://contrib.rocks).
139 |
140 | ## License
141 |
142 | [CCO License](LICENSE)
143 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | .
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
--------------------------------------------------------------------------------
/frontend/app/login/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useState } from "react";
3 | import { ToastContainer, toast } from "react-toastify";
4 | import "react-toastify/dist/ReactToastify.css";
5 | import Navbar from "@/components/navbar";
6 | import Abstract from "../../public/abstract_figures.png";
7 | import Link from "next/link"; // Make sure to import Link
8 | import Image from "next/image";
9 | import axios from "axios";
10 | import { useRouter } from "next/navigation";
11 | import { Cookie } from "next/font/google";
12 | import Cookies from "js-cookie";
13 | export default function Login() {
14 | const router = useRouter();
15 | const [formData, setFormData] = useState({
16 | email: "",
17 | password: "",
18 | });
19 |
20 | const handleInputChange = (e) => {
21 | const { name, value } = e.target;
22 | setFormData({
23 | ...formData,
24 | [name]: value,
25 | });
26 | };
27 |
28 | const handleLogin = async (e) => {
29 | e.preventDefault();
30 |
31 | try {
32 | const response = await axios.post(
33 | "https://music-streaming-app.onrender.com/users/login",
34 | formData,
35 | {
36 | withCredentials: true,
37 | headers: {
38 | "Access-Control-Allow-Origin": "*",
39 | "Content-Type": "application/json",
40 | },
41 | credentials: "include",
42 | }
43 | );
44 | console.log("Login successful:", response);
45 |
46 | const { token } = response.data;
47 | if (token) {
48 | toast.success("User Login successfully");
49 |
50 | // Set the token in a cookie
51 | Cookies.set("token", token, { expires: 2 / 24 });
52 |
53 | // Redirect to the home page
54 | router.push("/home");
55 | }
56 | } catch (error) {
57 | if (error.response) {
58 | if (error.response.status === 401) {
59 | toast.error(error.response.data.message);
60 | } else {
61 | toast.error("User Login failed");
62 | console.error("Login failed:", error);
63 | }
64 | } else {
65 | toast.error("An error occurred while logging in.");
66 | console.error("Login failed:", error);
67 | }
68 | }
69 | };
70 |
71 | return (
72 |
171 | );
172 | }
173 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/backend/controllers/songController.js:
--------------------------------------------------------------------------------
1 | // Import necessary models
2 | const Song = require('../models/songModel');
3 | const User = require('../models/userModel');
4 | const fs = require('fs');
5 | const path = require('path');
6 | // Controller functions for song-related actions
7 |
8 | // Get all songs
9 | exports.getAllSongs = async (res) => {
10 | try {
11 | // Find all songs in the database
12 | const songs = await Song.find();
13 | res.status(200).json({ songs });
14 | } catch (error) {
15 | console.error('Error Fetching All The Songs: ', error);
16 | res.status(500).json({ message: "Error Fetching All The Songs" })
17 | }
18 | }
19 |
20 | // Get a song by name
21 | exports.getSongByName = async (req, res) => {
22 | try {
23 | const { name } = req.body;
24 |
25 | // Find a song with the provided name
26 | const song = await Song.findOne({ name });
27 |
28 | if (!song) {
29 | return res.status(404).json({ song: [] });
30 | }
31 | res.status(200).json({ song });
32 | } catch (error) {
33 | console.error("Error Fetching Specific Songs by Name: ", error);
34 | res.status(500).json({ message: "Error Fetching Specific Songs by Name" });
35 | }
36 | }
37 |
38 | // Upload a new song in form-data format
39 | exports.uploadSong = async (req, res) => {
40 | try {
41 | const { title, artist, genre, owner } = req.body;
42 |
43 | // Find the user who is the owner of the song
44 | const user = await User.findById(owner);
45 |
46 | if (!user) {
47 | return res.status(404).json({ message: "User Not Found" });
48 | }
49 |
50 | const audioData = req.files.audio.data;
51 | const audioContentType = req.files.audio.mimetype;
52 |
53 | // Create a new song document with the provided data and audio
54 | const newSong = new Song({
55 | title,
56 | artist,
57 | genre,
58 | owner,
59 | audio: {
60 | data: audioData,
61 | contentType: audioContentType,
62 | },
63 | });
64 |
65 | // Save the new song to the database
66 | await newSong.save();
67 |
68 | // Update the user's uploaded songs list
69 | user.uploadedSongs.push(newSong._id);
70 | await user.save();
71 |
72 | res.status(201).json({ message: "Song Uploaded Successfully", song: newSong, user: user });
73 | } catch (error) {
74 | console.error("Error Uploading Data: ", error);
75 | res.status(500).json({ message: "Error Uploading Data" });
76 | }
77 | }
78 |
79 | // Get songs by artist
80 | exports.getSongByArtist = async (req, res) => {
81 | try {
82 | const { Artist } = req.query;
83 |
84 | // Find songs with the provided artist name
85 | const song = await Song.find({ Artist });
86 |
87 | if (!song) {
88 | return res.status(404).json({ song: [] });
89 | }
90 | res.status(200).json({ song });
91 | } catch (error) {
92 | console.error("Error Fetching Specific Songs by Artist: ", error);
93 | res.status(500).json({ message: "Error Fetching Specific Songs by Artist" });
94 | }
95 | }
96 |
97 | // Get songs by genre
98 | exports.getSongByGenre = async (req, res) => {
99 | try {
100 | const { Genre } = req.body;
101 |
102 | // Find songs with the provided genre
103 | const song = await Song.find({ Genre });
104 |
105 | if (!song || song.length === 0) {
106 | return res.status(404).json({ song: [] });
107 | }
108 |
109 | res.status(200).json({ song });
110 | } catch (error) {
111 | console.error("Error Fetching Specific Songs by Genre: ", error);
112 | res.status(500).json({ message: "Error Fetching Specific Songs by Genre" });
113 | }
114 | }
115 |
116 | // Delete a song
117 | exports.deleteSong = async (req, res) => {
118 | try {
119 | const userId = req.params.userId;
120 | const songID = req.params.songId;
121 |
122 | // Find the song by its ID
123 | const song = await Song.findById(songID);
124 |
125 | if (!song) {
126 | return res.status(404).json({ message: "Song Not Found" });
127 | }
128 |
129 | // Find the user by their ID
130 | const user = await User.findById(userId);
131 | if (!user) {
132 | return res.status(404).json({ message: "User Not Found" });
133 | }
134 |
135 | // Check if the user is the owner of the song
136 | if (song.owner.toString() !== userId.toString()) {
137 | return res.status(403).json("Unauthorized: You are not the owner of this song");
138 | }
139 |
140 | // Remove the song from the user's uploaded songs list
141 | user.uploadedSongs.pull(songID);
142 | await user.save();
143 |
144 | // Delete the song from the database
145 | await Song.deleteOne({ _id: songID });
146 |
147 | res.status(204).end({ message: "Song Deleted Successfully!!" });
148 | console.log("Song Deleted Successfully!!");
149 |
150 | } catch (error) {
151 | console.error("Song Deletion Error: ", error);
152 | res.status(500).json({ message: "Song Deletion Error" });
153 | }
154 | }
155 |
156 | exports.editSong = async (req, res) => {
157 | try {
158 | const songId = req.params.songId;
159 | const { title, artist, genre, SongPhoto } = req.body;
160 | const userId = req.body.userId;
161 |
162 | // Find the song by its ID
163 | const song = await Song.findById(songId);
164 |
165 | if (!song) {
166 | return res.status(404).json({ message: "Song Not Found" });
167 | }
168 |
169 | // Check if the user is the owner of the song
170 | if (song.owner.toString() !== userId) {
171 | return res.status(403).json({ message: "Unauthorized: You are not the owner of this song" });
172 | }
173 |
174 | // Convert the base64-encoded image data to a buffer
175 | const imageBuffer = Buffer.from(SongPhoto, 'base64');
176 |
177 | // Generate a unique file name
178 | const uniqueFileName = `${songId}-${Date.now()}.jpg`;
179 |
180 | // Define the file path
181 | const filePath = path.join(__dirname, 'uploads', uniqueFileName);
182 |
183 | // Write the image data to the file
184 | fs.writeFileSync(filePath, imageBuffer);
185 |
186 | // Update the song data with the file path
187 | const updatedSong = await Song.findByIdAndUpdate(songId, { title, artist, genre, SongPhoto: filePath }, { new: true });
188 |
189 | res.status(200).json(updatedSong);
190 | console.log("Song Edited Successfully!!");
191 | } catch (error) {
192 | console.error("Error Editing Song: ", error);
193 | res.status(500).json({ message: "Song Editing Error" });
194 | }
195 | }
196 |
197 |
198 | // Get songs by user
199 | exports.getSongByUser = async (req, res) => {
200 | try {
201 | const userId = req.params.userId;
202 |
203 | // Find songs owned by the user
204 | const song = await Song.find({ owner: userId });
205 |
206 | if (!song) {
207 | return res.status(404).json({ message: "No Songs Found" });
208 | }
209 |
210 | res.status(200).json(song);
211 | console.log("Songs Found");
212 | } catch (error) {
213 | console.error("Error Editing Song: ", error);
214 | res.status(500).json({ message: "Song Editing Error" });
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/frontend/components/Player.js:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import React, { useState, useEffect } from 'react';
3 | import { FaPlay, FaPause, FaStepBackward, FaStepForward, FaVolumeUp, FaVolumeMute } from 'react-icons/fa';
4 |
5 | const imagepath = "https://i1.sndcdn.com/artworks-ykhavMzzmobOTXYI-vm7RSg-t500x500.jpg";
6 |
7 | const Player = () => {
8 | const [isPlaying, setIsPlaying] = useState(false);
9 | const [paused, setPaused] = useState(true);
10 | const [currentSongIndex, setCurrentSongIndex] = useState(0);
11 | const [changingSong, setChangingSong] = useState(false);
12 | const [volume, setVolume] = useState(50); // Initial volume (0-100)
13 | const [currentTime, setCurrentTime] = useState(0);
14 | const [songDuration, setSongDuration] = useState(30000); // Simulated song duration in milliseconds
15 | const [playbackPosition, setPlaybackPosition] = useState(0);
16 |
17 | // Dummy list of songs
18 | const songs = [
19 | {
20 | title: 'Song 1',
21 | artist: 'Artist 1',
22 | url: '/song1.mp3',
23 | },
24 | {
25 | title: 'Song 2',
26 | artist: 'Artist 2',
27 | url: '/song2.mp3',
28 | },
29 | {
30 | title: 'Song 3',
31 | artist: 'Artist 3',
32 | url: '/song3.mp3',
33 | },
34 | ];
35 |
36 | const playPause = () => {
37 | if (paused) {
38 | // Start playing from the beginning
39 | setIsPlaying(true);
40 | setPaused(false);
41 | setCurrentTime(0); // Reset currentTime
42 | } else {
43 | // Pause the audio and store the current playback position
44 | setIsPlaying(false);
45 | setPaused(true);
46 | setPlaybackPosition(currentTime);
47 | }
48 | };
49 |
50 | const previousSong = () => {
51 | if (currentSongIndex > 0) {
52 | setChangingSong(true);
53 | setTimeout(() => {
54 | setCurrentSongIndex(currentSongIndex - 1);
55 | setCurrentTime(0); // Reset currentTime
56 | setChangingSong(false);
57 | // Add logic to play the previous song here
58 | }, 500); // Simulated delay for the transition
59 | }
60 | };
61 |
62 | const nextSong = () => {
63 | if (currentSongIndex < songs.length - 1) {
64 | setChangingSong(true);
65 | setTimeout(() => {
66 | setCurrentSongIndex(currentSongIndex + 1);
67 | setCurrentTime(0); // Reset currentTime
68 | setChangingSong(false);
69 | // Add logic to play the next song here
70 | }, 500); // Simulated delay for the transition
71 | }
72 | };
73 |
74 | const handleVolumeChange = (e) => {
75 | setVolume(e.target.value);
76 | // Set the volume of the audio element here
77 | };
78 |
79 | const isMuted = volume === 0; // Check if volume is muted
80 |
81 | useEffect(() => {
82 | if (isPlaying && !paused) {
83 | if (currentTime < songDuration) {
84 | const interval = setInterval(() => {
85 | if (isPlaying && !paused) {
86 | setCurrentTime((prevTime) => {
87 | if (prevTime + 100 <= songDuration) {
88 | return prevTime + 100;
89 | } else {
90 | setIsPlaying(false);
91 | setPaused(true);
92 | setCurrentTime(0);
93 | if (currentSongIndex < songs.length - 1) {
94 | nextSong();
95 | }
96 | return prevTime;
97 | }
98 | });
99 | }
100 | }, 100);
101 |
102 | return () => {
103 | clearInterval(interval);
104 | };
105 | }
106 | }
107 | }, [currentSongIndex, isPlaying, paused, currentTime, nextSong, songDuration, songs]);
108 |
109 | useEffect(() => {
110 | if (isPlaying && !paused) {
111 | // When resuming, set the current time to the playback position
112 | setCurrentTime(playbackPosition);
113 | }
114 | }, [paused, playbackPosition, isPlaying]);
115 |
116 | return (
117 |