Add VIB Rank, stars, VIB_score decimal, and rating count to anime list based on API data
当前为
// ==UserScript==
// @name VIBDataListInsertion
// @namespace https://jirehlov.com
// @version 0.1.4
// @description Add VIB Rank, stars, VIB_score decimal, and rating count to anime list based on API data
// @include /^https?://(bangumi\.tv|bgm\.tv|chii\.in)/(.+?/list|.+?/tag|.+?/browser|subject_search|index)(/|\?).+$/
// @author Jirehlov
// @grant none
// @license MIT
// ==/UserScript==
(async function () {
'use strict';
let isTraversing = true;
async function addVIBData(innerElement) {
if (innerElement.getAttribute('data-vib-processed') === 'true') {
return;
}
const aElement = innerElement.parentElement.querySelector('a[href^="/subject/"]');
if (!aElement) {
return;
}
const match = aElement.getAttribute('href').match(/\/(\d+)$/);
if (!match) {
return;
}
const id = match[1];
const apiUrl = `https://api.jirehlov.com/vib/${ id }`;
const response = await fetch(apiUrl, { redirect: 'manual' });
if (response.ok) {
const data = await response.json();
const {VIB_rank, VIB_score, VIB_enum} = data;
const vibRankElement = document.createElement('span');
vibRankElement.classList.add('rank', 'vibrank');
vibRankElement.innerHTML = `<small>VIB Rank </small>${ VIB_rank }`;
vibRankElement.style.right = '85px';
const starsElement = document.createElement('span');
starsElement.classList.add('starstop-s');
const starCount = Math.round(VIB_score);
starsElement.innerHTML = `<span class="starlight stars${ starCount }"></span>`;
const vibScoreDecimal = parseFloat(VIB_score).toFixed(1);
const ratingInfoElement = document.createElement('p');
ratingInfoElement.classList.add('rateInfo');
const rankElement = innerElement.querySelector('.rank');
if (rankElement) {
rankElement.insertAdjacentElement('afterend', vibRankElement);
} else {
innerElement.appendChild(vibRankElement);
}
ratingInfoElement.appendChild(starsElement);
ratingInfoElement.innerHTML += `<small class="fade"> ${ vibScoreDecimal }</small> <span class="tip_j">(${ VIB_enum }人VIB评分)</span>`;
innerElement.appendChild(ratingInfoElement);
innerElement.setAttribute('data-vib-processed', 'true');
} else {
innerElement.setAttribute('data-vib-processed', 'true');
return;
}
}
function observeDOMChanges() {
const browserFullContainer = document.querySelector('.browserFull');
if (!browserFullContainer) {
return;
}
const mutationObserver = new MutationObserver(async mutations => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
const addedLiElements = Array.from(mutation.addedNodes).filter(node => node.nodeName === 'LI');
console.log(mutation.addedNodes.length);
if (mutation.addedNodes.length > 0) {
isTraversing = true;
await processElementWithDelay(addedLiElements);
isTraversing = false;
}
}
}
});
mutationObserver.observe(browserFullContainer, {
childList: true,
attributes: false,
subtree: false
});
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function processElementWithDelay(elements) {
return new Promise(async (resolve, reject) => {
try {
for (const [index, liElement] of elements.entries()) {
const innerElement = liElement.querySelector('.inner');
if (innerElement) {
await delay((index + 1) * 10);
addVIBData(innerElement);
}
}
resolve();
} catch (error) {
reject(error);
}
});
}
let ascendingOrder = true;
function sortByVIBRank() {
const browserFullContainer = document.querySelector('.browserFull');
if (!browserFullContainer) {
return;
}
const liElements = Array.from(browserFullContainer.querySelectorAll('.browserFull > li'));
liElements.sort((a, b) => {
const rankAElement = a.querySelector('.vibrank');
const rankBElement = b.querySelector('.vibrank');
if (!rankAElement && !rankBElement) {
return 0;
}
if (!rankAElement) return 1;
if (!rankBElement) return -1;
const rankA = parseFloat(rankAElement.textContent.replace('VIB Rank', '').trim());
const rankB = parseFloat(rankBElement.textContent.replace('VIB Rank', '').trim());
if (isNaN(rankA)) return 1;
if (isNaN(rankB)) return -1;
return ascendingOrder ? rankA - rankB : rankB - rankA;
});
while (browserFullContainer.firstChild) {
browserFullContainer.removeChild(browserFullContainer.firstChild);
}
liElements.forEach((liElement, index) => {
browserFullContainer.appendChild(liElement);
});
const vibSortButton = document.querySelector('.vibBtn');
if (vibSortButton) {
vibSortButton.textContent = `VIB排序 ${ ascendingOrder ? '\u2191' : '\u2193' }`;
}
ascendingOrder = !ascendingOrder;
}
observeDOMChanges();
isTraversing = true;
const initialLiElements = document.querySelectorAll('.browserFull li');
await processElementWithDelay(initialLiElements);
isTraversing = false;
const browserToolsElement = document.getElementById('browserTools');
if (browserToolsElement) {
const vibSortButton = document.createElement('a');
vibSortButton.classList.add('chiiBtn', 'vibBtn');
vibSortButton.href = 'javascript:;';
vibSortButton.textContent = 'VIB排序';
browserToolsElement.appendChild(vibSortButton);
}
const vibSortButton = document.querySelector('.vibBtn');
if (vibSortButton) {
vibSortButton.addEventListener('click', function () {
if (isTraversing) {
return;
}
sortByVIBRank();
});
}
}());