javascriptnode.jsdiscord.jsspotifyspotify-app

Nodejs - Spotify API check if track exists in playlist


I'm creating a bot in Discord that adds shared songs in specific channels and adds it to corresponding playlist. The function work as intended, so I've left that part of the code out. But I can add if it's needed.

With the code below, I want to be able to evaluate if the playlist already contains the shared song and if so don't add it. Skip duplicates basically.

const playlists = new Map();
    
const spectrumPlaylist = {
  channelid: '948177541414404116',
  name: 'spectrum',
  id: '2SDsHpVc7yfQhGg0tS0RQ0'
}
playlists.set('948177541414404116', {
  name: 'spectrum',
  id: '2SDsHpVc7yfQhGg0tS0RQ0'
})
playlists.set('948185709037121566', {
  name: 'indie',
  id: '0JxWP48wGTRJrijEzg41fD'
})
playlists.set('948185876909936661', {
  name: 'instrumental',
  id: '37IIwZg5ek7mEYa2Ulmy85'
})

client.on('messageCreate', async message => {
  const playlist = playlists.get(message.channel.id);
  
  spotifyApi.getPlaylistTracks(playlist.id, {
    offset: 0,
    limit: 20,
    fields: 'items'
  })
  .then(
    function(data) {
      console.log('The playlist contains these tracks', data.body.items);
    },
    function(err) {
      console.log('Something went wrong!', err);
    }
  );
});

Code above logs the following:

    The playlist contains these tracks [
  {
    added_at: '2022-04-18T20:25:51Z',
    added_by: {
      external_urls: [Object],
      href: 'https://api.spotify.com/v1/users/<userid>',
      id: '<userid>',
      type: 'user',
      uri: 'spotify:user:<userid>'
    },
    is_local: false,
    primary_color: null,
    track: {
      album: [Object],
      artists: [Array],
      available_markets: [Array],
      disc_number: 1,
      duration_ms: 285122,
      episode: false,
      explicit: false,
      external_ids: [Object],
      external_urls: [Object],
      href: 'https://api.spotify.com/v1/tracks/04wZnll09OSLNgvOEpWNHF',
      id: '04wZnll09OSLNgvOEpWNHF',
      is_local: false,
      name: 'Pacifica (Sultan + Shepard Remix)',
      popularity: 53,
      preview_url: 'https://p.scdn.co/mp3-preview/0fc9fadef348ded977208eea4a16135f497ecaa6?cid=d447a9a0d3d14fab83523430649b6c39',
      track: true,
      track_number: 1,
      type: 'track',
      uri: 'spotify:track:04wZnll09OSLNgvOEpWNHF'
    },
    video_thumbnail: { url: null }
  },
  {
    added_at: '2022-04-18T20:25:57Z',
    added_by: {
      external_urls: [Object],
      href: 'https://api.spotify.com/v1/users/<userid>',
      id: '<userid>',
      type: 'user',
      uri: 'spotify:user:<userid>'
    },
    is_local: false,
    primary_color: null,
    track: {
      album: [Object],
      artists: [Array],
      available_markets: [Array],
      disc_number: 1,
      duration_ms: 265506,
      episode: false,
      explicit: false,
      external_ids: [Object],
      external_urls: [Object],
      href: 'https://api.spotify.com/v1/tracks/5pEmeoMUW38w1oU3kPelvR',
      id: '5pEmeoMUW38w1oU3kPelvR',
      is_local: false,
      name: 'Spectrum',
      popularity: 46,
      preview_url: 'https://p.scdn.co/mp3-preview/5d9104b4cb82e3879867e2dc65997ab19b0f501b?cid=d447a9a0d3d14fab83523430649b6c39',
      track: true,
      track_number: 3,
      type: 'track',
      uri: 'spotify:track:5pEmeoMUW38w1oU3kPelvR'
    },
    video_thumbnail: { url: null }
  },
  {
    added_at: '2022-04-18T20:26:04Z',
    added_by: {
      external_urls: [Object],
      href: 'https://api.spotify.com/v1/users/<userid>',
      id: '<userid>',
      type: 'user',
      uri: 'spotify:user:<userid>'
    },
    is_local: false,
    primary_color: null,
    track: {
      album: [Object],
      artists: [Array],
      available_markets: [Array],
      disc_number: 1,
      duration_ms: 244186,
      episode: false,
      explicit: false,
      external_ids: [Object],
      external_urls: [Object],
      href: 'https://api.spotify.com/v1/tracks/1AA3ZjLo9tD2iSZAs2svyj',
      id: '1AA3ZjLo9tD2iSZAs2svyj',
      is_local: false,
      name: 'Chinatown (feat. Bruce Springsteen)',
      popularity: 54,
      preview_url: 'https://p.scdn.co/mp3-preview/993c6eecf44d181f50146c9cbf0d9e316d5c6cf8?cid=d447a9a0d3d14fab83523430649b6c39',
      track: true,
      track_number: 2,
      type: 'track',
      uri: 'spotify:track:1AA3ZjLo9tD2iSZAs2svyj'
    },
    video_thumbnail: { url: null }
  }
]

I want to retrieve the id of each track and see if any matches the message.content that has been parsed to an id. Also, not sure how to handle the limit as I couldn't set it too high.

