Greasy Fork is available in English.
查询贴中所有用户观看某部作品的状态
当前为
// ==UserScript==
// @name 查询bangumi贴中用户观看状态
// @namespace http://tampermonkey.net/
// @version 1.5.6
// @description 查询贴中所有用户观看某部作品的状态
// @author Hirasawa Yui
// @run-at document-idle
// @match https://bangumi.tv/group/topic/*
// @match https://bangumi.tv/subject/topic/*
// @match https://bangumi.tv/blog/*
// @match https://bangumi.tv/ep/*
// @match https://bgm.tv/group/topic/*
// @match https://bgm.tv/subject/topic/*
// @match https://bgm.tv/blog/*
// @match https://bgm.tv/ep/*
// @match https://chii.in/group/topic/*
// @match https://chii.in/subject/topic/*
// @match https://chii.in/blog/*
// @match https://chii.in/ep/*
// @match https://bangumi.tv/subject_search/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @license MIT
// ==/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
let autoload = false;
// 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 = 'black';
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);
}
}
// insert input elements below the specified div
function insertInputElements(postTopicDiv) {
if (postTopicDiv) {
const placeholder = document.createElement('div');
const input = document.createElement('input');
input.type = 'text';
input.className = 'searchInputL';
input.placeholder = '输入条目ID';
const button = document.createElement('button');
button.type = 'button';
button.textContent = '获取信息';
button.style.fontSize = '13px';
button.style.border = 'none';
button.style.background = '#4EB1D4';
button.style.color = '#FFF';
button.style.padding = '6px 15px';
button.style.borderRadius = '5px';
button.style.cursor = 'pointer';
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);
}
};
// Create a checkbox
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = autoload; // Set the initial state of the checkbox
let check_tag = document.createElement('span');
check_tag.textContent = '自动加载';
// Change listener for the checkbox
checkbox.addEventListener('change', function() {
autoload = checkbox.checked; // Update the switch variable
GM_setValue('autoload', autoload); // Save the new state
});
const quickSearch = document.createElement('a');
quickSearch.href = 'https://bangumi.tv/subject_search';
quickSearch.textContent = '快速查询条目ID';
quickSearch.style.color = 'pink';
quickSearch.cursor = 'pointer';
quickSearch.target = '_blank';
postTopicDiv.appendChild(placeholder);
postTopicDiv.appendChild(input);
postTopicDiv.appendChild(button);
postTopicDiv.appendChild(checkbox);
postTopicDiv.appendChild(check_tag);
postTopicDiv.appendChild(quickSearch);
postTopicDiv.appendChild(div); // Append the div for subject info
}
}
// Initialize input box and button, and handle click event
async function initInputAndButton() {
autoload = GM_getValue('autoload', false);
if (['https://bangumi.tv/group/',
'https://bangumi.tv/subject/',
'https://bgm.tv/group/',
'https://bgm.tv/subject/',
'https://chii.in/group/',
'https://chii.in/subject/',].some(prefix => document.URL.startsWith(prefix))) {
const postTopicDiv = document.querySelector('.postTopic');
insertInputElements(postTopicDiv);
} else if (['https://bangumi.tv/blog/', 'https://bgm.tv/blog/', 'https://chii.in/blog/'].some(prefix => document.URL.startsWith(prefix))) {
const postTopicDiv = document.querySelector('#viewEntry');
insertInputElements(postTopicDiv);
} else if (['https://bangumi.tv/ep/', 'https://bgm.tv/ep/', 'https://chii.in/ep/'].some(prefix => document.URL.startsWith(prefix))) {
const postTopicDiv = document.querySelector('.epDesc');
insertInputElements(postTopicDiv);
}
// auto load watching status for all users on page 'ep' and 'subject/topic'
if (autoload && ['https://bangumi.tv/subject/topic/',
'https://bgm.tv/subject/topic/',
'https://chii.in/subject/topic/',
'https://bangumi.tv/ep/',
'https://bgm.tv/ep/',
'https://chii.in/ep'].some(prefix => document.URL.startsWith(prefix))) {
let anchor = document.querySelector('#subject_inner_info .avatar');
const subjectId = anchor.href.split('/subject/')[1].split('/')[0];
await fetchSubjectInfo(subjectId);
processAllUsers(subjectId);
}
// search page
if (document.URL.startsWith('https://bangumi.tv/subject_search/')){
const anchorElements = document.querySelectorAll('a.l');
anchorElements.forEach(anchor => {
if(anchor.href.includes('/subject/')) {
const subjectId = anchor.href.split('/subject/')[1].split('/')[0];
let newElement = document.createElement('span');
newElement.style.cursor = 'pointer';
// Add an event listener for the click event
newElement.addEventListener('click', function() {
// Copy text to clipboard logic
navigator.clipboard.writeText(newElement.textContent.replace(/^\(|\)$/g, '')).then(() => {
console.log('Text copied to clipboard');
}).catch(err => {
console.error('Error in copying text: ', err);
});
});
newElement.textContent = `(${subjectId})`;
newElement.style.color = 'red';
newElement.style.fontWeight = 'bold';
anchor.parentNode.insertBefore(newElement, anchor.nextSibling);
}
});
}
}
// 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);
}
});
}
initInputAndButton(); // Initialize and append input box and button
})();