Greasy Fork is available in English.
https://bgm.tv/group/topic/386575
当前为
// ==UserScript==
// @name BgmSyncF
// @version 0.2.4
// @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
// ==/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;
// Function to set data in local storage with expiration time
function setLocalStorageWithExpiration(key, value, expirationTimeInDays) {
const expirationTimestamp = Date.now() + expirationTimeInDays * 24 * 60 * 60 * 1000;
const dataToStore = {
value,
expiration: expirationTimestamp
};
localStorage.setItem(key, JSON.stringify(dataToStore));
}
// Function to get data from local storage
function getLocalStorage(key) {
const storedData = localStorage.getItem(key);
if (storedData) {
const data = JSON.parse(storedData);
if (data.expiration && data.expiration > Date.now()) {
return data.value;
}
localStorage.removeItem(key); // Remove expired data
}
return null;
}
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 cachedCountRateAbove7 = getLocalStorage(`${ username }_countRateAbove7`);
const cachedCountBothAbove7 = getLocalStorage(`${ username }_countBothAbove7`);
const cachedSyncRate = getLocalStorage(`${ username }_syncRate`);
// Check if cache is valid
if (cachedCountRateAbove7 !== null && cachedCountBothAbove7 !== null && cachedSyncRate !== null) {
countRateAbove7 = cachedCountRateAbove7;
countBothAbove7 = cachedCountBothAbove7;
// Update button text to indicate calculation progress
calculateButton.textContent = '已命中缓存';
// 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 = '计算全站同步率';
// Store data in local storage with a 7-day expiration time
setLocalStorageWithExpiration(`${ username }_countRateAbove7`, countRateAbove7, 7);
setLocalStorageWithExpiration(`${ username }_countBothAbove7`, countBothAbove7, 7);
setLocalStorageWithExpiration(`${ username }_syncRate`, (countBothAbove7 / countRateAbove7).toFixed(2), 7);
// 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 = `
<h3>${ realname }与全站的同步率</h3><small class="hot">/ ${ countBothAbove7 }个共同喜好</small><p class="bar"><span class="percent_text rr">${ syncRate.toFixed(2) }%</span> <span class="percent" style="width:${ syncRate.toFixed(2) }%"></span> </p>
`;
percentageBarDiv.innerHTML = percentageBar;
}
console.log(`Number of items with rate >= 7: ${ countRateAbove7 }`);
console.log(`Number of items with both rate and score >= 7: ${ countBothAbove7 }`);
console.log(`Sync rate: ${ (countBothAbove7 / countRateAbove7).toFixed(2) }`);
}
function updateButtonText(offset) {
if (buttonCounter < 5) {
calculateButton.textContent = '计算中' + '.'.repeat(buttonCounter);
buttonCounter++;
} else {
calculateButton.textContent = '计算中.';
buttonCounter = 1;
}
}
function addButton() {
const link = document.createElement('a');
link.href = 'javascript:void(0)';
link.textContent = '计算全站同步率';
link.className = 'chiiBtn';
link.addEventListener('click', fetchAllData);
const actionsDiv = document.querySelector('.nameSingle > .inner > .actions');
actionsDiv.appendChild(link);
calculateButton = link; // Store the reference to the button
}
addButton();
}());