// ==UserScript== // @name BgmSyncF // @version 0.2.3 // @namespace https://jirehlov.com // @description https://bgm.tv/group/topic/386575 // @include /^https?:\/\/(bgm\.tv|chii\.in|bangumi\.tv)\/user/.+/ // @author Jirehlov // @grant none // @license MIT // @downloadURL none // ==/UserScript== (function () { 'use strict'; // Check if the current page is under /user/username const isUserPage = /^\/user\/[^/]+$/.test(window.location.pathname); if (!isUserPage) { return; // Stop script execution if not on the user page } const limit = 50; let guess = 1000000; let totalItems = 0; const allData = []; let calculateButton; let buttonCounter = 0; const [username, page = '', subpage = ''] = (() => { const {pathname} = window.location; if (/^\/user/.test(pathname)) { return pathname.match(/\/user\/(\w+)\/?(\w+)?\/?(\w+)?/).slice(1, 4); } return [ '', '', '' ]; })(); if (!username) { throw new Error('Username is not detected'); } let countBothAbove7 = 0; let countRateAbove7 = 0; let subject_type = [ 1, 2, 3, 4, 6 ]; let subject_type_index = 0; let percentageBarDiv = null; const nameDiv = document.querySelector('.name'); const realname = nameDiv.querySelector('a').textContent; // Define constants for cache keys const CACHE_KEY_COUNT_RATE_ABOVE_7 = 'countRateAbove7'; const CACHE_KEY_COUNT_BOTH_ABOVE_7 = 'countBothAbove7'; const CACHE_KEY_SYNC_RATE = 'syncRate'; const CACHE_KEY_TIMESTAMP = 'cacheTimestamp'; // Function to check if the cache is still valid function isCacheValid() { const cacheTimestamp = localStorage.getItem(CACHE_KEY_TIMESTAMP); if (cacheTimestamp) { const currentTime = new Date().getTime(); const cachedTime = parseInt(cacheTimestamp, 10); const elapsedTime = currentTime - cachedTime; // Cache is valid for 7 days (7 * 24 * 60 * 60 * 1000 milliseconds) return elapsedTime < 7 * 24 * 60 * 60 * 1000; } return false; // Cache not found or expired } async function fetchData(offset, userAgent, cookie) { const url = `https://api.bgm.tv/v0/users/${ username }/collections?subject_type=${ subject_type[subject_type_index] }&type=2&limit=${ limit }&offset=${ offset }`; const headers = { 'Accept': 'application/json', 'User-Agent': userAgent, 'Cookie': cookie }; const response = await fetch(url, { headers }); const data = await response.json(); return data; } async function fetchAllData() { const userAgent = window.navigator.userAgent; const cookie = document.cookie; // Check if cache is valid if (isCacheValid()) { // Update button text to indicate calculation progress calculateButton.textContent = '已命中缓存'; // Cache hit, retrieve values from local storage countRateAbove7 = parseInt(localStorage.getItem(CACHE_KEY_COUNT_RATE_ABOVE_7), 10) || 0; countBothAbove7 = parseInt(localStorage.getItem(CACHE_KEY_COUNT_BOTH_ABOVE_7), 10) || 0; // Calculate sync rate from cached values let syncRate = 0; if (countRateAbove7 > 0) { syncRate = countBothAbove7 / countRateAbove7 * 100; } // Update the UI with cached values updateUI(syncRate); } else { // Cache miss, fetch data from the API const userAgent = window.navigator.userAgent; const cookie = document.cookie; // Update button text to indicate calculation progress calculateButton.textContent = '计算中...'; for (let i = 0; i < subject_type.length; i++) { subject_type_index = i; const initialData = await fetchData(guess, userAgent, cookie); if ('description' in initialData && initialData.description.includes('equal to')) { totalItems = parseInt(initialData.description.split('equal to ')[1]); console.log(`Updated totalItems to: ${ totalItems }`); } for (let offset = 0; offset < totalItems; offset += limit) { const data = await fetchData(offset, userAgent, cookie); allData.push(...data.data); console.log(`Fetched ${ offset + 1 }-${ offset + limit } items...`); // Update button text with cyclic progress dots updateButtonText(offset); } } for (const item of allData) { const rate = parseFloat(item.rate || 0); const score = parseFloat(item.subject && item.subject.score !== undefined ? item.subject.score : 0); if (rate >= 7 || rate === 0) { countRateAbove7++; } if ((rate >= 7 || rate === 0) && score >= 7) { countBothAbove7++; } } // Update button text to indicate calculation is complete calculateButton.textContent = '计算全站同步率'; // Update local storage with fetched values and cache timestamp localStorage.setItem(CACHE_KEY_COUNT_RATE_ABOVE_7, countRateAbove7); localStorage.setItem(CACHE_KEY_COUNT_BOTH_ABOVE_7, countBothAbove7); localStorage.setItem(CACHE_KEY_TIMESTAMP, new Date().getTime()); // Calculate sync rate from fetched values let syncRate = 0; if (countRateAbove7 > 0) { syncRate = countBothAbove7 / countRateAbove7 * 100; } updateUI(syncRate); } } function updateUI() { // Add userSynchronize div if not present let synchronizeDiv = document.querySelector('.userSynchronize'); if (!synchronizeDiv) { const userBoxDiv = document.querySelector('.user_box.clearit'); if (userBoxDiv) { synchronizeDiv = document.createElement('div'); synchronizeDiv.className = 'userSynchronize'; userBoxDiv.appendChild(synchronizeDiv); } } // Add the percentage bar directly to the existing userSynchronize div if (!percentageBarDiv) { const synchronizeDiv = document.querySelector('.userSynchronize'); if (synchronizeDiv) { percentageBarDiv = document.createElement('div'); synchronizeDiv.appendChild(percentageBarDiv); } } if (percentageBarDiv) { let syncRate = 0; if (countRateAbove7 > 0) { syncRate = countBothAbove7 / countRateAbove7 * 100; } const percentageBar = `