Skip to content

Instantly share code, notes, and snippets.

@zebapy
Last active April 11, 2024 13:36
Show Gist options
  • Select an option

  • Save zebapy/2e553225779c9e2bd2adfed06dbe6798 to your computer and use it in GitHub Desktop.

Select an option

Save zebapy/2e553225779c9e2bd2adfed06dbe6798 to your computer and use it in GitHub Desktop.
const fs = require("fs");
const path = require("path");
// open.spotify.com -> open devtools -> network requests -> find a request to grab your Bearer token
const TOKEN = "YOUR_TOKEN";
const fetchSpotify = async ({ endpoint, params, body, method = "GET" }) => {
const baseurl = "https://api.spotify.com/v1/";
const headers = {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
accept: "application/json",
};
const url = new URL(baseurl + endpoint);
url.search = new URLSearchParams(params).toString();
const request = new Request(url, {
headers,
body: body ? JSON.stringify(body) : undefined,
method,
});
// log the request details
console.log(method, request.url, body);
const resp = await fetch(request);
if (!resp.ok) {
console.error(resp.status, resp.statusText);
const text = await resp.text();
throw new Error(text);
}
try {
if (method === "DELETE") {
console.log("skipping response for DELETE");
return;
}
return await resp.json();
} catch (e) {
console.log("Failed to parse response");
console.error(e);
console.error(await resp.text());
}
};
async function main() {
const likedTracks = new Map();
const duplicateTrackIds = [];
let offset = 0;
const limit = 20;
let max = Infinity;
let count = 0;
while (true) {
if (count >= max) {
break;
}
count++;
const tracks = await fetchSpotify({
endpoint: "me/tracks",
params: {
offset,
limit,
},
});
if (tracks.items.length === 0) {
break;
}
for (const item of tracks.items) {
const track = item.track;
const albumId = track.album.id;
if (!likedTracks.has(albumId)) {
likedTracks.set(albumId, track);
} else {
duplicateTrackIds.push(track.id);
}
}
console.log("found duplicates", duplicateTrackIds.length, "tracks");
offset += limit;
}
const deletePromises = [];
// Now that we have all the duplicate track IDs, we can delete them in bulk
for (let i = 0; i < duplicateTrackIds.length; i += 50) {
const idsToDelete = duplicateTrackIds.slice(i, i + 50).join(",");
deletePromises.push(
fetchSpotify({
endpoint: "me/tracks",
params: {
ids: idsToDelete,
},
method: "DELETE",
})
);
}
console.log("deleting", duplicateTrackIds.length, "tracks");
await Promise.all(deletePromises);
}
main()
.then(() => console.log("done"))
.catch(console.error);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment