Create a gist now

Instantly share code, notes, and snippets.

Embed
Last.fm duplicate scrobble deleter
var elements = Array.from(document.querySelectorAll('.js-link-block'))
elements.map(function (element) {
var nameElement = element.querySelector('.chartlist-name')
return nameElement && nameElement.textContent.replace(/\s+/g, ' ').trim()
}).forEach(function (name, i, names) {
if (name !== names[i + 1]) return
var deleteButton = elements[i].querySelector('[data-ajax-form-sets-state="deleted"]')
if (deleteButton) deleteButton.click()
location.reload()
})

Last.fm duplicate scrobble deleter

This script serves to delete duplicate scrobbles (i.e. the same song scrobbled multiple times in a row) from your Last.fm library. To use it, paste the script into your browser's console (or the address bar, but prefix the script with javascript:) while logged in and in the own library.

Why would I need this?

Your scrobbler might just have decided to scrobble every song hundreds of times and you can't really remove those scrobbles efficiently. Or, if you're like me, you might have accidentally installed multiple scrobbler extensions at the same time - wondering why multiple scrobbles appear for every song played at a time - and you want to clear them after finding the issue.

Using this script still doesn't necessarily make the process quick, since Last.fm only shows a specific number of scrobbles which can be removed on each page in your library.

How-to (create a bookmarklet)

  1. Copy the following script URL into your clipboard
javascript:var elements=Array.from(document.querySelectorAll('.js-link-block'));elements.map(function(a){var b=a.querySelector('.chartlist-name');return b&&b.textContent.replace(/\s+/g,' ').trim()}).forEach(function(a,b,c){if(a===c[b+1]){var d=elements[b].querySelector('[data-ajax-form-sets-state="deleted"]');d&&d.click(),location.reload()}});
  1. Right-click your browser's bookmark bar and click "Add page..."
  2. Give the bookmark a name, like "Remove duplicates"
  3. Paste the script you copied in step 1 into the bookmark's URL.
  4. Save the bookmark
  5. Open your Last.fm account's library while being logged in (https://www.last.fm/user/_/library).
  6. Opening your bookmark will trigger the script to execute.
  7. Repeat clicking the bookmark as long as there are no duplicates left.

How-to (alternative, one-time way)

  1. Copy the script
var elements=Array.from(document.querySelectorAll('.js-link-block'));elements.map(function(a){var b=a.querySelector('.chartlist-name');return b&&b.textContent.replace(/\s+/g,' ').trim()}).forEach(function(a,b,c){if(a===c[b+1]){var d=elements[b].querySelector('[data-ajax-form-sets-state="deleted"]');d&&d.click(),location.reload()}});
  1. Open your Last.fm account's library while being logged in (https://www.last.fm/user/_/library).
  2. Type javascript: into the address bar and paste the script directly after it. Or, open the dev tools and paste the script into the console.
  3. Press enter. This will remove all duplicates on the current page.
  4. Let the site reload (invoked by the script).
  5. Repeat pasting the script and pressing enter if more duplicates appear at the bottom.
  6. If needed, go to the next page of your library repeat the steps as of step 3.

Why do I need to repeat executing the script?

The script will only remove what's visible on the current library page. After entries were deleted, more duplicates may appear at the bottom. This might happen multiple times. Once one page is finally duplicate-free, the process can be repeated for next pages.

var elements=Array.from(document.querySelectorAll('.js-link-block'));elements.map(function(a){var b=a.querySelector('.chartlist-name');return b&&b.textContent.replace(/\s+/g,' ').trim()}).forEach(function(a,b,c){if(a===c[b+1]){var d=elements[b].querySelector('[data-ajax-form-sets-state="deleted"]');d&&d.click(),location.reload()}});
@d-ly

This comment has been minimized.

Show comment
Hide comment
@d-ly

d-ly Aug 14, 2017

Thank you for this! Question: For whatever reason, my duplicates sometimes scrobble in an alternating pattern, eg. a b a b c d c d. Is there a way to make a variant that takes care of these duplicates as well? Is it just a matter of changing the if (name !== names[i + 1]) return to if (name !== names[i + 2]) return?

d-ly commented Aug 14, 2017

Thank you for this! Question: For whatever reason, my duplicates sometimes scrobble in an alternating pattern, eg. a b a b c d c d. Is there a way to make a variant that takes care of these duplicates as well? Is it just a matter of changing the if (name !== names[i + 1]) return to if (name !== names[i + 2]) return?

@sk22

This comment has been minimized.

