// ==UserScript== // @name 查询bangumi贴中用户观看状态 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 查询贴中所有用户观看某部作品的状态 // @author Hirasawa Yui // @match https://bangumi.tv/group/topic/* // @match https://bangumi.tv/subject/topic/* // @grant none // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; let userInfoMap = new Map(); // To store results for each user ID let validSubjectId = false; // Flag to track if the subject ID is valid // Function to create or update an element with the fetched data function insertOrUpdateAdjacentToAnchor(anchor, siblingClass, userId, subjectId) { let key = `${userId}-${subjectId}`; let text = userInfoMap.get(key) || "No info available"; // Find existing element or create new one let newElement = anchor.parentNode.querySelector('.userData'); if (!newElement) { newElement = anchor.parentNode.parentNode.querySelector('.userData'); } if (!newElement) { newElement = document.createElement('span'); newElement.className = 'userData'; // Assign a class for easy identification let inserted = false; let nextElement = anchor.parentNode.nextSibling; while (nextElement) { if (nextElement.nodeType === 1 && nextElement.matches(siblingClass)) { nextElement.parentNode.insertBefore(newElement, nextElement.nextSibling); inserted = true; break; } nextElement = nextElement.nextSibling; } if (!inserted) { anchor.parentNode.insertBefore(newElement, anchor.nextSibling); } } // Update text content and color newElement.textContent = `(${text})`; newElement.style.color = 'red'; newElement.style.fontWeight = 'bold'; } // Fetch collection data for a specific user ID and subject ID async function fetchUserInfo(userId, subjectId) { const url = `https://api.bgm.tv/v0/users/${userId}/collections/${subjectId}`; // Skip fetching if the user is already in the map with the same subjectId or subject ID is invalid if (!validSubjectId || userInfoMap.has(`${userId}-${subjectId}`)) return; try { const response = await fetch(url); if (!response.ok) { userInfoMap.set(`${userId}-${subjectId}`, "TA没看过这部作品"); return; } const data = await response.json(); let infoText = getInfoTextFromData(data); userInfoMap.set(`${userId}-${subjectId}`, infoText); } catch (error) { console.error('Error fetching or processing data', error); userInfoMap.set(`${userId}-${subjectId}`, "Error fetching data"); } } // Convert fetched data to a user-friendly text function getInfoTextFromData(data) { if (data.type === 1) return "TA想看这部作品"; else if (data.type === 2) return data.rate ? `TA打了${data.rate}分` : 'TA看过这部作品'; else if (data.type === 3) return data.ep_status ? `TA看到了${data.ep_status}集` : 'TA在看这部作品'; else if (data.type === 4) return "TA搁置了这部作品"; else if (data.type === 5) return "TA抛弃了这部作品"; else return "未知状态"; } // Fetch subject info by subject ID async function fetchSubjectInfo(subjectId) { const url = `https://api.bgm.tv/v0/subjects/${subjectId}`; try { const response = await fetch(url); const div = document.querySelector('.subjectInfo'); if (!response.ok) { div.textContent = "无效条目"; validSubjectId = false; // Mark subject ID as invalid userInfoMap.clear(); // Clear previous info if any return; } const data = await response.json(); div.textContent = `你当前正在查询所有用户观看 ${data.name} 的状态`; div.style.color = 'green'; validSubjectId = true; // Mark subject ID as valid } catch (error) { console.error('Error fetching subject data', error); } } // Initialize input box and button, and handle click event function initInputAndButton() { const postTopicDiv = document.querySelector('.postTopic'); if (postTopicDiv) { const input = document.createElement('input'); input.type = 'text'; input.className = 'searchInputL'; input.placeholder = '输入条目ID'; const button = document.createElement('button'); button.className = 'searchBtnL'; button.textContent = '获取信息'; const div = document.createElement('div'); div.className = 'subjectInfo'; // For displaying subject info button.onclick = async function() { const subjectId = input.value.trim(); if (!subjectId) return; // Do nothing if the subject ID is empty await fetchSubjectInfo(subjectId); if (validSubjectId) { // Only process users if the subject ID is valid processAllUsers(subjectId); } }; postTopicDiv.appendChild(input); postTopicDiv.appendChild(button); postTopicDiv.appendChild(div); // Append the div for subject info } } // Fetch user info for all users and then process anchor tags async function processAllUsers(subjectId) { const anchorElements = document.querySelectorAll('a.l'); let fetchPromises = []; anchorElements.forEach(anchor => { if(anchor.href.includes('/user/')) { const userId = anchor.href.split('/user/')[1].split('/')[0]; if (!userInfoMap.has(`${userId}-${subjectId}`)) { fetchPromises.push(fetchUserInfo(userId, subjectId)); } } }); await Promise.all(fetchPromises); anchorElements.forEach(anchor => { if(anchor.href.includes('/user/')) { const userId = anchor.href.split('/user/')[1].split('/')[0]; insertOrUpdateAdjacentToAnchor(anchor, 'span.sign.tip_j', userId, subjectId); } }); } window.addEventListener('load', function() { initInputAndButton(); // Initialize and append input box and button }); })();