您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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.0
- // @author afkarxyz
- // @namespace https://github.com/afkarxyz/misc-scripts/
- // @supportURL https://github.com/afkarxyz/misc-scripts/issues
- // @license MIT
- // @match https://greasyfork.org/*/users/*
- // @match https://sleazyfork.org/*/users/*
- // @grant GM_setValue
- // @grant GM_getValue
- // ==/UserScript==
- (function() {
- 'use strict';
- const CACHE_DURATION = 10 * 60 * 1000;
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
- const collectScriptLinks = () =>
- Array.from(document.querySelectorAll('li[data-script-id]')).map(element => ({
- url: `https://greasyfork.org/en/scripts/${element.getAttribute('data-script-id')}-${
- element.getAttribute('data-script-name')
- .toLowerCase()
- .replace(/\s+/g, '-')
- .replace(/[^a-z0-9-]/g, '')
- }/stats`,
- name: element.getAttribute('data-script-name'),
- id: element.getAttribute('data-script-id'),
- element: element
- }));
- const fetchWithProxy = async (url) => {
- try {
- const response = await fetch(`https://api.allorigins.win/raw?url=${encodeURIComponent(url)}`);
- return response.ok ? await response.text() : null;
- } catch (error) {
- console.error(`Error fetching ${url}:`, error);
- return null;
- }
- };
- const parseStatsFromHTML = (html) => {
- if (!html) return null;
- const doc = new DOMParser().parseFromString(html, 'text/html');
- const rows = doc.querySelectorAll('.stats-table tbody tr');
- if (!rows.length) return null;
- const cells = rows[rows.length - 1].querySelectorAll('td.numeric');
- return {
- installs: parseInt(cells[0]?.textContent?.trim()) || 0,
- updateChecks: parseInt(cells[1]?.textContent?.trim()) || 0
- };
- };
- const insertOrUpdateStats = (element, label, value, className) => {
- const metadataList = element.querySelector('.inline-script-stats');
- if (!metadataList) return;
- const lastRow = metadataList.lastElementChild;
- if (!lastRow) return;
- let termElement = metadataList.querySelector(`dt.${className}`);
- let descElement = metadataList.querySelector(`dd.${className}`);
- if (!termElement) {
- termElement = document.createElement('dt');
- termElement.className = className;
- lastRow.parentNode.insertBefore(termElement, lastRow.nextSibling);
- }
- if (!descElement) {
- descElement = document.createElement('dd');
- descElement.className = className;
- termElement.after(descElement);
- }
- termElement.textContent = label;
- descElement.textContent = value;
- };
- const initializeStatsLabels = (scripts) => {
- scripts.forEach(scriptInfo => {
- insertOrUpdateStats(
- scriptInfo.element,
- 'Installs',
- '⌛ Checking...',
- 'script-list-installs'
- );
- insertOrUpdateStats(
- scriptInfo.element,
- 'Checks',
- '⌛ Checking...',
- 'script-list-update-checks'
- );
- });
- };
- const getCachedStats = (scriptId) => {
- const cacheKey = `script_stats_${scriptId}`;
- const cachedData = GM_getValue(cacheKey);
- if (cachedData) {
- const { timestamp, stats } = JSON.parse(cachedData);
- const now = Date.now();
- if (now - timestamp < CACHE_DURATION) {
- return stats;
- }
- }
- return null;
- };
- const setCachedStats = (scriptId, stats) => {
- const cacheKey = `script_stats_${scriptId}`;
- const cacheEntry = JSON.stringify({
- timestamp: Date.now(),
- stats: stats
- });
- GM_setValue(cacheKey, cacheEntry);
- };
- const processSingleScript = async (scriptInfo) => {
- console.log(`Processing: ${scriptInfo.name}`);
- const cachedStats = getCachedStats(scriptInfo.id);
- if (cachedStats) {
- insertOrUpdateStats(
- scriptInfo.element,
- 'Installs',
- cachedStats.installs.toLocaleString(),
- 'script-list-installs'
- );
- insertOrUpdateStats(
- scriptInfo.element,
- 'Checks',
- cachedStats.updateChecks.toLocaleString(),
- 'script-list-update-checks'
- );
- return {
- ...scriptInfo,
- ...cachedStats,
- cached: true
- };
- }
- const html = await fetchWithProxy(scriptInfo.url);
- const stats = parseStatsFromHTML(html);
- const result = {
- ...scriptInfo,
- ...(stats || { installs: 0, updateChecks: 0 }),
- error: !stats ? 'Failed to fetch stats' : null
- };
- if (!result.error) {
- setCachedStats(scriptInfo.id, {
- installs: result.installs,
- updateChecks: result.updateChecks
- });
- insertOrUpdateStats(
- scriptInfo.element,
- 'Installs',
- result.installs.toLocaleString(),
- 'script-list-installs'
- );
- insertOrUpdateStats(
- scriptInfo.element,
- 'Checks',
- result.updateChecks.toLocaleString(),
- 'script-list-update-checks'
- );
- } else {
- insertOrUpdateStats(
- scriptInfo.element,
- 'Installs',
- 'Failed to load',
- 'script-list-installs'
- );
- insertOrUpdateStats(
- scriptInfo.element,
- 'Checks',
- 'Failed to load',
- 'script-list-update-checks'
- );
- }
- return result;
- };
- const initScriptStats = async () => {
- try {
- const scripts = collectScriptLinks();
- console.log(`Found ${scripts.length} scripts to process`);
- initializeStatsLabels(scripts);
- const results = [];
- for (const scriptInfo of scripts) {
- const result = await processSingleScript(scriptInfo);
- results.push(result);
- console.log('Result:', result);
- await sleep(result.cached ? 100 : 1000);
- }
- const totals = results.reduce((acc, curr) => ({
- totalInstalls: acc.totalInstalls + (curr.error ? 0 : curr.installs),
- totalUpdateChecks: acc.totalUpdateChecks + (curr.error ? 0 : curr.updateChecks),
- successCount: acc.successCount + (curr.error ? 0 : 1),
- errorCount: acc.errorCount + (curr.error ? 1 : 0),
- cachedCount: acc.cachedCount + (curr.cached ? 1 : 0)
- }), {
- totalInstalls: 0,
- totalUpdateChecks: 0,
- successCount: 0,
- errorCount: 0,
- cachedCount: 0
- });
- console.log('\nAll results:', results);
- console.log('\nTotals:', totals);
- return { results, totals };
- } catch (error) {
- console.error('Error in initScriptStats:', error);
- }
- };
- const style = document.createElement('style');
- style.textContent = `
- .script-list-installs,
- .script-list-update-checks {
- opacity: 0.7;
- font-style: italic;
- }
- .script-list-installs:not(:empty),
- .script-list-update-checks:not(:empty) {
- opacity: 1;
- font-style: normal;
- }
- `;
- document.head.appendChild(style);
- document.readyState === 'loading'
- ? document.addEventListener('DOMContentLoaded', initScriptStats)
- : initScriptStats();
- })();