Show comment
Hide comment
@sk22

sk22 Dec 20, 2017

Hey @d-ly, sorry I didn't see your comment.
I don't know if you still need this (let me know if you do), but yes, your idea sounds good (although you might have to skip every second element while mapping?)

Owner

sk22 commented Dec 20, 2017

Hey @d-ly, sorry I didn't see your comment.
I don't know if you still need this (let me know if you do), but yes, your idea sounds good (although you might have to skip every second element while mapping?)

@sk22

This comment has been minimized.

Show comment
Hide comment
@sk22

sk22 Dec 20, 2017

Oh dear, just saw that Last.fm scrobbled a few songs around 90 times, good thing I've made this script.
Edit: This was easily hundreds of duplicates, jeez

Owner

sk22 commented Dec 20, 2017

Oh dear, just saw that Last.fm scrobbled a few songs around 90 times, good thing I've made this script.
Edit: This was easily hundreds of duplicates, jeez

@BatteryKinzie

This comment has been minimized.

Show comment
Hide comment
@BatteryKinzie

BatteryKinzie Jan 5, 2018

I don't know Javascript at all, but why doesn't just putting this into a for loop work?


for (i = 0; i < 10; i++) { 
var elements=Array.from(document.querySelectorAll('.js-link-block'));elements.map(function(a){var b=a.querySelector('.chartlist-name');return b&&b.textContent.replace(/\s+/g,' ').trim()}).forEach(function(a,b,c){{var d=elements[b].querySelector('[data-ajax-form-sets-state="deleted"]');d&&d.click(),location.reload()}});
}

