Checkout the design rationale on
Checkout a live demo of our project: Ringo.Pro live demo
Ringo Pro is a application for music supervisors. It makes looking for tracks and searching for music licenses easier. This project is a proof of concept prototype which illustrates the possibilities of this application.
- Projects(Spotify playlists for now) in the left side bar which are clickable, and will show tracks in the playlist
- Search
- Detailed information about tracks
- Draggable search results to temporarily save chosen tracks
- Music player
git clone https://github.com/Ringo-Pro/Ringo.Pro.git
cd Ringo.Pro
npm install
To make the application work you will need to make a .env
file in the root folder. Here you must add:
- Client ID from Spotify
- Client Secret from Spotify
- Redirect URI (for Spotify)
SPOTIFY_CLIENT_ID=<YOUR_CLIENT_ID>
SPOTIFY_CLIENT_SECRET=<YOUR CLIENT SECRET>
REDIRECT_URI=<YOUR_REDIRECT_URI>
npm start
or run the app in development mode
npm run start:dev
npm run prestart
= Build CSS before startingnpm run start
= Start the appnpm run dev
= Start the app with Nodemonnpm run start:dev
= Start the app in development mode (watch + dev)npm run build:css
= Build CSSnpm run build:js
= Build ESnpm run build:img
= Build IMGnpm run build
= Build CSS + ES + IMGnpm run watch
= Watch CSS + ES Filesnpm run deploy
= Deploy to Herokunpm run logs
= Show Heroku logs
The Spotify api makes use of a oAuth flow. In order to get data from the Spotify endpoints you have to go through the basice flow. The endpoints used in this prototype:
https://api.spotify.com/v1/me - gets the current users Spotify profile
//request:
await getDataFromSpotfy(`https://api.spotify.com/v1/me`, options)
//returned data:
{
"country":"SE",
"display_name":"JM Wizzler",
"email":"email@example.com",
"external_urls":{
"spotify":"https://open.spotify.com/user/wizzler"
},
"followers":{
"href":null,
"total":3829
},
"href":"https://api.spotify.com/v1/users/wizzler",
"id":"wizzler",
"images":[
{
"height":null,
"url":"https://fbcdn-profile-a.akamaihd.net/hprofile-ak-frc3/t1.0-1/1970403_10152215092574354_1798272330_n.jpg",
"width":null
}
],
"product":"premium",
"type":"user",
"uri":"spotify:user:wizzler"
}
https://api.spotify.com/v1/me/playlists - gets a list with the current users playlists
//request:
await getDataFromSpotfy(`https://api.spotify.com/v1/me/playlists`, options)
//returned data:
{
"href":"https://api.spotify.com/v1/users/wizzler/playlists",
"items":[
{
"collaborative":false,
"external_urls":{
"spotify":"http://open.spotify.com/user/wizzler/playlists/53Y8wT46QIMz5H4WQ8O22c"
},
"href":"https://api.spotify.com/v1/users/wizzler/playlists/53Y8wT46QIMz5H4WQ8O22c",
"id":"53Y8wT46QIMz5H4WQ8O22c",
"images":[
],
"name":"Wizzlers Big Playlist",
"owner":{
"external_urls":{
"spotify":"http://open.spotify.com/user/wizzler"
},
"href":"https://api.spotify.com/v1/users/wizzler",
"id":"wizzler",
"type":"user",
"uri":"spotify:user:wizzler"
},
"public":true,
"snapshot_id":"bNLWdmhh+HDsbHzhckXeDC0uyKyg4FjPI/KEsKjAE526usnz2LxwgyBoMShVL+z+",
"tracks":{
"href":"https://api.spotify.com/v1/users/wizzler/playlists/53Y8wT46QIMz5H4WQ8O22c/tracks",
"total":30
},
"type":"playlist",
"uri":"spotify:user:wizzler:playlist:53Y8wT46QIMz5H4WQ8O22c"
},
{
"collaborative":false,
"external_urls":{
"spotify":"http://open.spotify.com/user/wizzlersmate/playlists/1AVZz0mBuGbCEoNRQdYQju"
},
"href":"https://api.spotify.com/v1/users/wizzlersmate/playlists/1AVZz0mBuGbCEoNRQdYQju",
"id":"1AVZz0mBuGbCEoNRQdYQju",
"images":[
],
"name":"Another Playlist",
"owner":{
"external_urls":{
"spotify":"http://open.spotify.com/user/wizzlersmate"
},
"href":"https://api.spotify.com/v1/users/wizzlersmate",
"id":"wizzlersmate",
"type":"user",
"uri":"spotify:user:wizzlersmate"
},
"public":true,
"snapshot_id":"Y0qg/IT5T02DKpw4uQKc/9RUrqQJ07hbTKyEeDRPOo9LU0g0icBrIXwVkHfQZ/aD",
"tracks":{
"href":"https://api.spotify.com/v1/users/wizzlersmate/playlists/1AVZz0mBuGbCEoNRQdYQju/tracks",
"total":58
},
"type":"playlist",
"uri":"spotify:user:wizzlersmate:playlist:1AVZz0mBuGbCEoNRQdYQju"
}
],
"limit":9,
"next":null,
"offset":0,
"previous":null,
"total":9
}
-
https://api.spotify.com/v1/search?q=${req.query.query}&type=track%2Cartist&limit=10&offset=0 - used to search for tracks and artists
-
https://api.spotify.com/v1/audio-features/${song.id} - used to get the features of a track
In order to make a custom music player Spotify made something called the web playback SDK. This is a library which you can easily use to plat spotify tracks whithin your own web application. We made use of the following events:
Initialise the player:
window.onSpotifyWebPlaybackSDKReady = () => {
// here is wehere all events related to the SDK live
};
- getCurrentState:
player.getCurrentState().then((state) => {
if (!state) {
// nowPlaying.children[0].textContent = 'Click on a song!'
// console.error('User is not playing music through the Web Playback SDK')
console.log('User is not playing music through the Web Playback SDK');
fetch('https://api.spotify.com/v1/me/player', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
})
.then((res) => res.json())
.then((body) => {
console.log(body);
nowPlaying.children[0].textContent = body.item.name;
nowPlaying.children[1].textContent = body.item.artists[0].name;
albumArt.src = body.item.album.images[2].url;
});
return;
}
let {
current_track,
next_tracks: [next_track],
} = state.track_window;
console.log('Currently Playing', current_track);
console.log('Playing Next', next_track);
});
Player state changed:
let currState = {};
player.addListener('player_state_changed', (state) => {
currState.paused = state.paused;
currState.position = state.position;
currState.duration = state.duration;
currState.updateTime = performance.now();
currState.current_track = state.track_window.current_track;
});
Changing volume:
volume.addEventListener('mouseup', function () {
player.setVolume(this.value).then(() => {
console.log('volume updated to: ', this.value);
});
});
Play and pause toggling:
pauseButton.addEventListener('click', (event) => {
player.togglePlay().then(() => {});
});
Seeking in a track:
trackProgression.addEventListener('mouseup', function () {
// console.log('yeet: ', this.value)
player.seek(this.value).then(() => {
console.log('Changed position!');
});
});
- Jo Sandow for supporting us during this project.
- Marcel Alexander Wiebenga for thinking of this project.
- CSS Reset by Meyerweb for their CSS reset
- Danrovito - Country dropdown for saving us a lot of time.
- Peter-Paul Koch - checking input types with JS for his code to check if a browser supports an input type.
- Thomas Loven for making round sliders.
- Google Material Icons
- Cool visual graphs
- Make a new playlist with chosen tracks
- Show track lyrics in information screen
- Make data visualisations of track stats
- Better filtering
π Artikel / Documentation | βοΈ Code | πΉ Video | π Tools
- π Spotify API Documentation
- π RegEx Tool
- π The fundamental music genre list - Dan Gravell - Blisshq.com
- π An Efficient Classification Algorithm for Music Mood Detection in Western and Hindi Music Using Audio Feature Extraction - published 2014
- π Novel audio features for music emotion recognition - published March 2018
- π How JavaScript works: tracking changes in the DOM using MutationObserver - Alexander Zlatkov - Medium
- π Quick Reminder that Details/Summary is the Easiest Way Ever to Make an Accordion - Chris Coyier - CSS-Tricks
Thanks goes to these wonderful people (emoji key):
Marten de Bruijn π» π¨ |
Nick π» π¨ |
ilovemayhem π π¬ π |
This project follows the all-contributors specification. Contributions of any kind welcome!