9/2/2024, 12:22:50 PM
当前为
// ==UserScript==
// @name Anilist Audience Score
// @namespace Violentmonkey Scripts
// @match https://anilist.co/*
// @grant none
// @version 1.0
// @author dylan-dang
// @description 9/2/2024, 12:22:50 PM
// @license MIT
// ==/UserScript==
const query = `
query ($userId: Int, $userName: String, $type: MediaType) {
MediaListCollection(userId: $userId, userName: $userName, type: $type) {
lists {
name
isCustomList
isCompletedList: isSplitCompletedList
entries {
...mediaListEntry
}
}
}
}
fragment mediaListEntry on MediaList {
status
score
media {
type
format
status(version: 2)
episodes
volumes
chapters
averageScore
popularity
}
}`;
const observer = new MutationObserver(onMutation)
observer.observe(document, {
childList: true,
subtree: true,
});
let prevPathname = '';
let prevPath = [];
function isUserpage(path) {
return path.length === 2 && path[0].toLowerCase() === 'user';
}
function onMutation(mutList) {
const pathname = window.location.pathname;
const path = pathname.split('/').filter(Boolean);
if (pathname != prevPathname) {
// detect user navigated between two different user pages since stats element won't be readded
if (isUserpage(prevPath) && isUserpage(path)) {
const audienceStat = document.querySelector('#audience-score');
refreshAudienceScore(audienceStat, {
type: 'ANIME',
userName: path[1],
});
}
prevPathname = pathname;
prevPath = path;
}
for (const mut of mutList) {
for (const node of mut.addedNodes) {
const stats = node.querySelector?.('.list-stats>.stats-wrap');
if (!stats) continue;
if (!isUserpage(path)) return;
// clone a stat element and add audience score
const audienceStat = stats.lastChild.cloneNode(true);
audienceStat.id = 'audience-score'
audienceStat.querySelector('.label').textContent = 'Audience Score'
audienceStat.querySelector('.value').textContent = '...';
stats.appendChild(audienceStat);
refreshAudienceScore(audienceStat, {
type: 'ANIME',
userName: path[1],
});
}
}
}
function refreshAudienceScore(audienceStat, variables) {
audienceStat.querySelector('.value').textContent = '...';
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({ query, variables }),
};
fetch('https://graphql.anilist.co', options)
.then((res) => res.json())
.then(({data: {MediaListCollection: {lists}}}) => {
const completed = lists.flatMap((list) => list.entries).filter(entry => entry.status === 'COMPLETED');
const sumAudienceScore = completed.reduce((a, b) => a + b.media.averageScore, 0);
const avgAudienceScore = sumAudienceScore / completed.length;
// abort if client changed page before finishing
if (window.location.pathname.split('/').filter(Boolean)[1] != variables.userName) return;
audienceStat.querySelector('.value').textContent = avgAudienceScore.toFixed(1);
});
}