last.fm listeners you know

Shows play counts of "listeners you know" where it's present

  1. // ==UserScript==
  2. // @name last.fm listeners you know
  3. // @description Shows play counts of "listeners you know" where it's present
  4. // @version 0.0.2
  5. // @author wOxxOm
  6. // @namespace wOxxOm.scripts
  7. // @license MIT License
  8. // @match https://www.last.fm/*
  9. // @grant GM_xmlhttpRequest
  10. // @grant GM_addStyle
  11. // @run-at document-start
  12. // @connect self
  13. // ==/UserScript==
  14.  
  15. /* jshint lastsemic:true, multistr:true, laxbreak:true, -W030, -W041, -W084 */
  16.  
  17. GM_addStyle(`
  18. .col-sidebar .chartlist {
  19. margin-bottom: 2em;
  20. }
  21. .col-sidebar .chartlist tr {
  22. box-shadow: none!important;
  23. }
  24. .col-sidebar .chartlist .chartlist-ellipsis-wrap {
  25. position: static!important;
  26. }
  27. .col-sidebar .chartlist.chartlist--big-image .chartlist-ellipsis-wrap {
  28. margin-top: -1px!important;
  29. margin-left: 1ex;
  30. }
  31. `);
  32.  
  33. const CACHE_DURATION = 24 * 3600 * 1000; // last.fm updates the stats each day
  34. setTimeout(cleanupCache, 10000);
  35.  
  36. document.addEventListener('DOMContentLoaded', process);
  37. document.addEventListener('pjax:end.listeners-you-know', process);
  38. window.addEventListener('load', function onLoad() {
  39. window.removeEventListener('load', onLoad);
  40. unsafeWindow.jQuery(unsafeWindow.document).on('pjax:end', exportFunction(relayPJAX, unsafeWindow));
  41. });
  42.  
  43. function relayPJAX() {
  44. document.dispatchEvent(new CustomEvent('pjax:end.listeners-you-know'));
  45. }
  46.  
  47. function process() {
  48. const link = document.querySelector('a[href*="/+listeners/you-know"]');
  49. if (!link)
  50. return;
  51. const cached = readCache(link.href);
  52. if (cached)
  53. return render(cached);
  54.  
  55. fetchDoc(link.href).then(doc => {
  56. const listeners = doc.querySelector('.col-main table');
  57. if (!listeners)
  58. return;
  59. [].forEach.call(listeners.querySelectorAll('.chartlist-index'), e => e.style.display = 'none');
  60. const me = listeners.querySelector(`a[href$="${document.querySelector('.auth-link').pathname}"]`);
  61. if (me)
  62. me.closest('tr').remove();
  63. const html = listeners.outerHTML.replace(/\r?\n/g, ' ').replace(/\s\s+/g, ' ');
  64. writeCache({url: link.href, html});
  65. render(html);
  66. });
  67.  
  68. function render(html) {
  69. document.querySelector('.col-sidebar').insertAdjacentHTML('afterbegin', html);
  70. }
  71. }
  72.  
  73. function fetchDoc(options) {
  74. options = typeof options == 'string' ? {url: options} : options;
  75. options = Object.assign({method: 'GET'}, options);
  76. return new Promise(resolve => GM_xmlhttpRequest(
  77. Object.assign(options, {
  78. onload: r => resolve(new DOMParser().parseFromString(r.responseText, 'text/html'))
  79. })
  80. ));
  81. }
  82.  
  83. function readCache(url) {
  84. const data = (localStorage[url] || '').split('\0', 2);
  85. const expired = +data[0] < Date.now();
  86. return !expired && data[1];
  87. }
  88.  
  89. function writeCache({url, html, cleanupRetry}) {
  90. const expires = Date.now() + CACHE_DURATION;
  91. if (!tryCatch(() => localStorage[url] = expires + '\0' + html)) {
  92. if (cleanupRetry)
  93. return console.error('localStorage write error');
  94. cleanupCache({aggressive: true});
  95. setIimeout(writeCache, 0, {url, html, cleanupRetry: true});
  96. }
  97. setTimeout(() => { localStorage.removeItem(url) }, CACHE_DURATION + 1000);
  98. }
  99.  
  100. function cleanupCache({aggressive = false} = {}) {
  101. Object.keys(localStorage).forEach(k => {
  102. if (k.match(/^https?:\/\/[^\t]+$/)) {
  103. let meta = (localStorage[k] || '').split('\0', 2);
  104. if (+meta[0] < Date.now() || aggressive)
  105. localStorage.removeItem(k);
  106. }
  107. });
  108. }
  109.  
  110. function tryCatch(fn) {
  111. try { return fn() }
  112. catch(e) {}
  113. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址