您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
列表页用评论数做缓存判定,详情页始终直接统计,评论计数范围限制在指定div
// ==UserScript== // @name 剧集列表(页)显示评论中“神回”的次数 // @namespace https://jirehlov.com // @version 0.4 // @description 列表页用评论数做缓存判定,详情页始终直接统计,评论计数范围限制在指定div // @author Jirehlov // @include /^https?://(bangumi|bgm|chii)\.(tv|in)\/subject\/\d+\/ep/ // @include /^https?://(bangumi|bgm|chii)\.(tv|in)\/ep\/\d+$/ // @license MIT // ==/UserScript== const STORE = "shen_hui", DBVER = 1; $("head").append(`<style> .shen-hui-count{display:inline-block;padding:0 6px;margin-left:6px;border-radius:6px; color:#fff;font-weight:bold;font-size:13px;line-height:20px} </style>`); function color(c, max = 30) { const r = Math.min(c / max, 1); const s = { r: 33, g: 150, b: 243 }, e = { r: 244, g: 67, b: 54 }; return `rgb(${s.r + (e.r - s.r) * r | 0},${s.g + (e.g - s.g) * r | 0},${s.b + (e.b - s.b) * r | 0})`; } function openDB() { return new Promise(r => { const q = indexedDB.open(STORE, DBVER); q.onupgradeneeded = e => e.target.result.createObjectStore(STORE, { keyPath: "epId" }); q.onsuccess = () => r(q.result); q.onerror = () => r(null); }); } async function getCache(epId, curCount) { const db = await openDB(); if (!db) return null; return new Promise(res => { const r = db.transaction(STORE, "readonly").objectStore(STORE).get(epId); r.onsuccess = () => { const d = r.result; if (!d) return res(null); if (curCount != null && d.commentCount === curCount) return res(d.shenCount); res(null); }; r.onerror = () => res(null); }); } async function setCache(epId, commentCount, shenCount) { (await openDB())?.transaction(STORE, "readwrite").objectStore(STORE) .put({ epId, commentCount, shenCount }); } function countShen(doc) { let sc = 0; doc.querySelectorAll("#comment_list .cmt_sub_content, #comment_list .message").forEach(div => { if (/神回/.test(div.textContent)) sc++; }); return sc; } async function getCountForList(epId, curCount) { const c = await getCache(epId, curCount); if (c != null) return c; const h = await fetch(`/ep/${epId}`).then(r => r.ok ? r.text() : ""); if (!h) return 0; const doc = new DOMParser().parseFromString(h, "text/html"); const sc = countShen(doc); await setCache(epId, curCount, sc); return sc; } async function getCountForSingle(epId, doc) { const sc = countShen(doc); const cc = doc.querySelectorAll("#comment_list .row,#comment_list .item,#comment_list>li,#comment_list>div").length; await setCache(epId, cc, sc); return sc; } async function showList() { for (const el of $("ul.line_list>li").get()) { const epId = $("h6 a", el).attr("href")?.split("/").pop(); if (!epId) continue; const t = $(el).find("small.grey").filter((_, x) => /\+(\d+)/.test(x.textContent)).text(); const cur = +((t.match(/\+(\d+)/) || [])[1] || 0); const c = await getCountForList(epId, cur); if (c > 0) $("h6", el).append(`<span class="shen-hui-count" style="background:${color(c)}">神回×${c}</span>`); } } async function showSingle() { const h2 = $("h2.title").get(0); if (!h2) return; let epId = $("a[href^='/ep/'][href$='/edit']", h2).attr("href")?.match(/\/ep\/(\d+)/)?.[1] || location.pathname.split("/").pop(); const c = await getCountForSingle(epId, document); if (c > 0) $(h2).append(`<span class="shen-hui-count" style="background:${color(c)};vertical-align:middle;">神回×${c}</span>`); } if (/\/subject\/\d+\/ep/.test(location.pathname)) showList(); else if (/\/ep\/\d+$/.test(location.pathname)) showSingle();