Without the for loop it works (by deleting all on one page - oh yeah forgot to mention that I'm deleting all on a page rather than dupes), but now with the loop it still only runs once?

Thanks! Your code has been great help so far.

BatteryKinzie commented Jan 5, 2018

I don't know Javascript at all, but why doesn't just putting this into a for loop work?


for (i = 0; i < 10; i++) { 
var elements=Array.from(document.querySelectorAll('.js-link-block'));elements.map(function(a){var b=a.querySelector('.chartlist-name');return b&&b.textContent.replace(/\s+/g,' ').trim()}).forEach(function(a,b,c){{var d=elements[b].querySelector('[data-ajax-form-sets-state="deleted"]');d&&d.click(),location.reload()}});
}

Without the for loop it works (by deleting all on one page - oh yeah forgot to mention that I'm deleting all on a page rather than dupes), but now with the loop it still only runs once?

Thanks! Your code has been great help so far.

@MFSUPERVILL4IN

This comment has been minimized.

Show comment
Hide comment
@MFSUPERVILL4IN

MFSUPERVILL4IN Jan 19, 2018

Hey sk22, thank you for this script. It helped me delete over 500 scrobbles.

I'm with the other guy, over the past two weeks Last.fm has scrobbled a ton of duplicates, but most of them were in a b a b a b pattern, or a b c a b c a b c. If you ever made a variant that would delete those duplicates that would be awesome, then I could clean up the rest pretty easily, or if I ever need it again in the future.

Thanks again though, mate.

edit. Found a great chrome extension that adds a big delete button beside each track, it makes it a lot easier to clean up your scrobbles.
https://github.com/padraigfl/chrome-extension-lastfm-old-delete-button/tree/add-listeners?files=1v

MFSUPERVILL4IN commented Jan 19, 2018

Hey sk22, thank you for this script. It helped me delete over 500 scrobbles.

I'm with the other guy, over the past two weeks Last.fm has scrobbled a ton of duplicates, but most of them were in a b a b a b pattern, or a b c a b c a b c. If you ever made a variant that would delete those duplicates that would be awesome, then I could clean up the rest pretty easily, or if I ever need it again in the future.

Thanks again though, mate.

edit. Found a great chrome extension that adds a big delete button beside each track, it makes it a lot easier to clean up your scrobbles.
https://github.com/padraigfl/chrome-extension-lastfm-old-delete-button/tree/add-listeners?files=1v

@huw

This comment has been minimized.

Show comment
Hide comment
@huw

huw Jan 23, 2018

@MFSUPERVILL4IN It's a quick edit to do what you needed — I had the same problem, but sometimes with loops of even more (5+). Just change the variable num=5 to whatever you want. 50 also does the trick if you're just viewing one page and are pretty sure you're going to just wipe them all.

var num = 5
var elements = Array.from(document.querySelectorAll('.js-link-block'))
elements.map(function (element) {
  var nameElement = element.querySelector('.chartlist-name')
  return nameElement && nameElement.textContent.replace(/\s+/g, ' ').trim()
}).forEach(function (name, i, names) {
  if (!names.slice(i + 1, i + 1 + num).includes(name)) return
  var deleteButton = elements[i].querySelector('[data-ajax-form-sets-state="deleted"]')
  if (deleteButton) deleteButton.click()
  location.reload()
})

huw commented Jan 23, 2018

@MFSUPERVILL4IN It's a quick edit to do what you needed — I had the same problem, but sometimes with loops of even more (5+). Just change the variable num=5 to whatever you want. 50 also does the trick if you're just viewing one page and are pretty sure you're going to just wipe them all.

var num = 5
var elements = Array.from(document.querySelectorAll('.js-link-block'))
elements.map(function (element) {
  var nameElement = element.querySelector('.chartlist-name')
  return nameElement && nameElement.textContent.replace(/\s+/g, ' ').trim()
}).forEach(function (name, i, names) {
  if (!names.slice(i + 1, i + 1 + num).includes(name)) return
  var deleteButton = elements[i].querySelector('[data-ajax-form-sets-state="deleted"]')
  if (deleteButton) deleteButton.click()
  location.reload()
})
@valenzine

This comment has been minimized.

Show comment
Hide comment
@valenzine

valenzine Jan 31, 2018

You saved my day. Thank you.

You saved my day. Thank you.

@sk22

This comment has been minimized.

Show comment
Hide comment
@sk22

sk22 Mar 19, 2018

@BatteryKinzie Well, after removing the scrobbles on one page, the library page needs to reload, which basically kills any running script. That's why your loop approach doesn't work: The script stops when reloading the page.
Sure, one could, like, put this in a browser extension or something, so the script can continue while the page reloads. (Maybe a web worker would also work, but I've never used them, so idk).

Owner

sk22 commented Mar 19, 2018

@BatteryKinzie Well, after removing the scrobbles on one page, the library page needs to reload, which basically kills any running script. That's why your loop approach doesn't work: The script stops when reloading the page.
Sure, one could, like, put this in a browser extension or something, so the script can continue while the page reloads. (Maybe a web worker would also work, but I've never used them, so idk).

@sk22

This comment has been minimized.

Show comment
Hide comment
@sk22

sk22 Mar 19, 2018

Thank you, @huw!

Owner

sk22 commented Mar 19, 2018

Thank you, @huw!

@sk22

This comment has been minimized.

Show comment
Hide comment
@sk22

sk22 Mar 19, 2018

Glad I could help, @valenzine!

Owner

sk22 commented Mar 19, 2018

Glad I could help, @valenzine!

@edrozaor-usrex

This comment has been minimized.

Show comment
Hide comment
@edrozaor-usrex

edrozaor-usrex Apr 24, 2018

This is really helpful. My 2 issues thus far though are:

  1. This doesn't delete scrobbles that aren't consecutively listed in the library. For instance, back when I was using my iPod I had instances of Song A being scrobbled at a certain timestamp and at another timestamp + x minutes later. I believe this occurred when I sync my iPod to my laptop. Since I was also listening to other songs, what's listed in my library would be Song A - timestamp, Song B - timestamp + x. Song A - timestamp + y, etc.

  2. Unfortunately, I accidentally deleted my scrobbles of my all-time played track. I was just testing if this would delete the scrobbles with the same timestamp. But since there were no other listed tracks there (because I visited the scrobbles of a specific track), it deleted everything in that page. =/

Anyway, kudos to you @sk22. I had to create an account here in github to personally acknowledge your efforts, especially that I've been a last.fm user for more than a decade now.

This is really helpful. My 2 issues thus far though are:

  1. This doesn't delete scrobbles that aren't consecutively listed in the library. For instance, back when I was using my iPod I had instances of Song A being scrobbled at a certain timestamp and at another timestamp + x minutes later. I believe this occurred when I sync my iPod to my laptop. Since I was also listening to other songs, what's listed in my library would be Song A - timestamp, Song B - timestamp + x. Song A - timestamp + y, etc.

  2. Unfortunately, I accidentally deleted my scrobbles of my all-time played track. I was just testing if this would delete the scrobbles with the same timestamp. But since there were no other listed tracks there (because I visited the scrobbles of a specific track), it deleted everything in that page. =/

Anyway, kudos to you @sk22. I had to create an account here in github to personally acknowledge your efforts, especially that I've been a last.fm user for more than a decade now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment