-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
11 changed files
with
1,232 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"name": "music-api", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"start": "node src/app.js" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"dotenv": "^16.4.5", | ||
"express": "^4.19.2", | ||
"spotify-web-api-node": "^5.0.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
body{ | ||
box-sizing: border-box; | ||
background-image: url('../img/search-background1.jpg'); | ||
background-size: cover; | ||
background-repeat: no-repeat; | ||
background-position: 0 -50px; | ||
/* backdrop-filter: contrast(50%) brightness(80%); */ | ||
margin: 0; | ||
height: 88vh; | ||
} | ||
.enterp{ | ||
text-align: left; | ||
color: white; | ||
padding-left: 8%; | ||
} | ||
.search-container{ | ||
font-family: Arial, Helvetica, sans-serif; | ||
width: 60%; | ||
margin: 3% auto; | ||
text-align: center; | ||
padding: 5% 6%; | ||
backdrop-filter: blur(3px) contrast(50%) brightness(70%); | ||
height: 77%; | ||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); | ||
} | ||
.msg-box{ | ||
width: 55%; | ||
margin: 3% auto; | ||
backdrop-filter: blur(3px); | ||
color: white; | ||
} | ||
input{ | ||
width: 40vw; | ||
height: 7vh; | ||
margin: 0 5% 2% 0; | ||
font-size: 17px; | ||
text-indent: 10px; | ||
outline: none; | ||
border: none; | ||
background-color: rgb(238, 236, 236); | ||
} | ||
button{ | ||
height: 7vh; | ||
width: 7rem; | ||
cursor: pointer; | ||
background-color: rgb(81, 133, 231); | ||
border: none; | ||
color: white; | ||
font-size: 17px; | ||
} | ||
img{ | ||
height: 200px; | ||
width: 220px; | ||
object-fit: cover; | ||
} | ||
.hidden{ | ||
display: none; | ||
} | ||
.flex { | ||
display: flex; | ||
} | ||
.first-para{ | ||
margin-bottom: 0; | ||
width: 66%; | ||
text-align: right; | ||
} | ||
.icon{ | ||
margin-top: 6px; | ||
margin-left: 15px; | ||
height: 18px; | ||
width: 32px; | ||
border: 1px solid; | ||
padding: 7px 0; | ||
border-radius: 50%; | ||
color: white; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
body{ | ||
box-sizing: border-box; | ||
background-color: rgb(200, 200, 200); | ||
} | ||
.login-container{ | ||
width: 40%; | ||
height: 200px; | ||
margin: auto; | ||
background-color: rgb(97, 149, 245); | ||
font-family: Arial, Helvetica, sans-serif; | ||
padding: 6%; | ||
color: rgb(38, 38, 38); | ||
text-align: center; | ||
margin-top: 8%; | ||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); | ||
} | ||
.login-container button{ | ||
width: 110px; | ||
height: 45px; | ||
cursor: pointer; | ||
margin-top: 35px; | ||
font-size: 16px; | ||
border: none; | ||
} | ||
.login-container button:hover{ | ||
color: rgb(54, 121, 245); | ||
text-decoration: underline; | ||
font-weight: bold; | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Music API</title> | ||
<link rel ="stylesheet" href = "/css/style.css"> | ||
<link rel = 'icon' href="/img/favicon.png"> | ||
</head> | ||
<body> | ||
<div class = 'login-container'> | ||
<p class = 'loginp'>Welcome to the music app! <br><br>Login with your Spotify account to continue!</p> | ||
<a href = '/login'><button>Login</button></a> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
const searchForm = document.querySelector('form') | ||
const searchItem = document.querySelector('input') | ||
|
||
const messageOne = document.querySelector('#message-1') | ||
const messageTwo = document.querySelector('#message-2') | ||
const messageThree = document.querySelector('#message-3') | ||
const messageFour = document.querySelector('#message-4') | ||
const messageFive = document.querySelector('#message-5') | ||
const messageSong = document.querySelector('#song-image') | ||
const play = document.querySelector('#icon') | ||
let songUri = '' | ||
|
||
searchForm.addEventListener('submit', (e) => { | ||
//set this to prevent browser from refreshing on form submission | ||
e.preventDefault() | ||
|
||
const song = searchItem.value | ||
messageOne.textContent = 'Loading...' | ||
messageTwo.textContent = '' | ||
messageThree.textContent = '' | ||
messageFour.textContent = '' | ||
messageFive.textContent = '' | ||
messageSong.classList.add('hidden') | ||
play.classList.add('hidden') | ||
|
||
fetch('/search?q='+song).then((response) => { | ||
response.json().then((data) => { | ||
if (data.error){ | ||
messageOne.classList.remove('first-para') | ||
messageOne.textContent = data.error | ||
} | ||
else{ | ||
messageOne.classList.add('first-para') | ||
messageOne.textContent = 'Your song is : '+data.name | ||
play.classList.remove('hidden') | ||
sungByMsg = '' | ||
artist = data.artists | ||
|
||
for (i = 0; i < artist.length; i++ ){ | ||
sungByMsg += artist[i].name | ||
if (i < artist.length-1) | ||
sungByMsg += ', ' | ||
} | ||
messageTwo.textContent = 'Movie Name : '+data.album.name | ||
messageThree.textContent = 'Song by : '+ sungByMsg | ||
messageFour.textContent = 'Composed by : '+data.album.artists[0].name | ||
messageFive.textContent = 'Duration : '+ formatDuration(data.duration_ms) | ||
messageSong.src = data.album.images[0].url | ||
messageSong.classList.remove('hidden') | ||
|
||
//Store song uri | ||
songUri = data.uri | ||
} | ||
}) | ||
}) | ||
}) | ||
|
||
function formatDuration(milliseconds) { | ||
// Convert milliseconds to seconds | ||
const totalSeconds = Math.floor(milliseconds / 1000); | ||
|
||
// Calculate minutes and seconds | ||
const minutes = Math.floor(totalSeconds / 60) | ||
const seconds = totalSeconds % 60 | ||
|
||
// Construct the formatted string | ||
var formattedDuration = '' | ||
if (minutes > 0) { | ||
formattedDuration += minutes + 'min ' | ||
} | ||
if (seconds > 0) { | ||
formattedDuration += seconds + 'sec' | ||
} | ||
|
||
return formattedDuration.trim() | ||
} | ||
|
||
play.addEventListener('click', (e) => { | ||
e.preventDefault() // Prevent default button behavior | ||
window.location.href = '/play?uri=' + encodeURIComponent(songUri) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Music API</title> | ||
<link rel = 'icon' href="/img/favicon.png"> | ||
|
||
</head> | ||
<body> | ||
<p>Playback started! Enjoy!!!! 😃</p> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Music API</title> | ||
<link rel ="stylesheet" href = "/css/search.css"> | ||
<link rel = 'icon' href="/img/favicon.png"> | ||
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" | ||
integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" | ||
crossorigin="anonymous" referrerpolicy="no-referrer" /> <!--for font awesome icons--> | ||
|
||
</head> | ||
<body> | ||
<div class = 'search-container'> | ||
<p class = 'enterp'>Enter song to search:</p> | ||
<form> | ||
<input placeholder="Search a song"> | ||
<button>Search</button> | ||
</form> | ||
<div class="msg-box"> | ||
<img id = 'song-image' src = '' alt = 'song-image' class = 'hidden'> | ||
<div id = 'flex' class="flex"> | ||
<p id = 'message-1' class="first-para"></p> | ||
<a href = '' class = 'hidden icon' id = 'icon'> | ||
<i class="fa-solid fa-play"></i> | ||
</a> | ||
</div> | ||
<p id = 'message-2'></p> | ||
<p id = 'message-3'></p> | ||
<p id = 'message-4'></p> | ||
<p id = 'message-5'></p> | ||
</div> | ||
</div> | ||
|
||
<script src = '/js/script.js'></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
require('dotenv').config() | ||
const path = require('path') | ||
const express = require('express') | ||
const SpotifyWebApi = require('spotify-web-api-node'); | ||
|
||
const spotifyApi = new SpotifyWebApi({ | ||
clientId: process.env.CLIENT_ID , | ||
clientSecret: process.env.CLIENT_SECRET, | ||
redirectUri: process.env.REDIRECT_URL | ||
}); | ||
|
||
const app = express() | ||
|
||
const publicDirectoryName = path.join(__dirname, '../public') | ||
|
||
app.use(express.static(publicDirectoryName)) | ||
|
||
app.get('/login', (req, res) => { | ||
|
||
const scope = ['user-read-private', 'user-read-email', 'user-read-playback-state', 'user-modify-playback-state']; | ||
res.redirect(spotifyApi.createAuthorizeURL(scope)); | ||
}); | ||
|
||
app.get('/callback', (req, res) => { | ||
const error = req.query.error | ||
const code = req.query.code | ||
const state = req.query.state | ||
|
||
if (error){ | ||
return res.send(`Error : ${error}`) | ||
} | ||
|
||
spotifyApi.authorizationCodeGrant(code).then((data) => { | ||
const accessToken = data.body['access_token'] | ||
const refreshToken = data.body['refresh_token'] | ||
const expiresIn = data.body['expires_in'] | ||
|
||
spotifyApi.setAccessToken(accessToken) | ||
spotifyApi.setRefreshToken(refreshToken) | ||
|
||
res.sendFile(path.join(__dirname, '../public', 'search.html')); | ||
|
||
setInterval(async () => { | ||
const data = await spotifyApi.refreshAccessToken() | ||
const accessTokenRefreshed = data.body['access_token'] | ||
|
||
spotifyApi.setAccessToken(accessTokenRefreshed) | ||
}, expiresIn/2*1000) | ||
}).catch((error) => { | ||
return res.send({ | ||
error : error.body.error_description, | ||
solution : 'Session has expired! Login Again to continue!!' | ||
}) | ||
}) | ||
}); | ||
|
||
|
||
app.get('/search', (req, res) => { | ||
const {q} = req.query | ||
spotifyApi.searchTracks(q).then((searchData) => { | ||
const trackUri = searchData.body.tracks.items[0] | ||
res.send(trackUri) | ||
}).catch((error) => { | ||
res.send({ | ||
error : 'Sorry! We could not find a match. Try another search and if issue persists, login again to continue!' | ||
}) | ||
}) | ||
}) | ||
|
||
|
||
app.get('/play', (req, res) => { | ||
const { uri } = req.query | ||
spotifyApi.play({uris : [uri]}).then(() => { | ||
res.sendFile(path.join(__dirname, '../public', 'play.html')); | ||
|
||
}).catch((error) => { | ||
res.send(`Error in playing song : ${error}`) | ||
}) | ||
}) | ||
|
||
app.get('/help' , (req, res) => { | ||
res.send({ | ||
message : 'This Page is built to help you with the functionalities of app!🙂', | ||
title : 'Help Section' | ||
}) | ||
}) | ||
|
||
app.get('*',(req, res) => { | ||
res.send({ | ||
title: '404 Page', | ||
message: 'Page could not be found!' | ||
}) | ||
}) | ||
|
||
app.listen(3000, () => { | ||
console.log('Server has started functioning!') | ||
}) |