Last active
April 11, 2024 13:36
-
-
Save zebapy/2e553225779c9e2bd2adfed06dbe6798 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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