您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在蜜柑计划首页显示 Bangumi 评分及跳转链接。
当前为
// ==UserScript== // @name mikananiBgmScore // @namespace https://github.com/kjtsune/UserScripts // @version 0.2 // @description 在蜜柑计划首页显示 Bangumi 评分及跳转链接。 // @author kjtsune // @match https://mikanani.me/ // @icon https://www.google.com/s2/favicons?sz=64&domain=mikanani.me // @grant none // @license MIT // ==/UserScript== 'use strict'; let config = { logLevel: 2, minScore: 0 }; let logger = { error: function (...args) { if (config.logLevel >= 1) console.log("%cerror", "color: yellow; font-style: italic; background-color: blue;", args); }, info: function (...args) { if (config.logLevel >= 2) console.log("%cinfo", "color: yellow; font-style: italic; background-color: blue;", args); }, debug: function (...args) { if (config.logLevel >= 3) console.log("%cdebug", "color: yellow; font-style: italic; background-color: blue;", args); }, } function createElementFromHTML(htmlString) { let div = document.createElement('div'); div.innerHTML = htmlString.trim(); return div.firstElementChild; } async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function getJSON(url) { try { const response = await fetch(url); logger.info(`fetch ${url}`) if (response.status >= 200 && response.status < 400) return await response.json(); console.error(`Error fetching ${url}:`, response.status, response.statusText, await response.text()); } catch (e) { console.error(`Error fetching ${url}:`, e); } } async function getBgmJson(bgmId) { let url = `https://api.bgm.tv/v0/subjects/${bgmId}` return await getJSON(url) } async function getParsedBgmInfo(bgmId, stringify = false) { let bgmJson = await getBgmJson(bgmId); let score = (bgmJson) ? bgmJson.rating.score : 0.1; let summary = (bgmJson) ? bgmJson.summary : "maybe 18x"; let res = { score: score, summary: summary } res = (stringify) ? JSON.stringify(res) : res; return res } function queryAllForArray(seletor, elementArray) { let result = []; for (const element of elementArray) { let res = element.querySelectorAll(seletor); if (!res) logger.error("queryAllForArray not result", seletor, element); result.push(...res); } return result } function multiTimesSeletor(storage = null, seletorAll = false, ...cssSeletor) { const seletor = cssSeletor[0] const restSeletor = cssSeletor.slice(1) if (!seletor) return storage; if (seletorAll) { storage = storage || [document] let res = queryAllForArray(seletor, storage); if (res) storage = res; storage && logger.debug('storage', storage.length, seletor, restSeletor); if (!restSeletor) { return storage; } else { return multiTimesSeletor(storage, true, ...restSeletor); } } else { storage = storage || document; const lastRes = storage; storage = storage.querySelector(seletor); storage && logger.debug('storage', storage, seletor); if (!storage) logger.error("not result", seletor, lastRes); if (!restSeletor) { return storage; } else { return multiTimesSeletor(storage, false, ...restSeletor); } } } async function myFetch(url, selector = null, selectAll = false) { let response = await fetch(url); let text = await response.text(); const parser = new DOMParser(); const htmlDocument = parser.parseFromString(text, "text/html"); const element = htmlDocument.documentElement; if (!selector) return element; if (selectAll) { return element.querySelectorAll(selector); } else { return element.querySelector(selector); } } async function getBgmId(mikanUrl) { let selector = "p.bangumi-info > a[href*='tv/subject']"; let bgm = await myFetch(mikanUrl, selector); if (bgm) bgm = bgm.href.split("/").slice(-1)[0]; return bgm } class MyStorage { constructor(prefix, splitStr = '|', expireDay = 0) { this.prefix = prefix; this.splitStr = splitStr; this.expireMs = expireDay * 864E5 } _keyGenerator(key) { return `${this.prefix}${this.splitStr}${key}` } get(key, defalut = null) { key = this._keyGenerator(key); let res = localStorage.getItem(key); if (this.expireMs && res) { res = JSON.parse(localStorage.getItem(key)).value; } res = res || defalut; return res } set(key, value) { key = this._keyGenerator(key); if (this.expireMs) { value = JSON.stringify({ timestamp: Date.now(), value: value }) } localStorage.setItem(key, value) } del(key) { key = this._keyGenerator(key); if (key in localStorage) { localStorage.removeItem(key) }; } checkIsExpire(key) { key = this._keyGenerator(key); if (!(key in localStorage)) return true; if (!this.expireMs && key in localStorage) { return false }; let timestamp = JSON.parse(localStorage.getItem(key)).timestamp; if (!timestamp) throw `checkIsExpire not work , not timestamp, key: ${key}`; if (timestamp + this.expireMs < Date.now()) { return true; } else { return false; } } } async function addScoreSummaryToHtml(mikanElementList) { for (const element of mikanElementList) { let scoreElement = element.nextElementSibling; if (scoreElement) continue; let mikanUrl = element.href; let mikanId = mikanUrl.split('/').slice(-1)[0]; let bgmId = mikanBgmStorage.get(mikanId); let bgmInfo = bgmInfoStorage.get(bgmId); if (!bgmId || !bgmInfo) continue; let bgmUrl = `https://bgm.tv/subject/${bgmId}` let score = bgmInfo.score; let summary = bgmInfo.summary; let title = element.textContent; let pathName = element.pathname; let bgmHtml = `<a href="${bgmUrl}" target="_blank" title="${summary}" id="bgmScore">${score}</a>` element.insertAdjacentHTML("afterend", bgmHtml); let minScore = config.minScore; let lowScore = (score <= minScore && score > 0.1 && minScore > 0.1) ? true : false; let mobileFatherElement = document.querySelectorAll(`a[href="${pathName}"`)[1]; if (lowScore) { logger.info('delete', title, score, minScore); element.parentElement.parentElement.parentElement.remove(); } if (!mobileFatherElement) continue; mobileFatherElement = mobileFatherElement.parentElement; let mobileElement = mobileFatherElement.querySelector('div'); if (!mobileElement) continue; let mobileHtml = `<a href="${bgmUrl}" target="_blank" title="${summary}" id="bgmScore">${title} ${score}</a>` let newMobileElement = createElementFromHTML(mobileHtml); mobileElement.replaceWith(newMobileElement); // mobileFatherElement.replaceChild(newMobileElement, mobileElement); if (lowScore) { logger.info('delete', title, score, minScore); newMobileElement.parentElement.parentElement.remove(); } } } let mikanBgmStorage = new MyStorage("mikan"); let bgmInfoStorage = new MyStorage("bgm", undefined, 7); async function storeMikanBgm(mikanElementList, storeBgmInfo = false) { async function checkBgmInfoExist(mkId) { let bgmId = mikanBgmStorage.get(mkId); if (!bgmId) return; if (bgmInfoStorage.checkIsExpire(bgmId)) { bgmInfoStorage.set(bgmId, await getParsedBgmInfo(bgmId)); } } let count = 0; for (const element of mikanElementList) { let mikanUrl = element.href; let mikanId = mikanUrl.split('/').slice(-1)[0]; if (!mikanBgmStorage.checkIsExpire(mikanId)) { continue }; let bgmId = await getBgmId(mikanUrl); logger.info("fetch mikan", mikanId) if (mikanBgmStorage.checkIsExpire(mikanId)) { mikanBgmStorage.set(mikanId, bgmId); logger.info(`set ${mikanId} to ${bgmId}`); } await sleep(1000); if (storeBgmInfo) await checkBgmInfoExist(mikanId); await addScoreSummaryToHtml([element]); count++; } count && logger.info('fetch count', count); } async function main() { let animeList = multiTimesSeletor(null, true, "div.sk-bangumi", "a[href^='/Home/Bangumi']"); // animeList = animeList.slice(0, 100); await storeMikanBgm(animeList, true); await addScoreSummaryToHtml(animeList); logger.info(animeList) } (function loop() { setTimeout(async function () { let start = Date.now() await main(); let usedSec = (Date.now() - start) / 1000; if (usedSec > 0.01) logger.info(`used time ${usedSec}`); loop(); }, 2000); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址