Greasy Fork is available in English.
Display today's script installations and update checks.
// ==UserScript== // @name Greasyfork/Sleazyfork Update Checks Display // @description Display today's script installations and update checks. // @icon https://greasyfork.org/vite/assets/blacklogo96-CxYTSM_T.png // @version 1.4 // @author afkarxyz // @namespace https://github.com/afkarxyz/misc-scripts/ // @supportURL https://github.com/afkarxyz/misc-scripts/issues // @license MIT // @match https://greasyfork.org/* // @match https://sleazyfork.org/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @connect api.greasyfork.org // @connect api.sleazyfork.org // ==/UserScript== (function() { 'use strict'; const CACHE_DURATION = 10 * 60 * 1000; function formatNumber(num) { return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } function displayScriptStats() { document.head.appendChild(Object.assign(document.createElement('style'), { textContent: '.script-list-installs, .script-list-update-checks { opacity: 1; }' })); function addStat(element, label, className) { const list = element.querySelector('.inline-script-stats'); if (!list) return; const dt = document.createElement('dt'); const dd = document.createElement('dd'); dt.className = className; dd.className = className; dt.textContent = label; dd.textContent = '...'; list.lastElementChild.parentNode.insertBefore(dt, list.lastElementChild.nextSibling); dt.after(dd); return dd; } document.querySelectorAll('li[data-script-id]').forEach(script => { const installsElement = addStat(script, 'Installs', 'script-list-installs'); const checksElement = addStat(script, 'Checks', 'script-list-update-checks'); script.dataset.installsElement = installsElement.id = `installs-${script.dataset.scriptId}`; script.dataset.checksElement = checksElement.id = `checks-${script.dataset.scriptId}`; }); } const collectScriptIds = () => Array.from(document.querySelectorAll('li[data-script-id]')) .map(el => ({ scriptId: el.getAttribute('data-script-id'), element: el })); function getCacheKey(scriptId) { const domain = window.location.hostname.includes('sleazyfork') ? 'sleazyfork' : 'greasyfork'; return `${domain}_stats_${scriptId}`; } function getFromCache(scriptId) { const cacheKey = getCacheKey(scriptId); const cachedData = GM_getValue(cacheKey); if (!cachedData) return null; const now = Date.now(); if (now - cachedData.timestamp > CACHE_DURATION) { return null; } return cachedData.data; } function saveToCache(scriptId, data) { const cacheKey = getCacheKey(scriptId); GM_setValue(cacheKey, { timestamp: Date.now(), data: data }); } function getCurrentLanguage() { const pathMatch = window.location.pathname.match(/^\/([a-z]{2}(?:-[A-Z]{2})?)\//); if (pathMatch) { return pathMatch[1]; } return document.documentElement.lang || 'en'; } function fetchStats(scriptInfo) { return new Promise((resolve) => { const cachedStats = getFromCache(scriptInfo.scriptId); if (cachedStats) { resolve(cachedStats); return; } const domain = window.location.hostname.includes('sleazyfork') ? 'sleazyfork.org' : 'greasyfork.org'; const language = getCurrentLanguage(); const apiUrl = `https://api.${domain}/${language}/scripts/${scriptInfo.scriptId}/stats.json`; GM_xmlhttpRequest({ method: 'GET', url: apiUrl, responseType: 'json', onload: function(response) { try { const data = response.response; if (!data || typeof data !== 'object' || Object.keys(data).length === 0) { resolve({ installs: 0, checks: 0 }); return; } const dates = Object.keys(data).sort(); const latestDate = dates[dates.length - 1]; if (!data[latestDate] || typeof data[latestDate] !== 'object') { resolve({ installs: 0, checks: 0 }); return; } const stats = { installs: data[latestDate].installs || 0, checks: data[latestDate].update_checks || 0 }; saveToCache(scriptInfo.scriptId, stats); resolve(stats); } catch (error) { console.error(error); resolve({ installs: 0, checks: 0 }); } }, onerror: function(error) { console.error(error); resolve({ installs: 0, checks: 0 }); } }); }); } function updateStats(scriptInfo, stats) { if (!stats) return; const element = scriptInfo.element; const installsElement = document.getElementById(element.dataset.installsElement); const checksElement = document.getElementById(element.dataset.checksElement); if (installsElement) installsElement.textContent = formatNumber(stats.installs); if (checksElement) checksElement.textContent = formatNumber(stats.checks); } async function init() { displayScriptStats(); const scriptInfos = collectScriptIds(); const fetchPromises = scriptInfos.map(async (scriptInfo) => { try { const stats = await fetchStats(scriptInfo); updateStats(scriptInfo, stats); } catch (error) { console.error(error); updateStats(scriptInfo, { installs: 0, checks: 0 }); } }); await Promise.all(fetchPromises); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();