13

I'm using a node.js server, the Spotify API, and the spotify-web-api-js node module to create a web application where the user can enter an artist's name, see a list of songs from related artists, and then optionally save that playlist to their own Spotify account. However, i'm still having trouble with the last step.

My user authorization flow is happening first:

if (params.access_token) {

    s.setAccessToken(params.access_token);

    s.getMe().then(function(data) {

      console.log(data);
      console.log(data.id);
      user_id = data.id;

Below it is where the the actual details of the songs are gathered by the API. Although it is lower down, it occurs first and the user authorization only happens if the user clicks a second button on that page.

async.times(counter, function(n, next){
        s.getArtistTopTracks(relatedArtists[n].id, "US", function (err, data2) {
          relatedArtists[n].song = data2.tracks[0].name;
          relatedArtists[n].uri = data2.tracks[0].uri;
          console.log(relatedArtists[n].uri);
          // make sure to put the access token here add song to playlist
          // create array
          song_uris.push(relatedArtists[n].uri);
          console.log(song_uris);

          // song_uris = relatedArtists[n].uri;
          //
          // console.log(song_uris);

          next(null);

      $("#playlist").load(function() {
            s.addTracksToPlaylist(user_id, playlist_id, song_uris);
          });
        });


      }, function(err) {
        // console.table(relatedArtists);

        for (k = 0; k < 20; k++)
        {
          $('#related-artist').append('<p><strong>' + relatedArtists[k].name + '</strong> -- \"' + relatedArtists[k].song + '\"</p>');


        }

(JSBin of full code here, though it might not work because I use browserify on my own server)

Right now, on line 114 I have song_uris.push(relatedArtists[n].uri); pushing the contents into an array, using async.times. Since this is below where I create the playlist on line 66, it shows as an empty array:

 s.createPlaylist(user_id, {name: 'Related Artist Playlist'}).then(function(data3) {
        console.log(data3);
        playlist_id = data3.uri;
        playlist_id = playlist_id.substring(33);
        console.log(playlist_id);


        console.log(song_uris);


       });

There, console.log(song_uris) shows an empty array, so addTracksToPlaylist() breaks like so:

enter image description here

On the other hand, if I try to addTracksToPlaylist() below, I don't have authorization to access the user's account.

The user authorization flow has been added later on after the basic functionality of showing a list of songs had already been working, but i'm not sure how to refactor it effectively in order to save that list to my user's playlist. At the moment, i'm only creating an empty playlist in the Spotify account.

What kind of event listener can I add so that it will wait until every single instance of async.times is performed, so that addTracksToPlaylist() can work? The DOM is already initially loaded before I get this data. I looked at this question, but it didn't quite help me solve this problem. Thanks!

EDIT: I now have the song_uri array created the way I need it, but I still can't get it into the playlist. I have been playing around with the location of my access token so that I can access the playlist created, but still no luck.

The console.log(song_uris); statement on line 130 shows the completed array that I need, but when I insert it into s.addTracksToPlaylist(user_id, playlist_id, song_uris); I get these errors in the developer console:

POST https://api.spotify.com/v1/users/tenderoni-/playlists/7MtQTzUsxD4IEJ8NmmE36q/tracks?uris= 400 (Bad Request)
bundle.js:10616
Uncaught (in promise) XMLHttpRequest {}

Basically, it's not receiving the parameter for some reason. And I log the playlist_id beforehand, so I can tell it's working (also, I see blank playlists with the specified title being created in my Spotify account).

Full updated code here: https://github.com/yamilethmedina/cs50xmiami/blob/master/assignments/portfolio/public/scripts.js

Community
  • 1
  • 1
Yami Medina
  • 660
  • 8
  • 26
  • Did you intend to include credentials in the JSBin post? I can't tell if those are from some API doc somewhere or if those are yours. – W. Cybriwsky Nov 03 '15 at 04:30

1 Answers1

2

The callback argument to async.times can take a second 'results' argument, so if you call 'next' with the song_uri you get around each turn of the loop, you can get song_uris at the end, pass a callback through searchArtists, and use it there. Skeleton version:

$(document).ready(function($) {
    $('#s').on('submit', function() {
        searchArtists($('#originalartist').val(), function(err, song_uris) {
            if (params.access_token) {
                // omitted
                s.setAccessToken(params.access_token);
                s.getMe().then(function(data) {
                    console.log(data);
                    console.log(song_uris);
                });
            }
        });
        return false;
    });
})

function searchArtists(originalArtist, callback) {
    s.getArtistRelatedArtists(originalArtistId, function(err, data) {
        async.times(counter, function(n, next) {
            // omitted
            next(related_artists[n].uri)
        }, function(err, song_uris) {
            callback(err, song_uris);
        });

    });

}
W. Cybriwsky
  • 456
  • 3
  • 5
  • Would `s.createPlaylist()` go in the first part, within the `getMe()` function? – Yami Medina Nov 03 '15 at 03:37
  • Yes, since you still need the user_id from the getMe() callback in order to call s.createPlaylist(). Since getMe() is already inside the callback to searchArtists, you'll be able to access the song_uris, too. – W. Cybriwsky Nov 03 '15 at 04:29
  • I noticed that it jumps from getArtistRelatedArtists() to async.times(). shouldn't getArtistTopTracks() go in between those two lines? I'm getting the first song from each of the related artists through that function in order to populate song_uris. I think I should go back to my initial code and then try to implement this again, because I think I may have messed something else up to break the for loop that prints the playlist on the screen, songs show as undefined in browser but not console, & while I see traffic to account.spotify.com I get no console response there anymore. – Yami Medina Nov 03 '15 at 05:10
  • getArtistTopTracks() would be inside the async.times() callback like before, sorry if cutting that out made things less clear. – W. Cybriwsky Nov 03 '15 at 05:23
  • Thanks, that function has been restored, but nothing seems to be pushing to the `song_uris` array. My terminal shows that i'm logging into my spotify account when I press the button, but it's not running my console.log(song_uris) in my final function callback. – Yami Medina Nov 03 '15 at 16:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/94117/discussion-between-yami-medina-and-w-cybriwsky). – Yami Medina Nov 03 '15 at 17:28
  • I haven't heard back from you in chat? This is the latest version of the code: http://codetidy.com/7215 nothing after `next(relatedArtists[n].uri);` seems to be running I've tried putting `s.addTracksToPlaylist(user_id, playlist_id, song_uris);` in different locations but it doesn't seem to make a . – Yami Medina Nov 06 '15 at 20:07
  • Please see my latest edit? I'm going to award the bounty to you but not accept your answer yet because I still need some assistance. Thanks! – Yami Medina Nov 09 '15 at 18:49