自动将TouchGal网站上的VNDB ID转换为可点击的链接
// ==UserScript==
// @name TouchGal VNDB链接转换器
// @namespace https://github.com/dccif
// @version 1.0.6
// @author dccif
// @description 自动将TouchGal网站上的VNDB ID转换为可点击的链接
// @license MIT
// @icon https://www.touchgal.us/favicon.ico
// @source https://github.com/dccif/touchgal-script.git
// @match https://www.touchgal.io/*
// @match https://www.touchgal.us/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
let executed = false;
let stylesCached = false;
let cachedRefClass = "";
let cachedRefRole = null;
let styleAttempts = 0;
let vndbAttempts = 0;
let pollCount = 0;
let currentInterval = 300;
const maxStyleAttempts = 5;
const maxVndbAttempts = 8;
const maxPollCount = 30;
const intervals = [300, 500, 800, 1200, 2e3];
function shouldRun() {
const { href, pathname } = location;
return href.includes("?") || href.includes("#") || pathname !== "/";
}
function tryCacheStyles() {
if (stylesCached || styleAttempts >= maxStyleAttempts) return;
styleAttempts++;
const refLink = document.querySelector(".kun-prose a");
if (refLink) {
cachedRefClass = refLink.className || "";
cachedRefRole = refLink.getAttribute("role");
stylesCached = true;
console.log("✅ 样式信息已缓存");
} else if (styleAttempts >= maxStyleAttempts) {
stylesCached = true;
console.log("⚠️ 未找到参考样式,使用默认样式");
}
}
function isPageReady() {
const hasGrid = document.querySelector('div[class*="grid"]');
const hasFlex = document.querySelector('div[class*="flex"]');
return !!(hasGrid && hasFlex);
}
function replaceVNDBIds() {
if (executed) return true;
vndbAttempts++;
const vndbSpans = document.querySelectorAll("span");
if (!vndbSpans.length) {
if (vndbAttempts >= maxVndbAttempts) {
console.log("⚠️ 多次尝试后未找到VNDB ID,页面上可能没有相关内容");
return true;
}
return false;
}
const vndbRegex = /VNDB ID:\s*(v\d+)/;
let hasChanges = false;
for (let i = 0; i < vndbSpans.length; i++) {
const span = vndbSpans[i];
const text = span.textContent;
if (!(text == null ? void 0 : text.includes("VNDB ID:"))) continue;
const match = text.match(vndbRegex);
if (!match) continue;
const vndbId = match[1];
const parentNode = span.parentNode;
if (parentNode) {
const fragment = document.createDocumentFragment();
fragment.appendChild(document.createTextNode("VNDB ID: "));
const link = document.createElement("a");
link.href = `https://vndb.org/${vndbId}`;
link.textContent = vndbId;
link.target = "_blank";
link.rel = "noopener noreferrer";
if (cachedRefClass) {
link.className = cachedRefClass;
}
if (cachedRefRole) {
link.setAttribute("role", cachedRefRole);
}
fragment.appendChild(link);
parentNode.replaceChild(fragment, span);
hasChanges = true;
console.log(`替换VNDB ID: ${vndbId}`);
}
}
if (hasChanges) {
executed = true;
console.log("✅ VNDB链接替换完成");
return true;
}
if (vndbAttempts >= maxVndbAttempts) {
console.log("⚠️ 多次尝试后未找到VNDB ID,页面上可能没有相关内容");
return true;
}
return false;
}
function execute() {
if (!shouldRun() || executed) return;
tryCacheStyles();
if (!isPageReady()) return;
if (!stylesCached) return;
const shouldStop = replaceVNDBIds();
if (shouldStop) {
executed = true;
}
}
function poll() {
pollCount++;
if (pollCount > maxPollCount) {
console.log("⚠️ 轮询超时,停止执行");
return;
}
execute();
if (!executed) {
const intervalIndex = Math.min(
Math.floor(pollCount / 6),
intervals.length - 1
);
currentInterval = intervals[intervalIndex];
console.log(`🔄 轮询第${pollCount}次,间隔${currentInterval}ms`);
setTimeout(poll, currentInterval);
}
}
poll();
})();