Problems with saving tracks to playlist on Jammming

I’m working on the Jammming project for React and am right at the end, but can’t get the tracks to save to the playlist. Here’s a link to the project.

When I click the ‘Save to Spotify’ button at the bottom I get the following error:

I can see a playlist has been created in Spotify, but it doesn’t have any of the tracks in it that I have added to the Jammming playlist.

I think the issue is related to the track URIs not being recorded correctly and noticed when logging the tracks URI to the console that ‘undefined’ is being returned. The problem is with my tracksUri variable (I assume) but I am not sure how to fix it. I’ve copied my code below. If anyone has any advice on how I can get the tracks to add to the playlist I would be very grateful.

From Spotify.js

const clientID = '********************';
const redirectURI = "http://localhost:3000/";

let accessToken;
const Spotify = {
    getAccessToken() {
        if(accessToken) {
            return accessToken;
        }

        //check for access token match
        const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
        const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);

        //check if access token and expiry time are in the URL
        if(accessTokenMatch && expiresInMatch) {
            accessToken = accessTokenMatch[1];
            const expiresIn = Number(expiresInMatch[1]);
            //this clears the parameters and allows us to grab a new access token when it expires
            window.setTimeout(() => accessToken = '', expiresIn * 1000);
            window.history.pushState('Access Token', null, '/');
            return accessToken;
        } else {
            const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientID}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectURI}`;
            window.location = accessUrl;
        }
    },

    search(term) {
        const accessToken = Spotify.getAccessToken();
        return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {headers: {
            Authorization: `Bearer ${accessToken}`
            }
        })
        .then(response => {
            return response.json();
        })
        .then(jsonResponse => {
            if(!jsonResponse.tracks) {
                return [];
            }
            return jsonResponse.tracks.items.map(track => ({
                id: track.id,
                name: track.name,
                artist: track.artists[0].name,
                album: track.album.name,
                URI: track.uri
            })) //end of map

        }) //end of then
    }, //end of search

    savePlaylist(name, trackUris) {

        if(!name || !trackUris.length) {
            return;
        }

        const accessToken = Spotify.getAccessToken();
        const headers = {Authorization: `Bearer ${accessToken}`};
        let userId;

        return fetch('https://api.spotify.com/v1/me', {headers: headers})
        .then(response => {
            return response.json();
        })
        .then(jsonResponse => {
            userId = jsonResponse.id;
            return fetch(`https://api.spotify.com/v1/users/${userId}/playlists`, 
            {
                headers: headers,
                method: 'POST',
                body: JSON.stringify({ name: name })
            })
            .then(response => {
                return response.json();
            })
            .then(jsonResponse => {
                const playlistId = jsonResponse.id;
                return fetch(`https://api.spotify.com/v1/playlists/${playlistId}/tracks`,  //there is also an error here
                {
                    headers: headers,
                    method: 'POST',
                    body: JSON.stringify({ uris: trackUris })
                })
            })
        })


    } //end of savePlaylist
}; //end of Spotify

export default Spotify;

From App.js

import React from 'react';
import './App.css';
import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';
import Spotify from '../../util/Spotify';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchResults: [],

      playlistName: 'My Playlist',

      playlistTracks: [],
    };

    this.addTrack = this.addTrack.bind(this);
    this.removeTrack = this.removeTrack.bind(this);
    this.updatePlaylistName = this.updatePlaylistName.bind(this);
    this.savePlaylist = this.savePlaylist.bind(this);
    this.search = this.search.bind(this);
  }

  addTrack(track) {
    let tracks = this.state.playlistTracks;
    if(tracks.find(savedTrack => savedTrack.id === track.id)) {
      return;
    }

    tracks.push(track);

    this.setState({ playlistTracks: tracks });
  }

  removeTrack(track) {
    let tracks = this.state.playlistTracks;
    tracks = tracks.filter(currentTrack => currentTrack.id !== track.id);

    this.setState({ playlistTracks: tracks });
  }

  updatePlaylistName(name) {
    this.setState({ playlistName: name})
  }
