// ==UserScript==
// @name 条目讨论页显示用户评价
// @namespace https://bgm.tv/group/topic/411796
// @version 0.0.1
// @description 也可以干别的
// @author mmv
// @include http*://bgm.tv/subject/topic/*
// @include http*://bgm.tv/blog/*
// @include http*://bgm.tv/ep/*
// @include http*://bgm.tv/character/*
// @include http*://bgm.tv/group/topic/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=bgm.tv
// @license MIT
// @grant none
// ==/UserScript==
(function() {
'use strict';
const styleSheet = document.createElement("style");
styleSheet.innerText = `
.ccf-star { margin-left: 5px; }
.ccf-status { margin-left: 5px; color: #999; font-size: 12px; font-weight: normal; }
.ccf-comment {
margin-left: 5px;
position: relative;
cursor: help;
}
.ccf-comment::after {
content: attr(data-comment);
position: absolute;
top: 100%;
left: 0;
background-color: rgba(254, 254, 254, 0.9);
box-shadow: inset 0 1px 1px hsla(0, 0%, 100%, 0.3), inset 0 -1px 0 hsla(0, 0%, 100%, 0.1), 0 2px 4px hsla(0, 0%, 0%, 0.2);
backdrop-filter: blur(5px);
border-radius: 5px;
padding: 5px;
width: 250px;
z-index: 1000;
font-weight: normal;
font-size: 12px;
color: rgba(0, 0, 0, .7);
cursor: text;
transform: scale(0);
}
.ccf-comment:hover::after {
transform: scale(1);
}
html[data-theme="dark"] .ccf-comment::after {
background: rgba(80, 80, 80, 0.7);
color: rgba(255, 255, 255, .7);
}
.loader {
margin-left: 5px;
border: 2px solid transparent; /* Light grey */
border-top: 2px solid #F09199; /* Blue */
border-radius: 50%;
width: 10px;
height: 10px;
animation: spin 2s linear infinite;
display: inline-block;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(styleSheet);
if (location.pathname.startsWith('/subject/topic') || location.pathname.startsWith('/ep')) {
const userLinks = document.querySelectorAll('.inner strong a');
const subject_id = document.querySelector('#subject_inner_info a').href.split('/').pop();
if (!userLinks || !subject_id) return;
lazyRender(userLinks, subject_id);
} else if (location.pathname.startsWith('/blog')) {
const userLinks = [document.querySelector('#pageHeader a')].concat([...document.querySelectorAll('#columnA .inner strong a')]);
const relatedSubjects = document.querySelectorAll('#related_subject_list .ll a');
if (!userLinks || !relatedSubjects) return;
multiSubjectsRender(userLinks, relatedSubjects);
} else if (location.pathname.startsWith('/character')) {
const userLinks = document.querySelectorAll('.inner strong a');
const castSubjects = document.querySelectorAll('.browserList .inner a[href^="/subject/"]');
if (!userLinks || !castSubjects) return;
multiSubjectsRender(userLinks, castSubjects);
} else if (location.pathname.startsWith('/group/topic')) {
const userLinks = document.querySelectorAll('#columnInSubjectA .inner strong a');
if (!userLinks) return;
window.ccf = subject_id => {
lazyRender(userLinks, subject_id, true);
}
}
async function getUserData(username, subject_id) {
const cachedData = sessionStorage.getItem(`userData_${username}_${subject_id}`);
if (cachedData) return JSON.parse(cachedData);
const response = await fetch(`https://api.bgm.tv/v0/users/${username}/collections/${subject_id}`);
if (response.ok) {
const data = await response.json();
sessionStorage.setItem(`userData_${username}_${subject_id}`, JSON.stringify(data));
return data;
} else if (response.status === 404) {
const data = { not_found: true }
sessionStorage.setItem(`userData_${username}_${subject_id}`, JSON.stringify(data));
return data;
} else {
throw new Error('Network response was not ok: ', response);
}
}
async function renderUserData(userLink, subject_id, showSubject=false) {
const username = userLink.href.split('/').pop();
let html = '';
const loader = document.createElement('div');
loader.classList.add('loader');
userLink.after(loader);
try {
const data = await getUserData(username, subject_id);
const { not_found, subject_type, rate, type, ep_status, vol_status, comment, subject } = data;
const name = subject?.name;
loader.remove();
if (!not_found) {
const verb = ['读', '看', '听', '玩', '', '看'][subject_type - 1];
if (rate && rate !== 0) {
html += `<span class="ccf-star starstop-s"><span class="starlight stars${rate}"></span></span>`;
}
if (type) {
html += `<span class="ccf-status">${[`想${verb}`, `${verb}过`, `在${verb}`, '搁置', '抛弃'][type-1]}${
showSubject && name || ''
}</span>`;
}
if (ep_status) {
html += `<span class="ccf-status">${verb}到ep${ep_status}</span>`;
}
if (vol_status) {
html += `<span class="ccf-status">${verb}到vol${ep_status}</span>`;
}
if (comment) {
html += `<span class="ccf-comment" data-comment="${comment.replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"')}">💬</span>`;
}
html = `<span id="ccf-${subject_id}">${html}</span>`;
userLink.insertAdjacentHTML('afterend', html);
} else {
userLink.insertAdjacentElement('afterend', loadBtn('未标记该条目', userLink, subject_id));
}
} catch (error) {
console.error('Error fetching user data:', error);
userLink.insertAdjacentElement('afterend', loadBtn('加载失败', userLink, subject_id));
}
userLink.parentElement.querySelector('.loader')?.remove();
}
function loadBtn(text, userLink, subject_id) {
const btn = document.createElement('span');
btn.classList.add('ccf-status');
btn.textContent = text;
btn.style.cursor = 'pointer';
btn.addEventListener('click', () => {
const username = userLink.href.split('/').pop();
sessionStorage.removeItem(`userData_${username}_${subject_id}`);
btn.remove();
renderUserData(userLink, subject_id);
});
return btn;
}
function lazyRender(userLinks, subject_id, showSubject) {
const observer = new IntersectionObserver(entries => {
if (entries[0].intersectionRatio <= 0) return;
for (const { isIntersecting, target } of entries) {
if (!isIntersecting) continue;
observer.unobserve(target);
renderUserData(target, subject_id, showSubject);
}
});
for (const userLink of userLinks) observer.observe(userLink);
}
function multiSubjectsRender(userLinks, subjectLinks) {
if (subjectLinks.length === 1) {
lazyRender(userLinks, subjectLinks[0].href.split('/').pop());
return;
}
for (const subjectLink of subjectLinks) {
const frag = document.createDocumentFragment();
const br = document.createElement('br');
const btn = document.createElement('a');
btn.href = 'javascript:;';
btn.textContent = '显示评价';
btn.classList.add('l');
btn.addEventListener('click', function() {
const status = document.createElement('span');
status.classList.add('ccf-status');
status.textContent = '已显示本作评价';
this.insertAdjacentElement('afterend', status);
this.remove();
lazyRender(userLinks, subjectLink.href.split('/').pop(), true);
});
frag.append(br, btn);
subjectLink.after(frag);
}
}
})();