// ==UserScript== // @name 哔哩哔哩网页版展示 IP 属地 // @namespace http://zhangmaimai.com // @version 1.2 // @author MaxChang3 // @description 我不喜欢 IP 属地,但是你手机都显示了,为什么电脑不显示呢?目前仅视频、动态评论区 // @license MIT // @icon https://www.bilibili.com/favicon.ico // @match https://www.bilibili.com/video/* // @match https://www.bilibili.com/list/* // @match https://www.bilibili.com/medialist/play/* // @match https://www.bilibili.com/bangumi/play/* // @match https://t.bilibili.com/* // @run-at document-body // @downloadURL none // ==/UserScript== (function () { 'use strict'; const isElementLoaded = async (selector) => { while (document.querySelector(selector) === null) { await new Promise((resolve) => requestAnimationFrame(resolve)); } return document.querySelector(selector); }; const getIPAddress = (replyItemEl) => { var _a, _b, _c, _d, _e, _f; const IPString = (_f = (_e = (_d = (_c = (_b = (_a = replyItemEl.className.startsWith("sub") ? replyItemEl.querySelector(".reply-content") : replyItemEl) == null ? void 0 : _a.__vnode) == null ? void 0 : _b.ctx) == null ? void 0 : _c.props) == null ? void 0 : _d.reply) == null ? void 0 : _e.reply_control) == null ? void 0 : _f.location; return IPString ? `  ${IPString}` : ""; }; const insertPAddressEl = (replyItemEl) => { const replyInfo = replyItemEl.className.startsWith("sub") ? replyItemEl.querySelector(".sub-reply-info") : replyItemEl.querySelector(".reply-info"); if (!replyInfo) throw Error("Can not detect reply info"); replyInfo.children[0].innerHTML += getIPAddress(replyItemEl); }; const isReplyItem = (el) => el instanceof HTMLDivElement && ["reply-item", "sub-reply-item"].includes(el.className); const setupObserver = async () => { const targetNode = await isElementLoaded(".reply-list"); if (!targetNode) throw Error("Can not detect target node"); const config = { attributes: true, childList: true, subtree: true }; const callback = (mutationsList) => { for (let mutation of mutationsList) { if (mutation.type !== "childList") continue; mutation.addedNodes.forEach((node) => { if (!isReplyItem(node)) return; insertPAddressEl(node); if (node.className.startsWith("sub")) return; const subReplyListEl = node.querySelector(".sub-reply-list"); if (!subReplyListEl) return; const subReplyList = Array.from(subReplyListEl.children); subReplyList.pop(); subReplyList.map(insertPAddressEl); }); } }; const observer = new MutationObserver(callback); observer.observe(targetNode, config); }; const injectVideoComments = () => { setupObserver(); }; const pageType = { "dynamic": Symbol("video"), "bangumi": Symbol("bangumi") }; const injectBbComment = async (type) => { if (type === pageType.dynamic) { const dynBtn = await isElementLoaded(".bili-dyn-action.comment"); if (dynBtn) dynBtn.click(); await isElementLoaded(".bb-comment"); dynBtn.click(); } else if (type === pageType.bangumi) { await isElementLoaded(".bb-comment"); } const createListCon = bbComment.prototype._createListCon; const createSubReplyItem = bbComment.prototype._createSubReplyItem; const applyHandler = (target, thisArg, args) => { const [item] = args; const result = Reflect.apply(target, thisArg, args); const replyTimeRegex = /(.*?)<\/span>/; return result.replace(replyTimeRegex, `$1  ${item.reply_control.location}`); }; bbComment.prototype._createListCon = new Proxy(createListCon, { apply: applyHandler }); bbComment.prototype._createSubReplyItem = new Proxy(createSubReplyItem, { apply: applyHandler }); }; const matchPrefix = (url) => { if (url.startsWith("https://www.bilibili.com/video/") || url.startsWith("https://www.bilibili.com/medialist/play/") || url.startsWith("https://www.bilibili.com/list/")) { injectVideoComments(); } else if (url.startsWith("https://t.bilibili.com")) { injectBbComment(pageType.dynamic); } else if (url.startsWith("https://www.bilibili.com/bangumi/play/")) { injectBbComment(pageType.bangumi); } }; matchPrefix(location.href); })();