// ==UserScript==
// @name VNDB Highlighter
// @namespace https://github.com/MarvNC
// @homepageURL https://github.com/MarvNC/vndb-highlighter
// @match https://vndb.org/s*
// @match https://vndb.org/p*
// @match https://vndb.org/v*
// @match https://vndb.org/c*
// @match https://vndb.org/u*/edit
// @version 1.65
// @author Marv
// @downloadURL https://raw.githubusercontent.com/MarvNC/vndb-highlighter/main/vndb-list-highlighter.user.js
// @updateURL https://raw.githubusercontent.com/MarvNC/vndb-highlighter/main/vndb-list-highlighter.user.js
// @description Highlights and provides tooltips for known entries on VNDB.
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @grant GM_getResourceText
// @require https://unpkg.com/@popperjs/core@2.9.2/dist/umd/popper.min.js
// @require https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js
// @resource pickrCSS https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/classic.min.css
// @run-at document-idle
// ==/UserScript==
let delayMs = 300;
const fetchListMs = 600000;
const updatePageMs = 86400000;
const listExportUrl = (id) => `https://vndb.org/${id}/list-export/xml`;
const types = {
VN: 'loli',
Settings: 'cute',
Staff: {
vnSelector: 'tr > td.tc1 > a',
insertBefore: '#maincontent > .boxtitle',
box: (novels, count) => `
No Novels on List (of ${info.total})
`
);
}
tooltip = entryElem.replaceChild(newTable, tooltip);
tooltip = newTable;
tooltip.className += ' tooltip';
let show = makePopper(entryElem, tooltip);
if (visible) show();
});
}
})();
let queue = [];
let prioQueue = [];
let resolvers = {};
let active = false;
(async function () {
while (true) {
if (active || prioQueue.length > 0) {
let currURL;
if (prioQueue.length > 0) {
currURL = prioQueue.shift();
queue = queue.filter((queueUrl) => queueUrl != currURL);
console.log(`Priority: getting ${currURL}, waiting ${delayMs} ms`);
} else if (queue.length > 0) {
currURL = queue.shift();
console.log(`Getting ${currURL}: ${queue.length} pages remaining, waiting ${delayMs} ms`);
}
if (currURL) {
let responseText = await getUrl(currURL);
resolvers[currURL].forEach((resolver) => resolver(responseText));
}
}
await timer(delayMs);
}
})();
async function getUrl(url) {
let response = await fetch(url);
let waitMs = delayMs;
while (!response.ok) {
response = await fetch(url);
waitMs *= 2;
delayMs *= 1.2;
delayMs = Math.round(delayMs);
console.log('Failed response, new wait:' + waitMs);
await timer(waitMs);
}
return await response.text();
}
(async function () {
while (true) {
await timer(1000);
let currPage = GM_getValue('currPage', null);
console.log('current page: ', active, 'queue: ', queue.length);
if (queue.length == 0) {
console.log('finished fetching');
active = false;
return;
}
if (currPage == null || currPage?.page == document.URL) {
GM_setValue('currPage', { page: document.URL, lastUpdate: Date.now() });
active = true;
} else {
if (currPage != null && currPage?.page != document.URL) {
if (currPage?.lastUpdate + 2000 < Date.now()) {
GM_setValue('currPage', { page: document.URL, lastUpdate: Date.now() });
active = true;
} else active = false;
}
}
}
})();
async function getPage(url, doc = null, updateInfo, priority = false) {
let type,
table,
count = 0,
total = 0;
if (!doc) {
if (url.match('vndb.org/p')) url = 'https://vndb.org/' + url.match(/p\d+/)[0] + '/vn';
if (GM_getValue('pages', null)[url]) {
updateInfo(GM_getValue('pages', null)[url]);
if (GM_getValue('pages', null)[url].lastUpdate + updatePageMs > Date.now()) return;
}
if (priority) {
prioQueue.unshift(url);
return;
}
doc = document.createElement('html');
let [promise, resolver] = createPromise();
if (resolvers[url]) resolvers[url].push(resolver);
else {
resolvers[url] = [resolver];
queue.push(url);
}
doc.innerHTML = await promise;
}
type = getType(url, doc);
vns = GM_getValue('vns', null);
// add highlights to vns on list
let vnElems = [...doc.querySelectorAll(type.vnSelector)];
let novelelements = '';
vnElems.forEach((elem) => {
let vnID = elem.href.split('/').pop();
if (vns[vnID] && vns[vnID].lists.length > 0) {
let bgElem = type == types.Staff ? elem.parentElement.parentElement : elem.parentElement;
bgElem.className += 'colorbg ';
bgElem.className += vns[vnID].lists.join(' ');
elem.innerHTML = `