I'm currently using the very cool app Exportify (https://github.com/watsonbox/exportify) to export Spotify playlists to CSV.
My javascript knowledge is terrible but I'm currently trying to include the genre of each track, and I'm having trouble retrieving it.
I'm assuming that:
I can get the artist ID by adding
item.track.artists.map(function (artist) {
return artist.id
}).join(', '),
to the code below in the exportify.js file.
var tracks = responses.map(function (response) {
return response.items.map(function (item) {
return [
item.track.uri,
item.track.id,
item.track.name,
item.track.artists.map(function (artist) {
return artist.name
}).join(', '),
item.track.artists.map(function (artist) {
return artist.id
}).join(', '),
item.track.album.name,
item.track.disc_number,
item.track.track_number,
item.track.duration_ms,
item.added_by == null ? '' : item.added_by.uri,
item.added_at
].map(function (track) {
return '"' + track + '"';
})
});
});
Can anyone tell me how I can then get the artist genre based on the artist ID and add it as another column?
Use the "Get an Artist" endpoint in Spotify:
https://developer.spotify.com/documentation/web-api/reference/get-an-artist
Calling this endpoint will get you the genres of an artist given his artist ID.
I've implemented it!
Below is the function from https://github.com/pavelkomarov/exportify/blob/master/exportify.js that makes all the requisite API queries. My apiCall function is also in that file. It's using fetch.
Because it's all happening over the network, each new step has to be wrapped in a .then() which is dependent upon previous objects getting resolved. Thankfully, in recent years JavaScript has gotten more elegant at this. https://eloquentjavascript.net/11_async.html
The live app lives here. I've also created a notebook to analyze the output.
csvData(access_token, playlist) {
// Make asynchronous API calls for 100 songs at a time, and put the results (all Promises) in a list.
let requests = [];
for (let offset = 0; offset < playlist.tracks.total; offset = offset + 100) {
requests.push(utils.apiCall(playlist.tracks.href.split('?')[0] + '?offset=' + offset + '&limit=100',
access_token));
}
// "returns a single Promise that resolves when all of the promises passed as an iterable have resolved"
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
let artist_hrefs = new Set();
let data_promise = Promise.all(requests).then(responses => {
return responses.map(response => { // apply to all responses
return response.items.map(song => { // appy to all songs in each response
song.track.artists.forEach(a => { artist_hrefs.add(a.href) });
return [song.track.uri, '"'+song.track.name.replace(/"/g,'')+'"', '"'+song.track.album.name.replace(/"/g,'')+'"',
song.track.duration_ms, song.track.popularity, song.track.album.release_date,
'"'+song.track.artists.map(artist => { return artist.name }).join(',')+'"',
song.added_by.uri, song.added_at]
});
});
});
// Make queries on all the artists, because this json is where genre information lives. Unfortunately this
// means a second wave of traffic.
let genre_promise = data_promise.then(() => {
let artists_promises = Array.from(artist_hrefs).map(href => utils.apiCall(href, access_token));
return Promise.all(artists_promises).then(responses => {
let artist_genres = {};
responses.forEach(artist => { artist_genres[artist.name] = artist.genres.join(','); });
return artist_genres;
});
});
// join genres to the table, label the columns, and put all data in a single csv string
return Promise.all([data_promise, genre_promise]).then(values => {
[data, artist_genres] = values;
data = data.flat();
data.forEach(row => {
artists = row[6].substring(1, row[6].length-1).split(','); // strip the quotes
deduplicated_genres = new Set(artists.map(a => artist_genres[a]).join(",").split(",")); // join and split and take set
row.push('"'+Array.from(deduplicated_genres).filter(x => x != "").join(",")+'"'); // remove empty strings
});
data.unshift(["Spotify URI", "Track Name", "Album Name", "Duration (ms)",
"Popularity", "Release Date", "Artist Name(s)", "Added By", "Added At", "Genres"]);
csv = '';
data.forEach(row => { csv += row.join(",") + "\n" });
return csv;
});
},
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With