DEV Community

alliecaton
alliecaton

Posted on • Updated on

JS Music Recommendation Web App

Music-R is available for use here

img1

Over the past week, I've dug into my first real vanilla Javascript application, and it's been a really enjoyable week (surprisingly? maybe so)! For the last three weeks I have moaned and groaned over the growing pains of switching from Ruby to learning JS, but putting the skills I've learned to use on a real application has really changed my entire mindset about Javascript!

My application is a simple music recommender. You enter a song and its artist, and it spits out a related song. You have the option to make a username for yourself and keep track of recommended songs that you like. Pretty simple, but the whole thing ended up being about 300 lines of code! WHEW.

I used three API's for this project, the last.fm API, the Youtube API, and a my Rails backend API where I stored user and song data.

I had a good time playing around with CSS styling and DOM manipulation with this one-- it's probably the cutest looking application I've made so far. But more than that, I had a really good time building out the logic of my fetch requests.

The last.fm API has a built in endpoint that returns 100 related tracks when given a single track-- it's the first place that the app hooks into when a user enters a song. But, it leaves a lot to be desired. Last.fm doesn't have related songs for everything-- in fact, most of the music that I listen to at least, didn't have related tracks. So instead of throwing an alert or an error, I decided to construct a fallback for when the related tracks endpoint comes up empty. I built out some functions that would grab the artist of the song, feed it into the related artists last.fm endpoint, grab that artist's most popular songs, and return a random song from that list.

static getRelatedArtists(artistName) {
        return fetch(`https://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist=${artistName.toLowerCase().trim()}&api_key=efeaa32576655308d8b417be9812fc15&format=json`)
        .then(function(response) {
            return response.json();
        })
        .then(function(json) {
            console.log(json)
            let randomNum = Math.floor((Math.random() * 100))
            let artist = json.similarartists.artist[randomNum].name
            lastFmApi.getArtistTopSongs(artist)
        })
        .catch(function() {
            alert("Something went wrong. Reload and try again!")
        })
    }

    static getArtistTopSongs(artistName){
        return fetch(`https://ws.audioscrobbler.com/2.0/?method=artist.gettoptracks&artist=${artistName.toLowerCase().trim()}&api_key=efeaa32576655308d8b417be9812fc15&format=json`)
        .then(function(response) {
            return response.json();
        })
        .then(function(json) {
            console.log(json)
            let randomNum = Math.floor((Math.random() * 50))
            let song = json.toptracks.track[randomNum]
            lastFmApi.displayResults(song)
            console.log(song)
            Youtube.getVideo(song.name, song.artist.name)
        })
        .catch(function() {
            alert("Something went wrong. Reload and try again!")
        })
    }
Enter fullscreen mode Exit fullscreen mode

I call it off the back of my fetch request to the related tracks fetch request in a simple if/else block that checks the length of the returned related tracks array. Once I implemented this, I almost never bumped up against a song without a recommendation.

All in all, this was a really great experience-- probably my favorite thing to have built! It might not be the most interesting in terms of user interactivity, but I definitely felt like the process of building this project was smoother than others, and I learned a TON. Happy listening!

Top comments (0)