Greasy Fork is available in English.

Last.fm Bulk Delete Scrobbles

This script allows a bulk delete of Last.fm scrobbles

// ==UserScript==
// @name         Last.fm Bulk Delete Scrobbles
// @description  This script allows a bulk delete of Last.fm scrobbles
// @namespace    http://tampermonkey.net/
// @version      1.0
// @author       xXMrJackXx
// @match        https://*.last.fm/user/*
// @exclude      https://*.last.fm/user/*/loved
// @require      http://code.jquery.com/jquery-3.3.1.min.js
// @grant        GM_addStyle
// @run-at       document-end
// ==/UserScript==

GM_addStyle(`
.batch-button {
  width: 100px;
  margin-right: 10px;
  border-radius: 6px;
  outline: none;
}
`);

const observer = new MutationObserver(() => $('.chartlist') && process());

// clear localStorage on first load
localStorage.clear();
process();
registerPJax();

function init() {
    var main = $(".col-main");
    var div = $("<div id='batchDeleteButtons'>");
    main.prepend(div);

    // add delete button
    var deleteButton = $('<input type="button" value="Delete" class="batch-button"/>');
    deleteButton.click(function() {
        $('table.chartlist').find('input:checked').each(function() {
            // find delete button and simulate a click of every checked checkbox
            var del = this.closest('tr').querySelector('[data-ajax-form-sets-state="deleted"]');
            del && del.click();
            // remove from localStorage
            localStorage.removeItem(this.id);
            // remove checkbox
            this.remove();
        });
    });
    div.prepend(deleteButton);

    // add check/uncheck all button
    var checkButton = $('<input type="button" value="Check all" data-type="check" class="batch-button"/>');
    checkButton.click(function() {
        var cbs = $(main).find('input.batch-checkbox');
        if($(this).attr('data-type') == 'check') {
            $(this).attr('data-type', 'uncheck');
            $(this).val('Uncheck all');
            cbs.prop('checked', true);
            // add every checkbox id to the localStorage
            cbs.each(function() {
                localStorage.setItem(this.id, this.id);
            });
        } else {
            $(this).attr('data-type', 'check');
            $(this).val('Check all');
            cbs.prop('checked', false);
            // remove every checkbox id from the localStorage
            cbs.each(function() {
                localStorage.removeItem(this.id);
            });
        }
    });
    div.prepend(checkButton);
}

function process() {
    observer.disconnect();

    // add checkboxes for every not already deleted entries
    $('tr[data-ajax-form-state!="deleted"] .chartlist-play').each(function() {
        var track = $(this).siblings('.chartlist-name').find('a.link-block-target').attr('title');
        var timestamp = $(this).siblings('.chartlist-timestamp').find('span[title]').attr('title');
        if (track != undefined && timestamp != undefined) {
            // combination of timestamp and track title should be unique
            var id = normalizeString(timestamp + track);
            var cb = $('<input type="checkbox" class="batch-checkbox" id="' + id + '"/>');
            cb.click(function() {
                if (this.checked) {
                    // add checkbox id to localStorage
                    localStorage.setItem(id, id);
                }
                else {
                    // remove checkbox id from localStorage
                    localStorage.removeItem(id);
                }
            });
            // add checkbox as new first column
            $('<td style="width: 50px;"/>').append(cb).insertBefore($(this));
        }
    });

    // reload localStorage items
    var keys = Object.keys(localStorage), i = keys.length;
    while (i--) {
        // check every checkbox that is contained in the localStorage
        var id = keys[i];
        var cb = $('#' + id);
        if (cb.length) {
            cb.prop('checked', true);
        }
    }

    if ($('.chartlist tbody').length) {
        observer.takeRecords();
        observer.observe($('.chartlist tbody')[0], {childList: true});

        // add buttons only if not already present
        if ($('#batchDeleteButtons').length == 0) {
            init();
        }
    }
}

function registerPJax() {
    document.addEventListener('pjax:end:batch-delete', process);
    window.addEventListener('load', function onLoad() {
        window.removeEventListener('load', onLoad);
        unsafeWindow.jQuery(unsafeWindow.document).on('pjax:end', exportFunction(() => document.dispatchEvent(new CustomEvent('pjax:end:batch-delete')), unsafeWindow));
    });
}

function normalizeString(str) {
    return str.replace(/[^a-z0-9\s]/gi, '').replace(/[_\s]/g, '-')
}