Greasy Fork is available in English.
Shows play counts of "listeners you know" where it's present
// ==UserScript==// @name last.fm listeners you know// @description Shows play counts of "listeners you know" where it's present// @version 0.0.2// @author wOxxOm// @namespace wOxxOm.scripts// @license MIT License// @match https://www.last.fm/*// @grant GM_xmlhttpRequest// @grant GM_addStyle// @run-at document-start// @connect self// ==/UserScript==/* jshint lastsemic:true, multistr:true, laxbreak:true, -W030, -W041, -W084 */GM_addStyle(`.col-sidebar .chartlist {margin-bottom: 2em;}.col-sidebar .chartlist tr {box-shadow: none!important;}.col-sidebar .chartlist .chartlist-ellipsis-wrap {position: static!important;}.col-sidebar .chartlist.chartlist--big-image .chartlist-ellipsis-wrap {margin-top: -1px!important;margin-left: 1ex;}`);const CACHE_DURATION = 24 * 3600 * 1000; // last.fm updates the stats each daysetTimeout(cleanupCache, 10000);document.addEventListener('DOMContentLoaded', process);document.addEventListener('pjax:end.listeners-you-know', process);window.addEventListener('load', function onLoad() {window.removeEventListener('load', onLoad);unsafeWindow.jQuery(unsafeWindow.document).on('pjax:end', exportFunction(relayPJAX, unsafeWindow));});function relayPJAX() {document.dispatchEvent(new CustomEvent('pjax:end.listeners-you-know'));}function process() {const link = document.querySelector('a[href*="/+listeners/you-know"]');if (!link)return;const cached = readCache(link.href);if (cached)return render(cached);fetchDoc(link.href).then(doc => {const listeners = doc.querySelector('.col-main table');if (!listeners)return;[].forEach.call(listeners.querySelectorAll('.chartlist-index'), e => e.style.display = 'none');const me = listeners.querySelector(`a[href$="${document.querySelector('.auth-link').pathname}"]`);if (me)me.closest('tr').remove();const html = listeners.outerHTML.replace(/\r?\n/g, ' ').replace(/\s\s+/g, ' ');writeCache({url: link.href, html});render(html);});function render(html) {document.querySelector('.col-sidebar').insertAdjacentHTML('afterbegin', html);}}function fetchDoc(options) {options = typeof options == 'string' ? {url: options} : options;options = Object.assign({method: 'GET'}, options);return new Promise(resolve => GM_xmlhttpRequest(Object.assign(options, {onload: r => resolve(new DOMParser().parseFromString(r.responseText, 'text/html'))})));}function readCache(url) {const data = (localStorage[url] || '').split('\0', 2);const expired = +data[0] < Date.now();return !expired && data[1];}function writeCache({url, html, cleanupRetry}) {const expires = Date.now() + CACHE_DURATION;if (!tryCatch(() => localStorage[url] = expires + '\0' + html)) {if (cleanupRetry)return console.error('localStorage write error');cleanupCache({aggressive: true});setIimeout(writeCache, 0, {url, html, cleanupRetry: true});}setTimeout(() => { localStorage.removeItem(url) }, CACHE_DURATION + 1000);}function cleanupCache({aggressive = false} = {}) {Object.keys(localStorage).forEach(k => {if (k.match(/^https?:\/\/[^\t]+$/)) {let meta = (localStorage[k] || '').split('\0', 2);if (+meta[0] < Date.now() || aggressive)localStorage.removeItem(k);}});}function tryCatch(fn) {try { return fn() }catch(e) {}}