//I think the error is here
  savePlaylist() {
    const trackUris = this.state.playlistTracks.map(track => track.uri);
    Spotify.savePlaylist(this.state.playlistName, trackUris).then(() => {
      this.setState(
        { playlistName: 'New Playlist', 
          playlistTracks: [] 
        })
    })

  }



  search(term) {
    Spotify.search(term).then(searchResults => {
      this.setState({ searchResults: searchResults })
    })
  }


  render() {
  return (
    <div>
      <h1>Ja<span className="highlight">mmm</span>ing</h1>
      <div className="App">
        <SearchBar onSearch={this.search} />
        <div className="App-playlist">
          <SearchResults searchResults={this.state.searchResults} 
                         onAdd={this.addTrack}/>
          <Playlist playlistName={this.state.playlistName} 
                    playlistTracks={this.state.playlistTracks} 
                    onRemove={this.removeTrack}
                    onNameChange={this.updatePlaylistName}
                    onSave={this.savePlaylist}/>
        </div>
      </div>
</div>
    );
  }
}

export default App;

You might want to add content-type to “headers” :

const headers = {
Authorization: 'Bearer '+ this.getAccessToken(),
‘Content-Type’: ‘application/json’
};

Thank you for your reply. I have tried adding the ‘Content-Type’: ‘application/json’ as per your advice, but it’s still not working unfortunately.

I managed to fix the problem!

It all stemmed from my writing ‘uri’ as ‘URI’ in the Spotify.search() method. As soon as I changed it to uri tracks were returned.

Thanks again for your help!

1 Like

Hello Everyone,

I have a problem somewhat the same problem.

But most of my problems come from getAccessToken() .
To get an access token I have to click on Search twice to get access token.

The first time it becomes like this(These are console.logs to see if the app is getting access tokens in different sections):

Now if i just put sth on the search tab and click on search, this logs in the console:

This is frustrating because I have the same problem with SavePlayList() but this one doesn’t even work at all:
now the console shows :


As you can see the userID and PlaylistID both are undefined.
Here is my spotify.js:

const CLIENT_ID = '*****************************';
const REDIRECT_URI="http://localhost:3000/";
let accessToken ;

const spotify = {
  
    getAccessToken(){
        if(accessToken){
            return accessToken;
        }console.log("1");
        
        const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
        const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);
        console.log(`accessTokenMatch: ${accessTokenMatch}`);
        console.log(`expiresInMatch = ${expiresInMatch}`);
        if(accessTokenMatch && expiresInMatch){
            const accessToken = accessTokenMatch[1];
            console.log(`accessToken = ${accessToken}`);
            const expiresIn = Number(expiresInMatch[1])
            console.log(`expiresIn= ${expiresIn}`);
             /*following code, helps you to wipe the access token and URL parameters*/
            window.setTimeout(() => accessToken = '' , expiresIn * 1000);
            window.history.pushState('access Token', null, '/');
            return accessToken;
             }else{
                const accessUrl = `https://accounts.spotify.com/authorize?client_id=${CLIENT_ID}&response_type=token&scope=playlist-modify-public&redirect_uri=${REDIRECT_URI}`
                window.location = accessUrl;
                
            }
          
        
    },

    search(term){
        const accessToken = spotify.getAccessToken();
        console.log(1.1);
        console.log(`accessToken in search : ${accessToken}`);
        return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}` ,
        {headers:{
            Authorization: `Bearer ${accessToken}`
        }
        }).then(response => response.json())
          .then(jsonResponse => 
            {if(!jsonResponse.tracks){
                return [];
            }
            console.log(jsonResponse.tracks);
            return jsonResponse.tracks.items.map(track => ({
                id:track.id,
                name: track.name,
                artist: track.artists["0"].name,
                album: track.album.name,
                uri: track.uri
            })) 
        });

    },

    savePlaylist(name,trackUris){
        if(!name || !trackUris.length){
             return;
        }
            const accessToken = spotify.getAccessToken();
            console.log(1.2);
            console.log(accessToken);
            const headers = {Authorization: `bearer ${accessToken}`}
            
            let userID;
            return fetch(`https://api.spotify.com/v1/me`, {headers :headers}
            ).then(response => response.json)
             .then(
                    jsonResponse => {userID = jsonResponse.id;
                    console.log(`userID = ${userID}`)
                    return fetch(`https://api.spotify.com/v1/users/${userID}/playlists`, 
                    {headers:headers,
                    method:'POST',
                    body: JSON.stringify({name:name})})
                    .then(response => response.json())
                    .then(jsonResponse => {const playlistID = jsonResponse.id;
                        console.log(`playlistID = ${playlistID}`);
                     return fetch(`https://api.spotify.com/v1/users/${userID}/playlists/${playlistID}/tracks`,
                     {headers: headers,
                      method: "POST",
                      body:JSON.stringify({uris: trackUris})
                      })
                     .then(response => response.json())
                    })
                })
        
        
    }

}




export default spotify;

and here is my app.js:

import React from 'react';
import './App.css';
import SearchBar from '../SearchBar/SearchBar';
import SearchResaults from '../SearchResaults/SearchResaults';
import Playlist from '../Playlist/Playlist';
import spotify from '../../util/spotify';


class App extends React.Component{
  constructor(props){
    super(props);
    this.state= {searchResults: [],
                 PlaylistName:'My Playlist', 
                 playlistTracks: []
                 };
    this.addTrack = this.addTrack.bind(this);
    this.removeTrack = this.removeTrack.bind(this);
    this.updatePlaylistName = this.updatePlaylistName.bind(this);
    this.savePlaylist = this.savePlaylist.bind(this);
    this.search = this.search.bind(this);
  }
  search(term){
    spotify.search(term)
    .then(searchResults => this.setState({searchResults: searchResults}))
    console.log(0.9)
    let trackUris = this.state.playlistTracks.map(track => track.uri); 
    console.log(trackUris)
  }

  savePlaylist (){
    let trackUris = this.state.playlistTracks.map(track => track.uri); 
    console.log(trackUris)
    spotify.savePlaylist(this.state.PlaylistName, trackUris).then(
      this.setState({PlaylistName: 'New Playlist', playlistTracks:[]})
    )
  }

  updatePlaylistName(name){
    this.setState({PlaylistName: name})
  }

  addTrack(track){
    let tracks = this.state.playlistTracks;
    
    if(tracks.find(savedTrack => savedTrack.id === track.id)){
        return;
    }else{    
      tracks.push({id:track.id , name: track.name , artist: track.artist , album: track.album})
      this.setState({playlistTracks: tracks})
    }
  
  }

  removeTrack(track){
    let tracks = this.state.playlistTracks;
    tracks = tracks.filter(currentTrack => currentTrack.id !== track.id);
    this.setState({playlistTracks: tracks})
    console.log(track.id)
    
  }


  render(){
    return (
      <div>
        <h1>Ja<span className="highlight" >mmm</span>ing</h1>
        <div className="App">
          {/*<!-- Add a SearchBar component -->*/}
          <SearchBar 
          onSearch={this.search} />
          
          <div className="App-playlist">
            {/* <!-- Add a SearchResults component --> */}
            <SearchResaults 
              searchResults={this.state.searchResults} 
              onAdd={this.addTrack}/>
            {/* <!-- Add a Playlist component --> */}
            <Playlist  PlaylistName={this.state.PlaylistName} 
              playlistTracks={this.state.playlistTracks} 
              onRemove={this.removeTrack} 
              onNameChange={this.updatePlaylistName}
              onSave={this.savePlaylist}/>
          </div>
        </div>
      </div>
    );
  }
}

export default App;

Please Help me because I am so close to leaving all of this and go back to the hunter-gatherer lifestyle.

Thanks and regards