Skip to content

Instantly share code, notes, and snippets.

@orenyomtov
Last active February 7, 2021 14:19
Show Gist options
  • Save orenyomtov/2c2b8f430efb8841064ee67f35055a00 to your computer and use it in GitHub Desktop.
Save orenyomtov/2c2b8f430efb8841064ee67f35055a00 to your computer and use it in GitHub Desktop.
(async function () {
async function fetchAccessToken() {
let r = await fetch('https://open.spotify.com/get_access_token?reason=transport&productType=web_player');
let j = await r.json();
return j.accessToken;
}
async function fetchUserId() {
let r = await fetch('https://api.spotify.com/v1/me', { headers: { authorization: `Bearer ${accessToken}` } });
let j = await r.json();
return j.id;
}
async function fetchAllTracksInPlaylist(playlistId, next = false) {
if (next === null)
return []
let url = next ? next : `https://api.spotify.com/v1/playlists/${playlistId}/tracks?limit=100&market=from_token&type=track,episode`;
let r = await fetch(url, { headers: { authorization: `Bearer ${accessToken}` } });
let j = await r.json();
return extractTrackIds(j).concat(await fetchAllTracksInPlaylist(playlistId, j.next))
}
function extractTrackIds(j) {
return j.items.map(item => item.track.id);
}
async function fetchAllUserPlaylists(userId, next = false) {
if (next === null)
return []
let url = next ? next : `https://api.spotify.com/v1/users/${userId}/playlists?offset=0&limit=50`;
let r = await fetch(url, { headers: { authorization: `Bearer ${accessToken}` } });
let j = await r.json();
return (await extractPlaylists(j.items.filter(x => x.owner.id == userId))).concat(await fetchAllUserPlaylists(userId, j.next))
}
async function extractPlaylists(playlists) {
return await Promise.all(playlists.map(async (item) => {
return {
"name": item.name,
"href": item.external_urls.spotify,
"id": item.id,
"tracks": await fetchAllTracksInPlaylist(item.id)
}
}));
}
function injectCSS() {
var styles = `
/* Tooltip container */
.tooltip {
position: relative;
display: inline-block;
}
/* Tooltip text */
.tooltip .tooltiptext {
visibility: hidden;
width: 250px;
background-color: #555;
color: #fff;
text-align: center;
padding: 5px 0;
border-radius: 6px;
/* Position the tooltip text */
position: absolute;
z-index: 1;
bottom: auto;
left: 128%;
/* Fade in tooltip */
opacity: 0;
transition: opacity 0.3s;
}
/* Show the tooltip text when you mouse over the tooltip container */
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
`
var styleSheet = document.createElement("style")
styleSheet.type = "text/css"
styleSheet.innerText = styles
document.head.appendChild(styleSheet)
}
function addPlaylistCounters() {
let trackRows = Array.from(document.querySelectorAll('div[data-testid="tracklist-row"]:not(.playlistCounterAdded)'));
for (trackRow of trackRows) {
try {
console.log(trackRow)
let playlistCounter = document.createElement("div");
trackRow.insertBefore(playlistCounter, trackRow.children[trackRow.childElementCount - 1]);
let trackId = getTrackIdFromTrackRow(trackRow);
let playlists = getPlaylistsContainingTrack(trackId);
playlistCounter.outerHTML = `
<div class="tracklist-col playlistsCounter">
<div class="tracklist-top-align tooltip">
(${playlists.length})<span class="tooltiptext">This track exists in the following playlists:
${playlists.map(playlist => `<br><a href="${playlist.href}">${playlist.name}</a>`).join("")}
</span>
</div>
</div>
`
} catch (e) {
console.error(e);
}
trackRow.classList.add('playlistCounterAdded')
}
setTimeout(addPlaylistCounters, 100);
}
function getTrackIdFromTrackRow(trackRow) {
let reactContextmenuWrapper = trackRow.parentElement.parentElement;
let reactInternalInstancePropertyName = Object.keys(reactContextmenuWrapper).find(key => key.startsWith('__reactInternalInstance'))
let reactInternalInstance = reactContextmenuWrapper[reactInternalInstancePropertyName];
return reactInternalInstance.child.key.split(':').pop().split('/')[0];
}
function getPlaylistsContainingTrack(trackId) {
return playlists.filter(playlist => playlist.tracks.includes(trackId));
}
injectCSS();
alert('Fetching all your playlists and their tracks.. Please wait.. This can take up to a minute..')
let accessToken = await fetchAccessToken();
let userId = await fetchUserId(accessToken);
let playlists = await fetchAllUserPlaylists(userId);
alert('Playlists fetching completed :)')
addPlaylistCounters();
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment