You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

103 lines
4.8 KiB

<!doctype html>
<html lang="en">
  <!-- you found the secret, yay -->
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <link href="favicon.png" rel="icon">
    <link href="styles.css" rel="stylesheet">
    <title>a javascript oneliner</title>
    <meta name="description" content="how to delete scrobbles from last.fm">
  </head>
  <body>
    <header>
      <h1><a href="..">&larr;</a>A Javascript Oneliner</h1>
      <time datetime="2020-03-25">25 March 2020</time>
    </header>
    <main>
      <section>
        <h2>How to Delete Old Scrobbles from Last.fm</h2>
        <p>
          Like most reading this now, I am currently <em>riding out the
          storm</em> of coronavirus. Luckily I have my two cats to keep me
          company. Please, everyone, wash your hands! /PSA
        </p>
        <p>
          I was listening to some new music, when I remembered I had an old
          last.fm account with some data that would be better left forgotten.
          For those unaware, last.fm is a proprietary service that tracks your
          music listening habits to give you back data and recommendations. It
          can integrate with many different music players to continually upload
          what you listen to (each song is called a <em>scrobble</em>) to the
          world wide web. For anyone who cares, there are two (lackluster) FLOSS
          alternatives: <a href="https://libre.fm/">Libre.fm</a>
          and <a href="https://listenbrainz.org/">ListenBrainz</a>.
        </p>
        <p>
          With a seemingly simple problem to solve, delete all my scrobbles
          while keeping my account if I ever need it later, I logged in and did
          what any reasonable person would do: go to the settings page. Yet,
          there was no one-click option. I found that I would need to manually
          click to delete each one from my history. Yikes! There has to be a
          better way, and there is.
        </p>
      </section>
      <section>
        <h2>Ctrl-Shift-i</h2>
        <p>
          The dev-tools were my next stop. I found that each scrobble in the
          library page has its own <code>&lt;form&gt;</code> element with all
          the data required to make a <code>POST</code> to delete it.
        </p>
        <p>
          The first thing I need to do is get all
          those <code>&lt;form&gt;</code>s. That is a pretty simple by
          using <code>document.querySelectorAll</code>. The action attributes
          each end with <code>"/library/delete"</code>, so I will search for that:
        </p>
        <code>let forms = document.querySelectorAll('form$="/library/delete"')</code>
        <p>
          Now that we have a <code>NodeList</code>, we can submit each form:
        </p>
        <code>forms.forEach(fm => fm.submit())</code>
        <p>
          Alright, all done! <em>Wait, what did you say? It doesn&rsquo;t work?</em>
          This solution may seem correct, but it refreshes the page after only
          submitting the first form. We want to do it for all of them at once.
        </p>
      </section>
      <section>
        <h2>Enter: window.fetch</h2>
        <p>
          The <code>fetch</code> api is really a treat. Compared to the ugly old
          <code>XMLHttpRequest</code> (shortened to XHR), fetch is much more
          developer friendly. Changing from <code>.submit()</code>
          to <code>fetch</code> is not much effort. The new code looks like:
        </p>
        <code>forms.forEach(fm => fetch(`${window.location}/delete`, { method: 'POST', body: new FormData(fm) }))</code>
        <p>
          The <code>delete</code> endpoint is <code>POST</code>ed to with the
          parameters from the form. There are fifty scrobbles per page, so after
          waiting a few seconds, refresh to see the scrobble count be subtracted
          by fifty. Just past the code in again, and repeat.
        </p>
      </section>
      <section>
        <h2>Recap</h2>
        <p>
          To delete scrobbles from you last.fm library do the following: (1) Go
          to your library page. (2) Run the following code in the console. (3)
          Refresh the page and repeat step two until all scrobbles are gone.
        </p>
        <code>document.querySelectorAll('form$="/library/delete"').forEach(fm => fetch(`${window.location}/delete`, { method: 'POST', body: new FormData(fm) }))</code>
        <p>
          Thanks for sticking around! This was my first ever technical blog
          post, so feedback is much appreciated. <a href="https://lobste.rs/s/zw0ssa/javascript_oneliner">Comment on Lobsters</a>.
        </p>
      </section>
    </main>
    <footer>
      <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" src="images/cc.png"></a>
    </footer>
  </body>
</html>