javascriptreactjslast.fm

Getting similar songs using Last.fm API


I'm trying to use Last.fm's API to find similar songs given a song name. Last.fm has a feature that can do this called track.getSimilar, but both "track" and "artist" are required parameters. However, because of the way this works, I can't figure out a way to either a) get both a song name, and the artist from one search bar input, or b) get the song's artist using track.search. Here's the part of my code relating to this:

const API_KEY = (my key);
const DEFAULT_TRACK = 'humble.'
const DEFAULT_ARTIST = 'kendrick lamar'
const RESULT_LIMIT = 5;
const RESULT_PAGE = 1;
const API_GET_SIMILAR_URL = `http://ws.audioscrobbler.com/2.0/?method=track.getSimilar&limit=${RESULT_LIMIT}&format=json&api_key=${API_KEY}`

const initialState = {
  loading: false,
  tracks: [],
  errorMessage: null
}

const reducer = (state, action) => {
  switch (action.type) {
    case "SEARCH_REQUEST":
      return {
        ...state,
        loading: true,
        errorMessage: null
      };
    case "SEARCH_SUCCESS":
      return {
        ...state,
        loading: false,
        tracks: action.payload
      };
    case "SEARCH_FAILURE":
      return {
        ...state,
        loading: false,
        errorMessage: action.error
      };
    default:
      return state;
  }
}

useEffect(() => {
  fetch(`${API_GET_SIMILAR_URL}&page=${RESULT_PAGE}&artist=${DEFAULT_ARTIST}&track=${DEFAULT_TRACK}`)
    .then(response => response.json())
    .then(jsonResponse => {
      const tracks = jsonResponse.similartracks[Object.keys(jsonResponse.similartracks)[0]];
      dispatch({
        type: "SEARCH_SUCCESS",
        payload: tracks
      });
    });
}, []);

const search = (searchValue) => {
  dispatch({
    type: "SEARCH_REQUEST"
  });
  fetch(`${API_GET_SIMILAR_URL}&page=${RESULT_PAGE}&track=${searchValue}`)
    .then(response => response.json())
    .then(jsonResponse => {
      if (!jsonResponse.error) {
        const tracks = jsonResponse.similartracks[Object.keys(jsonResponse.similartracks)[0]];
        dispatch({
          type: "SEARCH_SUCCESS",
          payload: tracks
        });
      } else {
        dispatch({
          type: "SEARCH_FAILURE",
          error: jsonResponse.error
        });
      }
    });
};

let {
  tracks,
  errorMessage,
  loading
} = state;
console.log(state);

In my search function I'm getting an Uncaught (in promise) TypeError: Cannot convert undefined or null to object error in the Object.keys line because I'm not using the artist parameter when calling the API. I tried using track.search to get the artist but I'm not sure where/how to incorporate it into my code.

I appreciate any help or advice. Thanks.


Solution

  • I think your issue may be lack of URL encoding. The reason I think this is because your artist string has a space in it which would need to be encoded to kendrick%20lamar to be processed properly.

    Encoding is mentioned on the API's introduction page. UTF-8 is common, so you can use a function like encodeURI() to achieve this (MDN Web Docs).

    One way to test this theory would be to use the values from the example URL in the track.getSimilar docs which is artist cher and track believe. These values conveniently don't have spaces or special characters that need escaping. By knowing the expected response it's also a good functional baseline to ensure the rest of your code is functioning as intended.

    It's also a good song, make sure to give it a listen :)