I'm using spotify-web-api-node.


Solution

  • Well, you have an array of data for all tracks in the playlist. And your users send Spotify links to your channels in order to add them to the playlist. The Spotify link for a track definitely has the track's ID in its URL, and as you can see in the array of track data, the track ID is provided in the track data. Let's specifically look at the first song on your playlist, 'Pacifica (Sultan + Shepard Remix)', as an example. This is what the relevant part of the track data looks like for that song:

    {
       track: {
          album: [Object],
          artists: [Array],
          available_markets: [Array],
          disc_number: 1,
          duration_ms: 285122,
          episode: false,
          explicit: false,
          external_ids: [Object],
          external_urls: [Object],
          href: 'https://api.spotify.com/v1/tracks/04wZnll09OSLNgvOEpWNHF',
          id: '04wZnll09OSLNgvOEpWNHF', // <- Notice this
          is_local: false,
          name: 'Pacifica (Sultan + Shepard Remix)',
          popularity: 53,
          preview_url: 'https://p.scdn.co/mp3-preview/0fc9fadef348ded977208eea4a16135f497ecaa6?cid=d447a9a0d3d14fab83523430649b6c39',
          track: true,
          track_number: 1,
          type: 'track',
          uri: 'spotify:track:04wZnll09OSLNgvOEpWNHF'
       }
    }
    

    And this is what the user-sent Spotify link looks like:

    "https://open.spotify.com/track/04wZnll09OSLNgvOEpWNHF?si=FEomY7F6T_WAXwgLqIhhEw"
    

    As you can see, the ID "04wZnll09OSLNgvOEpWNHF" is present within both the link and the track data. This is what we can use to determine if the track is already within the playlist. First, let's parse the ID from the link:

    // Replace 'url' with the actual link from the message content
    const url = "https://open.spotify.com/track/04wZnll09OSLNgvOEpWNHF?si=FEomY7F6T_WAXwgLqIhhEw";
    
    const trackId = url.split("/").slice(-1)[0].split("?")[0];
    

    Here's what the above example does. First, it splits url into an array of all strings between "/", like so: ["https:", "", "open.spotify.com", "track", "04wZnll09OSLNgvOEpWNHF?si=FEomY7F6T_WAXwgLqIhhEw"]. We then use .slice(-1) to get a "slice" of the array, specifically the last slice of the array (.slice() accepts an index as a parameter, and returns a new array of all elements between the index and the end of the array; if you give it a negative index, it moves backward from the end of the array; therefore -1 represents 1 item backward from the end of the array, i.e. just the last item in the array). Once we have done this slice, we get the first element of the sliced array via [0], and we're now left with the following string: "04wZnll09OSLNgvOEpWNHF?si=FEomY7F6T_WAXwgLqIhhEw". Now, we use .split("?") to split the array into the strings between the question mark, and get the first of those strings (which is the track ID: "04wZnll09OSLNgvOEpWNHF"). Hopefully that explained how it works. It's basic javascript string parsing.

    Now that we have the track ID, we need to see if we can find the track ID somewhere within the array of track data items. We can use Array.some() to achieve this. That method loops through the array, and returns true if the callback function you pass as its parameter returns true for any item in the array. Here's how it will work in your case:

    const hasTrack = data.body.items.some(item => item.track.id == trackId);
    
    if (hasTrack) {
        // Track is already in playlist
    }
    else {
        // Add track to playlist
    }
    

    And that, combined with the snippet before that one that parses the track ID, is the solution to your answer. Remember that each individual track data item looks like this:

    {
        added_at: '2022-04-18T20:25:51Z',
        added_by: {
          external_urls: [Object],
          href: 'https://api.spotify.com/v1/users/<userid>',
          id: '<userid>',
          type: 'user',
          uri: 'spotify:user:<userid>'
        },
        is_local: false,
        primary_color: null,
        track: {
          album: [Object],
          artists: [Array],
          available_markets: [Array],
          disc_number: 1,
          duration_ms: 285122,
          episode: false,
          explicit: false,
          external_ids: [Object],
          external_urls: [Object],
          href: 'https://api.spotify.com/v1/tracks/04wZnll09OSLNgvOEpWNHF',
          id: '04wZnll09OSLNgvOEpWNHF',
          is_local: false,
          name: 'Pacifica (Sultan + Shepard Remix)',
          popularity: 53,
          preview_url: 'https://p.scdn.co/mp3-preview/0fc9fadef348ded977208eea4a16135f497ecaa6?cid=d447a9a0d3d14fab83523430649b6c39',
          track: true,
          track_number: 1,
          type: 'track',
          uri: 'spotify:track:04wZnll09OSLNgvOEpWNHF'
        },
        video_thumbnail: { url: null }
    }
    

    Hence why we need to do item.track.id to get to the track's ID in the .some() callback. This solution should work for you. Feel free to comment if you are confused about the process, logic, or code I used in any way.

    As for the final thing you mentioned:

    not sure how to handle the limit as I couldn't set it too high

    If you aren't able to retrieve all songs in the playlist from Spotify, consider keeping track of the song list of each playlist in your own database (adding to the database as each song is being added to the playlist), and retrieving the data from there instead. That would probably be easier than trying to bypass the limit.