show voiced characters that are from subjects in your collections
// ==UserScript==
// @name CharWhoIWouldveknow
// @namespace https://jirehlov.com
// @version 0.2.5
// @description show voiced characters that are from subjects in your collections
// @author Jirehlov
// @include /^https?:\/\/(bgm\.tv|bangumi\.tv|chii\.in)\/person\/\d+\/works\/voice+/
// @grant none
// @license MIT
// ==/UserScript==
(function () {
let collectStatus = {};
const limit = 50;
let guess = 1000000;
let totalItems = 0;
let allData = [];
let username = null;
let updatingData = false;
let getLi = null;
let numCollected = 0;
let numNotCollected = 0;
if (localStorage.getItem('bangumi_subject_collectStatus')) {
collectStatus = JSON.parse(localStorage.getItem('bangumi_subject_collectStatus'));
}
const idBadgerNeue = document.querySelector('.idBadgerNeue');
if (idBadgerNeue) {
const avatarLink = idBadgerNeue.querySelector('.avatar');
if (avatarLink) {
const href = avatarLink.getAttribute('href');
username = href.substring(href.lastIndexOf('/') + 1);
}
}
let subject_type = [
1,
2,
3,
4,
6
];
let collection_type = [
1,
2,
3,
4,
5
];
let subject_type_index = 0;
function isSubjectCollected(subjectId) {
return collectStatus[subjectId] === 'collect';
}
function findSubjectId(element) {
const aElement = element.querySelector('a');
if (aElement) {
const href = aElement.getAttribute('href');
const subjectIdMatch = href.match(/\/subject\/(\d+)/);
if (subjectIdMatch) {
return subjectIdMatch[1];
}
}
return null;
}
async function copyListItemIfMultipleClearitItems() {
const browserList = document.querySelectorAll('.browserList > li');
for (const browserListItem of browserList) {
browserListItem.classList.add('filteredchars1');
const clearitItems = browserListItem.querySelectorAll('.innerRightList.rr li.clearit');
if (clearitItems.length > 1) {
const clone1 = browserListItem.cloneNode(true);
const clone2 = browserListItem.cloneNode(true);
clone1.classList.remove('filteredchars1');
clone2.classList.remove('filteredchars1');
clone1.classList.add('filteredchars2');
clone2.classList.add('filteredchars3');
clone1.style.display = 'none';
clone2.style.display = 'none';
browserListItem.parentNode.insertBefore(clone1, browserListItem.nextSibling);
browserListItem.parentNode.insertBefore(clone2, browserListItem.nextSibling);
const clearitItems2 = clone1.querySelectorAll('.innerRightList.rr li.clearit');
for (const clearitItem of clearitItems2) {
const subjectId = findSubjectId(clearitItem);
if (subjectId && !isSubjectCollected(subjectId)) {
clearitItem.remove();
}
}
const clearitItems3 = clone2.querySelectorAll('.innerRightList.rr li.clearit');
for (const clearitItem of clearitItems3) {
const subjectId = findSubjectId(clearitItem);
if (subjectId && isSubjectCollected(subjectId)) {
clearitItem.remove();
}
}
if (clone1.querySelectorAll('.innerRightList.rr li.clearit').length === 0) {
clone1.remove();
}
if (clone2.querySelectorAll('.innerRightList.rr li.clearit').length === 0) {
clone2.remove();
}
} else {
const clearitItem = browserListItem.querySelector('.innerRightList.rr li.clearit');
const subjectId = findSubjectId(clearitItem);
if (subjectId) {
browserListItem.classList.add(isSubjectCollected(subjectId) ? 'filteredchars2' : 'filteredchars3');
}
}
await new Promise(resolve => setTimeout(resolve, 1));
}
}
function checkCharactersInPage(filterType) {
const browserListItems = document.querySelectorAll('.browserList > li');
browserListItems.forEach(item => {
if (filterType === 1 && item.classList.contains('filteredchars1')) {
item.style.display = 'block';
} else if (filterType === 2 && item.classList.contains('filteredchars2')) {
item.style.display = 'block';
} else if (filterType === 3 && item.classList.contains('filteredchars3')) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
}
function countCollectedAndNotCollected() {
const filteredChars2Items = document.querySelectorAll('.filteredchars2 .innerRightList.rr li.clearit');
const filteredChars3Items = document.querySelectorAll('.filteredchars3 .innerRightList.rr li.clearit');
numCollected = filteredChars2Items.length;
numNotCollected = filteredChars3Items.length;
}
function createFilterButtons() {
const subjectFilterElement = document.querySelector('.subjectFilter');
if (subjectFilterElement) {
const groupedUL = document.createElement('ul');
groupedUL.className = 'grouped clearit';
const titleLi = document.createElement('li');
titleLi.classList.add('title');
titleLi.innerHTML = '<span>收藏状态</span>';
const collectedLi = document.createElement('li');
collectedLi.innerHTML = `<a href="javascript:;" class="l"><span>已收藏 (${ numCollected })</span></a>`;
collectedLi.addEventListener('click', () => {
checkCharactersInPage(2);
});
const notCollectedLi = document.createElement('li');
notCollectedLi.innerHTML = `<a href="javascript:;" class="l"><span>未收藏 (${ numNotCollected })</span></a>`;
notCollectedLi.addEventListener('click', () => {
checkCharactersInPage(3);
});
const allLi = document.createElement('li');
allLi.innerHTML = '<a href="javascript:;" class="l"><span>全部</span></a>';
allLi.addEventListener('click', () => {
checkCharactersInPage(1);
});
getLi = document.createElement('li');
getLi.innerHTML = '<a href="javascript:;" class="l"><span>收藏数据有误\uFF1F单击手动刷新</span></a>';
getLi.addEventListener('click', () => {
getData();
});
groupedUL.appendChild(titleLi);
groupedUL.appendChild(allLi);
groupedUL.appendChild(collectedLi);
groupedUL.appendChild(notCollectedLi);
groupedUL.appendChild(getLi);
subjectFilterElement.appendChild(groupedUL);
}
}
async function fetchData(collection_type, offset) {
const url = `https://api.bgm.tv/v0/users/${ username }/collections?subject_type=${ subject_type[subject_type_index] }&type=${ collection_type }&limit=${ limit }&offset=${ offset }`;
const headers = { 'Accept': 'application/json' };
const response = await fetch(url, { headers });
const data = await response.json();
return data;
}
async function getData() {
if (updatingData) return;
updatingData = true;
console.log(`Update started.`);
if (getLi) {
getLi.innerHTML = '<a href="javascript:;" class="l"><span>更新中</span></a>';
getLi.removeEventListener('click', getData);
getLi.style.pointerEvents = 'none';
}
for (let ct = 1; ct < collection_type.length; ct++) {
for (let i = 0; i < subject_type.length; i++) {
subject_type_index = i;
const initialData = await fetchData(ct, guess);
if ('description' in initialData && initialData.description.includes('equal to')) {
totalItems = parseInt(initialData.description.split('equal to ')[1]);
console.log(`Updated totalItems to: ${ totalItems }`);
} else {
totalItems = 0;
}
for (let offset = 0; offset < totalItems; offset += limit) {
const data = await fetchData(ct, offset);
allData.push(...data.data);
console.log(`Fetched ${ offset + 1 }-${ offset + limit } items...`);
}
}
}
for (const item of allData) {
const subjectId = item.subject_id;
collectStatus[subjectId] = 'collect';
}
localStorage.setItem('bangumi_subject_collectStatus', JSON.stringify(collectStatus));
updatingData = false;
if (getLi) {
getLi.innerHTML = '<a href="javascript:;" class="l"><span>更新结束</span></a>';
}
console.log(`Update completed.`);
}
(async () => {
await copyListItemIfMultipleClearitItems();
countCollectedAndNotCollected();
createFilterButtons();
})();
}());